1 /* 2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.settings.datetime; 18 19 import static android.app.time.Capabilities.CAPABILITY_NOT_ALLOWED; 20 import static android.app.time.Capabilities.CAPABILITY_NOT_APPLICABLE; 21 import static android.app.time.Capabilities.CAPABILITY_NOT_SUPPORTED; 22 import static android.app.time.Capabilities.CAPABILITY_POSSESSED; 23 24 import android.app.time.TimeManager; 25 import android.app.time.TimeZoneCapabilities; 26 import android.app.time.TimeZoneCapabilitiesAndConfig; 27 import android.app.time.TimeZoneConfiguration; 28 import android.content.Context; 29 import android.util.Log; 30 31 import androidx.annotation.NonNull; 32 import androidx.annotation.Nullable; 33 import androidx.preference.Preference; 34 import androidx.preference.PreferenceScreen; 35 36 import com.android.settings.R; 37 import com.android.settings.core.TogglePreferenceController; 38 import com.android.settingslib.core.lifecycle.LifecycleObserver; 39 import com.android.settingslib.core.lifecycle.events.OnStart; 40 import com.android.settingslib.core.lifecycle.events.OnStop; 41 42 import java.util.concurrent.Executor; 43 44 public final class TimeZoneNotificationsPreferenceController 45 extends TogglePreferenceController 46 implements LifecycleObserver, OnStart, OnStop, TimeManager.TimeZoneDetectorListener { 47 48 private static final String TAG = "TZNotificationsSettings"; 49 50 private final TimeManager mTimeManager; 51 private @Nullable TimeZoneCapabilitiesAndConfig mTimeZoneCapabilitiesAndConfig; 52 private @Nullable Preference mPreference; 53 TimeZoneNotificationsPreferenceController(@onNull Context context, @NonNull String preferenceKey)54 public TimeZoneNotificationsPreferenceController(@NonNull Context context, 55 @NonNull String preferenceKey) { 56 super(context, preferenceKey); 57 mTimeManager = context.getSystemService(TimeManager.class); 58 } 59 60 /** 61 * Registers this controller with a category controller so that the category can be optionally 62 * displayed, i.e. if all the child controllers are not available, the category heading won't be 63 * available. 64 */ registerIn(@onNull NotificationsPreferenceCategoryController categoryController)65 public void registerIn(@NonNull NotificationsPreferenceCategoryController categoryController) { 66 categoryController.addChildController(this); 67 } 68 69 @Override isChecked()70 public boolean isChecked() { 71 if (!isAutoTimeZoneEnabled()) { 72 return false; 73 } 74 75 // forceRefresh set to true as the notifications toggle may have been turned off by 76 // switching off automatic time zone 77 TimeZoneCapabilitiesAndConfig capabilitiesAndConfig = 78 getTimeZoneCapabilitiesAndConfig(/* forceRefresh= */ true); 79 TimeZoneConfiguration configuration = capabilitiesAndConfig.getConfiguration(); 80 return configuration.areNotificationsEnabled(); 81 } 82 83 84 @Override setChecked(boolean isChecked)85 public boolean setChecked(boolean isChecked) { 86 TimeZoneConfiguration configuration = new TimeZoneConfiguration.Builder() 87 .setNotificationsEnabled(isChecked) 88 .build(); 89 return mTimeManager.updateTimeZoneConfiguration(configuration); 90 } 91 92 @Override displayPreference(@onNull PreferenceScreen screen)93 public void displayPreference(@NonNull PreferenceScreen screen) { 94 super.displayPreference(screen); 95 mPreference = screen.findPreference(getPreferenceKey()); 96 } 97 98 @Override onStart()99 public void onStart() { 100 // Register for updates to the user's time zone capabilities or configuration which could 101 // require UI changes. 102 Executor mainExecutor = mContext.getMainExecutor(); 103 mTimeManager.addTimeZoneDetectorListener(mainExecutor, this); 104 // Setup the initial state. 105 refreshUi(); 106 } 107 108 @Override onStop()109 public void onStop() { 110 mTimeManager.removeTimeZoneDetectorListener(this); 111 } 112 113 @Override isSliceable()114 public boolean isSliceable() { 115 return true; 116 } 117 118 @Override getSliceHighlightMenuRes()119 public int getSliceHighlightMenuRes() { 120 return R.string.menu_key_system; 121 } 122 123 @Override updateState(@onNull Preference preference)124 public void updateState(@NonNull Preference preference) { 125 super.updateState(preference); 126 127 // enable / disable the toggle based on automatic time zone being enabled or not 128 preference.setEnabled(isAutoTimeZoneEnabled()); 129 } 130 131 132 @Override getAvailabilityStatus()133 public int getAvailabilityStatus() { 134 TimeZoneCapabilities timeZoneCapabilities = 135 getTimeZoneCapabilitiesAndConfig(/* forceRefresh= */ false).getCapabilities(); 136 int capability = timeZoneCapabilities.getConfigureNotificationsEnabledCapability(); 137 138 // The preference can be present and enabled, present and disabled or not present. 139 if (capability == CAPABILITY_NOT_SUPPORTED || capability == CAPABILITY_NOT_ALLOWED) { 140 return UNSUPPORTED_ON_DEVICE; 141 } else if (capability == CAPABILITY_NOT_APPLICABLE || capability == CAPABILITY_POSSESSED) { 142 return isAutoTimeZoneEnabled() ? AVAILABLE : DISABLED_DEPENDENT_SETTING; 143 } else { 144 Log.e(TAG, "Unknown capability=" + capability); 145 return UNSUPPORTED_ON_DEVICE; 146 } 147 } 148 149 /** 150 * Implementation of {@link TimeManager.TimeZoneDetectorListener#onChange()}. Called by the 151 * system server after a change that affects {@link TimeZoneCapabilitiesAndConfig}. 152 */ 153 @Override onChange()154 public void onChange() { 155 refreshUi(); 156 } 157 158 @Override 159 @NonNull getSummary()160 public CharSequence getSummary() { 161 return mContext.getString(R.string.time_zone_change_notifications_toggle_summary); 162 } 163 refreshUi()164 private void refreshUi() { 165 // Force a refresh of cached user capabilities and config. 166 getTimeZoneCapabilitiesAndConfig(/* forceRefresh= */ true); 167 refreshSummary(mPreference); 168 } 169 170 /** 171 * Returns the current user capabilities and configuration. {@code forceRefresh} can be {@code 172 * true} to discard any cached copy. 173 */ getTimeZoneCapabilitiesAndConfig(boolean forceRefresh)174 private TimeZoneCapabilitiesAndConfig getTimeZoneCapabilitiesAndConfig(boolean forceRefresh) { 175 if (forceRefresh || mTimeZoneCapabilitiesAndConfig == null) { 176 mTimeZoneCapabilitiesAndConfig = mTimeManager.getTimeZoneCapabilitiesAndConfig(); 177 } 178 return mTimeZoneCapabilitiesAndConfig; 179 } 180 181 /** 182 * Returns whether the user can select this preference or not, as it is a sub toggle of 183 * automatic time zone. 184 */ isAutoTimeZoneEnabled()185 private boolean isAutoTimeZoneEnabled() { 186 return mTimeManager.getTimeZoneCapabilitiesAndConfig().getConfiguration() 187 .isAutoDetectionEnabled(); 188 } 189 } 190