1 /* 2 * Copyright (C) 2017 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.notification.zen; 18 19 import android.app.ActivityManager; 20 import android.app.AlarmManager; 21 import android.app.AlarmManager.AlarmClockInfo; 22 import android.app.NotificationManager; 23 import android.content.ContentResolver; 24 import android.content.Context; 25 import android.database.ContentObserver; 26 import android.net.Uri; 27 import android.os.Handler; 28 import android.os.UserHandle; 29 import android.provider.Settings; 30 import android.service.notification.ScheduleCalendar; 31 import android.service.notification.ZenModeConfig; 32 33 import androidx.annotation.VisibleForTesting; 34 import androidx.preference.Preference; 35 import androidx.preference.PreferenceScreen; 36 37 import com.android.settings.core.PreferenceControllerMixin; 38 import com.android.settings.overlay.FeatureFactory; 39 import com.android.settingslib.core.AbstractPreferenceController; 40 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; 41 import com.android.settingslib.core.lifecycle.Lifecycle; 42 import com.android.settingslib.core.lifecycle.LifecycleObserver; 43 import com.android.settingslib.core.lifecycle.events.OnPause; 44 import com.android.settingslib.core.lifecycle.events.OnResume; 45 46 abstract public class AbstractZenModePreferenceController extends 47 AbstractPreferenceController implements PreferenceControllerMixin, LifecycleObserver, 48 OnResume, OnPause { 49 50 @VisibleForTesting 51 protected SettingObserver mSettingObserver; 52 53 final String KEY; 54 final private NotificationManager mNotificationManager; 55 protected static ZenModeConfigWrapper mZenModeConfigWrapper; 56 protected MetricsFeatureProvider mMetricsFeatureProvider; 57 protected final ZenModeBackend mBackend; 58 protected PreferenceScreen mScreen; 59 AbstractZenModePreferenceController(Context context, String key, Lifecycle lifecycle)60 public AbstractZenModePreferenceController(Context context, String key, 61 Lifecycle lifecycle) { 62 super(context); 63 mZenModeConfigWrapper = new ZenModeConfigWrapper(context); 64 if (lifecycle != null) { 65 lifecycle.addObserver(this); 66 } 67 KEY = key; 68 mNotificationManager = (NotificationManager) context.getSystemService( 69 Context.NOTIFICATION_SERVICE); 70 71 final FeatureFactory featureFactory = FeatureFactory.getFeatureFactory(); 72 mMetricsFeatureProvider = featureFactory.getMetricsFeatureProvider(); 73 mBackend = ZenModeBackend.getInstance(context); 74 } 75 76 @Override getPreferenceKey()77 public String getPreferenceKey() { 78 return KEY; 79 } 80 81 @Override displayPreference(PreferenceScreen screen)82 public void displayPreference(PreferenceScreen screen) { 83 super.displayPreference(screen); 84 mScreen = screen; 85 Preference pref = screen.findPreference(KEY); 86 if (pref != null) { 87 if (mSettingObserver == null) { 88 mSettingObserver = new SettingObserver(); 89 } 90 mSettingObserver.setPreference(pref); 91 } 92 } 93 94 @Override onResume()95 public void onResume() { 96 if (mSettingObserver != null) { 97 mSettingObserver.register(mContext.getContentResolver()); 98 mSettingObserver.onChange(false, null); 99 } 100 } 101 102 @Override onPause()103 public void onPause() { 104 if (mSettingObserver != null) { 105 mSettingObserver.unregister(mContext.getContentResolver()); 106 } 107 } 108 getPolicy()109 protected NotificationManager.Policy getPolicy() { 110 return mNotificationManager.getNotificationPolicy(); 111 } 112 getZenModeConfig()113 protected ZenModeConfig getZenModeConfig() { 114 return mNotificationManager.getZenModeConfig(); 115 } 116 getZenMode()117 protected int getZenMode() { 118 return Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.ZEN_MODE, 119 mBackend.mZenMode); 120 } 121 getZenDuration()122 protected int getZenDuration() { 123 return Settings.Secure.getInt(mContext.getContentResolver(), Settings.Secure.ZEN_DURATION, 124 0); 125 } 126 127 class SettingObserver extends ContentObserver { 128 private final Uri ZEN_MODE_URI = Settings.Global.getUriFor(Settings.Global.ZEN_MODE); 129 private final Uri ZEN_MODE_CONFIG_ETAG_URI = Settings.Global.getUriFor( 130 Settings.Global.ZEN_MODE_CONFIG_ETAG); 131 private final Uri ZEN_MODE_DURATION_URI = Settings.Secure.getUriFor( 132 Settings.Secure.ZEN_DURATION); 133 134 private Preference mPreference; 135 SettingObserver()136 public SettingObserver() { 137 super(new Handler()); 138 } 139 setPreference(Preference preference)140 public void setPreference(Preference preference) { 141 mPreference = preference; 142 } 143 register(ContentResolver cr)144 public void register(ContentResolver cr) { 145 cr.registerContentObserver(ZEN_MODE_URI, false, this, UserHandle.USER_ALL); 146 cr.registerContentObserver(ZEN_MODE_CONFIG_ETAG_URI, false, this, UserHandle.USER_ALL); 147 cr.registerContentObserver(ZEN_MODE_DURATION_URI, false, this, UserHandle.USER_ALL); 148 } 149 unregister(ContentResolver cr)150 public void unregister(ContentResolver cr) { 151 cr.unregisterContentObserver(this); 152 } 153 154 @Override onChange(boolean selfChange, Uri uri)155 public void onChange(boolean selfChange, Uri uri) { 156 super.onChange(selfChange, uri); 157 if (uri == null || ZEN_MODE_URI.equals(uri) || ZEN_MODE_CONFIG_ETAG_URI.equals(uri) 158 || ZEN_MODE_DURATION_URI.equals(uri)) { 159 mBackend.updatePolicy(); 160 mBackend.updateZenMode(); 161 if (mScreen != null) { 162 displayPreference(mScreen); 163 } 164 updateState(mPreference); 165 } 166 } 167 } 168 169 /** 170 * Wrapper for testing compatibility 171 */ 172 @VisibleForTesting 173 static class ZenModeConfigWrapper { 174 private final Context mContext; 175 ZenModeConfigWrapper(Context context)176 public ZenModeConfigWrapper(Context context) { 177 mContext = context; 178 } 179 getOwnerCaption(String owner)180 protected String getOwnerCaption(String owner) { 181 return ZenModeConfig.getOwnerCaption(mContext, owner); 182 } 183 isTimeRule(Uri id)184 protected boolean isTimeRule(Uri id) { 185 return ZenModeConfig.isValidEventConditionId(id) || 186 ZenModeConfig.isValidScheduleConditionId(id); 187 } 188 getFormattedTime(long time, int userHandle)189 protected CharSequence getFormattedTime(long time, int userHandle) { 190 return ZenModeConfig.getFormattedTime(mContext, time, isToday(time), userHandle); 191 } 192 isToday(long time)193 private boolean isToday(long time) { 194 return ZenModeConfig.isToday(time); 195 } 196 parseManualRuleTime(Uri id)197 protected long parseManualRuleTime(Uri id) { 198 return ZenModeConfig.tryParseCountdownConditionId(id); 199 } 200 parseAutomaticRuleEndTime(Uri id)201 protected long parseAutomaticRuleEndTime(Uri id) { 202 if (ZenModeConfig.isValidEventConditionId(id)) { 203 // cannot look up end times for events 204 return Long.MAX_VALUE; 205 } 206 207 if (ZenModeConfig.isValidScheduleConditionId(id)) { 208 ScheduleCalendar schedule = ZenModeConfig.toScheduleCalendar(id); 209 long endTimeMs = schedule.getNextChangeTime(System.currentTimeMillis()); 210 211 // check if automatic rule will end on next alarm 212 if (schedule.exitAtAlarm()) { 213 long nextAlarm = getNextAlarm(mContext); 214 schedule.maybeSetNextAlarm(System.currentTimeMillis(), nextAlarm); 215 if (schedule.shouldExitForAlarm(endTimeMs)) { 216 return nextAlarm; 217 } 218 } 219 220 return endTimeMs; 221 } 222 223 return -1; 224 } 225 } 226 getNextAlarm(Context context)227 private static long getNextAlarm(Context context) { 228 final AlarmManager alarms = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); 229 final AlarmClockInfo info = alarms.getNextAlarmClock(ActivityManager.getCurrentUser()); 230 return info != null ? info.getTriggerTime() : 0; 231 } 232 } 233