1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 * except in compliance with the License. You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software distributed under the 10 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 11 * KIND, either express or implied. See the License for the specific language governing 12 * permissions and limitations under the License. 13 */ 14 15 package com.android.systemui.statusbar.phone; 16 17 import android.content.Context; 18 import android.content.res.Resources; 19 import android.hardware.display.ColorDisplayManager; 20 import android.hardware.display.NightDisplayListener; 21 import android.os.Handler; 22 import android.os.UserHandle; 23 import android.util.Log; 24 25 import com.android.internal.annotations.VisibleForTesting; 26 import com.android.systemui.R; 27 import com.android.systemui.dagger.qualifiers.Background; 28 import com.android.systemui.qs.AutoAddTracker; 29 import com.android.systemui.qs.QSTileHost; 30 import com.android.systemui.qs.SecureSetting; 31 import com.android.systemui.qs.external.CustomTile; 32 import com.android.systemui.statusbar.policy.CastController; 33 import com.android.systemui.statusbar.policy.CastController.CastDevice; 34 import com.android.systemui.statusbar.policy.DataSaverController; 35 import com.android.systemui.statusbar.policy.DataSaverController.Listener; 36 import com.android.systemui.statusbar.policy.HotspotController; 37 import com.android.systemui.statusbar.policy.HotspotController.Callback; 38 import com.android.systemui.util.UserAwareController; 39 40 import java.util.ArrayList; 41 import java.util.Objects; 42 43 import javax.inject.Inject; 44 45 /** 46 * Manages which tiles should be automatically added to QS. 47 */ 48 public class AutoTileManager implements UserAwareController { 49 private static final String TAG = "AutoTileManager"; 50 51 public static final String HOTSPOT = "hotspot"; 52 public static final String SAVER = "saver"; 53 public static final String INVERSION = "inversion"; 54 public static final String WORK = "work"; 55 public static final String NIGHT = "night"; 56 public static final String CAST = "cast"; 57 static final String SETTING_SEPARATOR = ":"; 58 59 private UserHandle mCurrentUser; 60 61 private final Context mContext; 62 private final QSTileHost mHost; 63 private final Handler mHandler; 64 private final AutoAddTracker mAutoTracker; 65 private final HotspotController mHotspotController; 66 private final DataSaverController mDataSaverController; 67 private final ManagedProfileController mManagedProfileController; 68 private final NightDisplayListener mNightDisplayListener; 69 private final CastController mCastController; 70 private final ArrayList<AutoAddSetting> mAutoAddSettingList = new ArrayList<>(); 71 72 @Inject AutoTileManager(Context context, AutoAddTracker.Builder autoAddTrackerBuilder, QSTileHost host, @Background Handler handler, HotspotController hotspotController, DataSaverController dataSaverController, ManagedProfileController managedProfileController, NightDisplayListener nightDisplayListener, CastController castController)73 public AutoTileManager(Context context, AutoAddTracker.Builder autoAddTrackerBuilder, 74 QSTileHost host, 75 @Background Handler handler, 76 HotspotController hotspotController, 77 DataSaverController dataSaverController, 78 ManagedProfileController managedProfileController, 79 NightDisplayListener nightDisplayListener, 80 CastController castController) { 81 mContext = context; 82 mHost = host; 83 mCurrentUser = mHost.getUserContext().getUser(); 84 mAutoTracker = autoAddTrackerBuilder.setUserId(mCurrentUser.getIdentifier()).build(); 85 mHandler = handler; 86 mHotspotController = hotspotController; 87 mDataSaverController = dataSaverController; 88 mManagedProfileController = managedProfileController; 89 mNightDisplayListener = nightDisplayListener; 90 mCastController = castController; 91 92 populateSettingsList(); 93 startControllersAndSettingsListeners(); 94 } 95 startControllersAndSettingsListeners()96 protected void startControllersAndSettingsListeners() { 97 if (!mAutoTracker.isAdded(HOTSPOT)) { 98 mHotspotController.addCallback(mHotspotCallback); 99 } 100 if (!mAutoTracker.isAdded(SAVER)) { 101 mDataSaverController.addCallback(mDataSaverListener); 102 } 103 if (!mAutoTracker.isAdded(WORK)) { 104 mManagedProfileController.addCallback(mProfileCallback); 105 } 106 if (!mAutoTracker.isAdded(NIGHT) 107 && ColorDisplayManager.isNightDisplayAvailable(mContext)) { 108 mNightDisplayListener.setCallback(mNightDisplayCallback); 109 } 110 if (!mAutoTracker.isAdded(CAST)) { 111 mCastController.addCallback(mCastCallback); 112 } 113 114 int settingsN = mAutoAddSettingList.size(); 115 for (int i = 0; i < settingsN; i++) { 116 if (!mAutoTracker.isAdded(mAutoAddSettingList.get(i).mSpec)) { 117 mAutoAddSettingList.get(i).setListening(true); 118 } 119 } 120 } 121 stopListening()122 protected void stopListening() { 123 mHotspotController.removeCallback(mHotspotCallback); 124 mDataSaverController.removeCallback(mDataSaverListener); 125 mManagedProfileController.removeCallback(mProfileCallback); 126 if (ColorDisplayManager.isNightDisplayAvailable(mContext)) { 127 mNightDisplayListener.setCallback(null); 128 } 129 mCastController.removeCallback(mCastCallback); 130 int settingsN = mAutoAddSettingList.size(); 131 for (int i = 0; i < settingsN; i++) { 132 mAutoAddSettingList.get(i).setListening(false); 133 } 134 } 135 destroy()136 public void destroy() { 137 stopListening(); 138 mAutoTracker.destroy(); 139 } 140 141 /** 142 * Populates a list with the pairs setting:spec in the config resource. 143 * <p> 144 * This will only create {@link AutoAddSetting} objects for those tiles that have not been 145 * auto-added before, and set the corresponding {@link ContentObserver} to listening. 146 */ populateSettingsList()147 private void populateSettingsList() { 148 String [] autoAddList; 149 try { 150 autoAddList = mContext.getResources().getStringArray( 151 R.array.config_quickSettingsAutoAdd); 152 } catch (Resources.NotFoundException e) { 153 Log.w(TAG, "Missing config resource"); 154 return; 155 } 156 // getStringArray returns @NotNull, so if we got here, autoAddList is not null 157 for (String tile : autoAddList) { 158 String[] split = tile.split(SETTING_SEPARATOR); 159 if (split.length == 2) { 160 String setting = split[0]; 161 String spec = split[1]; 162 // Populate all the settings. As they may not have been added in other users 163 AutoAddSetting s = new AutoAddSetting(mContext, mHandler, setting, spec); 164 mAutoAddSettingList.add(s); 165 } else { 166 Log.w(TAG, "Malformed item in array: " + tile); 167 } 168 } 169 } 170 171 @Override changeUser(UserHandle newUser)172 public void changeUser(UserHandle newUser) { 173 if (!Thread.currentThread().equals(mHandler.getLooper().getThread())) { 174 mHandler.post(() -> changeUser(newUser)); 175 return; 176 } 177 if (newUser.getIdentifier() == mCurrentUser.getIdentifier()) { 178 return; 179 } 180 stopListening(); 181 mCurrentUser = newUser; 182 int settingsN = mAutoAddSettingList.size(); 183 for (int i = 0; i < settingsN; i++) { 184 mAutoAddSettingList.get(i).setUserId(newUser.getIdentifier()); 185 } 186 mAutoTracker.changeUser(newUser); 187 startControllersAndSettingsListeners(); 188 } 189 190 @Override getCurrentUserId()191 public int getCurrentUserId() { 192 return mCurrentUser.getIdentifier(); 193 } 194 unmarkTileAsAutoAdded(String tabSpec)195 public void unmarkTileAsAutoAdded(String tabSpec) { 196 mAutoTracker.setTileRemoved(tabSpec); 197 } 198 199 private final ManagedProfileController.Callback mProfileCallback = 200 new ManagedProfileController.Callback() { 201 @Override 202 public void onManagedProfileChanged() { 203 if (mAutoTracker.isAdded(WORK)) return; 204 if (mManagedProfileController.hasActiveProfile()) { 205 mHost.addTile(WORK); 206 mAutoTracker.setTileAdded(WORK); 207 } 208 } 209 210 @Override 211 public void onManagedProfileRemoved() { 212 } 213 }; 214 215 private final DataSaverController.Listener mDataSaverListener = new Listener() { 216 @Override 217 public void onDataSaverChanged(boolean isDataSaving) { 218 if (mAutoTracker.isAdded(SAVER)) return; 219 if (isDataSaving) { 220 mHost.addTile(SAVER); 221 mAutoTracker.setTileAdded(SAVER); 222 mHandler.post(() -> mDataSaverController.removeCallback(mDataSaverListener)); 223 } 224 } 225 }; 226 227 private final HotspotController.Callback mHotspotCallback = new Callback() { 228 @Override 229 public void onHotspotChanged(boolean enabled, int numDevices) { 230 if (mAutoTracker.isAdded(HOTSPOT)) return; 231 if (enabled) { 232 mHost.addTile(HOTSPOT); 233 mAutoTracker.setTileAdded(HOTSPOT); 234 mHandler.post(() -> mHotspotController.removeCallback(mHotspotCallback)); 235 } 236 } 237 }; 238 239 @VisibleForTesting 240 final NightDisplayListener.Callback mNightDisplayCallback = 241 new NightDisplayListener.Callback() { 242 @Override 243 public void onActivated(boolean activated) { 244 if (activated) { 245 addNightTile(); 246 } 247 } 248 249 @Override 250 public void onAutoModeChanged(int autoMode) { 251 if (autoMode == ColorDisplayManager.AUTO_MODE_CUSTOM_TIME 252 || autoMode == ColorDisplayManager.AUTO_MODE_TWILIGHT) { 253 addNightTile(); 254 } 255 } 256 257 private void addNightTile() { 258 if (mAutoTracker.isAdded(NIGHT)) return; 259 mHost.addTile(NIGHT); 260 mAutoTracker.setTileAdded(NIGHT); 261 mHandler.post(() -> mNightDisplayListener.setCallback(null)); 262 } 263 }; 264 265 @VisibleForTesting 266 final CastController.Callback mCastCallback = new CastController.Callback() { 267 @Override 268 public void onCastDevicesChanged() { 269 if (mAutoTracker.isAdded(CAST)) return; 270 271 boolean isCasting = false; 272 for (CastDevice device : mCastController.getCastDevices()) { 273 if (device.state == CastDevice.STATE_CONNECTED 274 || device.state == CastDevice.STATE_CONNECTING) { 275 isCasting = true; 276 break; 277 } 278 } 279 280 if (isCasting) { 281 mHost.addTile(CAST); 282 mAutoTracker.setTileAdded(CAST); 283 mHandler.post(() -> mCastController.removeCallback(mCastCallback)); 284 } 285 } 286 }; 287 288 @VisibleForTesting getSecureSettingForKey(String key)289 protected SecureSetting getSecureSettingForKey(String key) { 290 for (SecureSetting s : mAutoAddSettingList) { 291 if (Objects.equals(key, s.getKey())) { 292 return s; 293 } 294 } 295 return null; 296 } 297 298 /** 299 * Tracks tiles that should be auto added when a setting changes. 300 * <p> 301 * When the setting changes to a value different from 0, if the tile has not been auto added 302 * before, it will be added and the listener will be stopped. 303 */ 304 private class AutoAddSetting extends SecureSetting { 305 private final String mSpec; 306 AutoAddSetting(Context context, Handler handler, String setting, String tileSpec)307 AutoAddSetting(Context context, Handler handler, String setting, String tileSpec) { 308 super(context, handler, setting); 309 mSpec = tileSpec; 310 } 311 312 @Override handleValueChanged(int value, boolean observedChange)313 protected void handleValueChanged(int value, boolean observedChange) { 314 if (mAutoTracker.isAdded(mSpec)) { 315 // This should not be listening anymore 316 mHandler.post(() -> setListening(false)); 317 return; 318 } 319 if (value != 0) { 320 if (mSpec.startsWith(CustomTile.PREFIX)) { 321 mHost.addTile(CustomTile.getComponentFromSpec(mSpec), /* end */ true); 322 } else { 323 mHost.addTile(mSpec); 324 } 325 mAutoTracker.setTileAdded(mSpec); 326 mHandler.post(() -> setListening(false)); 327 } 328 } 329 } 330 } 331