1 /* 2 * Copyright (C) 2008 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.systemui.statusbar.phone; 18 19 import android.annotation.Nullable; 20 import android.app.ActivityTaskManager; 21 import android.app.AlarmManager; 22 import android.app.AlarmManager.AlarmClockInfo; 23 import android.app.IActivityManager; 24 import android.app.SynchronousUserSwitchObserver; 25 import android.content.BroadcastReceiver; 26 import android.content.Context; 27 import android.content.Intent; 28 import android.content.IntentFilter; 29 import android.content.SharedPreferences; 30 import android.content.res.Resources; 31 import android.media.AudioManager; 32 import android.os.Handler; 33 import android.os.RemoteException; 34 import android.os.UserHandle; 35 import android.os.UserManager; 36 import android.provider.Settings.Global; 37 import android.service.notification.ZenModeConfig; 38 import android.telecom.TelecomManager; 39 import android.text.format.DateFormat; 40 import android.util.Log; 41 import android.view.View; 42 43 import androidx.lifecycle.Observer; 44 45 import com.android.systemui.R; 46 import com.android.systemui.broadcast.BroadcastDispatcher; 47 import com.android.systemui.dagger.qualifiers.DisplayId; 48 import com.android.systemui.dagger.qualifiers.Main; 49 import com.android.systemui.dagger.qualifiers.UiBackground; 50 import com.android.systemui.privacy.PrivacyItem; 51 import com.android.systemui.privacy.PrivacyItemController; 52 import com.android.systemui.privacy.PrivacyType; 53 import com.android.systemui.qs.tiles.DndTile; 54 import com.android.systemui.qs.tiles.RotationLockTile; 55 import com.android.systemui.screenrecord.RecordingController; 56 import com.android.systemui.statusbar.CommandQueue; 57 import com.android.systemui.statusbar.policy.BluetoothController; 58 import com.android.systemui.statusbar.policy.CastController; 59 import com.android.systemui.statusbar.policy.CastController.CastDevice; 60 import com.android.systemui.statusbar.policy.DataSaverController; 61 import com.android.systemui.statusbar.policy.DataSaverController.Listener; 62 import com.android.systemui.statusbar.policy.DeviceProvisionedController; 63 import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener; 64 import com.android.systemui.statusbar.policy.HotspotController; 65 import com.android.systemui.statusbar.policy.KeyguardStateController; 66 import com.android.systemui.statusbar.policy.LocationController; 67 import com.android.systemui.statusbar.policy.NextAlarmController; 68 import com.android.systemui.statusbar.policy.RotationLockController; 69 import com.android.systemui.statusbar.policy.RotationLockController.RotationLockControllerCallback; 70 import com.android.systemui.statusbar.policy.SensorPrivacyController; 71 import com.android.systemui.statusbar.policy.UserInfoController; 72 import com.android.systemui.statusbar.policy.ZenModeController; 73 import com.android.systemui.util.RingerModeTracker; 74 import com.android.systemui.util.time.DateFormatUtil; 75 76 import java.io.PrintWriter; 77 import java.io.StringWriter; 78 import java.util.List; 79 import java.util.Locale; 80 import java.util.concurrent.Executor; 81 82 import javax.inject.Inject; 83 84 /** 85 * This class contains all of the policy about which icons are installed in the status bar at boot 86 * time. It goes through the normal API for icons, even though it probably strictly doesn't need to. 87 */ 88 public class PhoneStatusBarPolicy 89 implements BluetoothController.Callback, 90 CommandQueue.Callbacks, 91 RotationLockControllerCallback, 92 Listener, 93 ZenModeController.Callback, 94 DeviceProvisionedListener, 95 KeyguardStateController.Callback, 96 PrivacyItemController.Callback, 97 LocationController.LocationChangeCallback, 98 RecordingController.RecordingStateChangeCallback { 99 private static final String TAG = "PhoneStatusBarPolicy"; 100 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 101 102 static final int LOCATION_STATUS_ICON_ID = PrivacyType.TYPE_LOCATION.getIconId(); 103 104 private final String mSlotCast; 105 private final String mSlotHotspot; 106 private final String mSlotBluetooth; 107 private final String mSlotTty; 108 private final String mSlotZen; 109 private final String mSlotVolume; 110 private final String mSlotAlarmClock; 111 private final String mSlotManagedProfile; 112 private final String mSlotRotate; 113 private final String mSlotHeadset; 114 private final String mSlotDataSaver; 115 private final String mSlotLocation; 116 private final String mSlotMicrophone; 117 private final String mSlotCamera; 118 private final String mSlotSensorsOff; 119 private final String mSlotScreenRecord; 120 private final int mDisplayId; 121 private final SharedPreferences mSharedPreferences; 122 private final DateFormatUtil mDateFormatUtil; 123 private final TelecomManager mTelecomManager; 124 125 private final Handler mHandler = new Handler(); 126 private final CastController mCast; 127 private final HotspotController mHotspot; 128 private final NextAlarmController mNextAlarmController; 129 private final AlarmManager mAlarmManager; 130 private final UserInfoController mUserInfoController; 131 private final IActivityManager mIActivityManager; 132 private final UserManager mUserManager; 133 private final StatusBarIconController mIconController; 134 private final CommandQueue mCommandQueue; 135 private final BroadcastDispatcher mBroadcastDispatcher; 136 private final Resources mResources; 137 private final RotationLockController mRotationLockController; 138 private final DataSaverController mDataSaver; 139 private final ZenModeController mZenController; 140 private final DeviceProvisionedController mProvisionedController; 141 private final KeyguardStateController mKeyguardStateController; 142 private final LocationController mLocationController; 143 private final PrivacyItemController mPrivacyItemController; 144 private final Executor mUiBgExecutor; 145 private final SensorPrivacyController mSensorPrivacyController; 146 private final RecordingController mRecordingController; 147 private final RingerModeTracker mRingerModeTracker; 148 149 private boolean mZenVisible; 150 private boolean mVolumeVisible; 151 private boolean mCurrentUserSetup; 152 153 private boolean mManagedProfileIconVisible = false; 154 155 private BluetoothController mBluetooth; 156 private AlarmManager.AlarmClockInfo mNextAlarm; 157 158 @Inject PhoneStatusBarPolicy(StatusBarIconController iconController, CommandQueue commandQueue, BroadcastDispatcher broadcastDispatcher, @UiBackground Executor uiBgExecutor, @Main Resources resources, CastController castController, HotspotController hotspotController, BluetoothController bluetoothController, NextAlarmController nextAlarmController, UserInfoController userInfoController, RotationLockController rotationLockController, DataSaverController dataSaverController, ZenModeController zenModeController, DeviceProvisionedController deviceProvisionedController, KeyguardStateController keyguardStateController, LocationController locationController, SensorPrivacyController sensorPrivacyController, IActivityManager iActivityManager, AlarmManager alarmManager, UserManager userManager, RecordingController recordingController, @Nullable TelecomManager telecomManager, @DisplayId int displayId, @Main SharedPreferences sharedPreferences, DateFormatUtil dateFormatUtil, RingerModeTracker ringerModeTracker, PrivacyItemController privacyItemController)159 public PhoneStatusBarPolicy(StatusBarIconController iconController, 160 CommandQueue commandQueue, BroadcastDispatcher broadcastDispatcher, 161 @UiBackground Executor uiBgExecutor, @Main Resources resources, 162 CastController castController, HotspotController hotspotController, 163 BluetoothController bluetoothController, NextAlarmController nextAlarmController, 164 UserInfoController userInfoController, RotationLockController rotationLockController, 165 DataSaverController dataSaverController, ZenModeController zenModeController, 166 DeviceProvisionedController deviceProvisionedController, 167 KeyguardStateController keyguardStateController, 168 LocationController locationController, 169 SensorPrivacyController sensorPrivacyController, IActivityManager iActivityManager, 170 AlarmManager alarmManager, UserManager userManager, 171 RecordingController recordingController, 172 @Nullable TelecomManager telecomManager, @DisplayId int displayId, 173 @Main SharedPreferences sharedPreferences, DateFormatUtil dateFormatUtil, 174 RingerModeTracker ringerModeTracker, 175 PrivacyItemController privacyItemController) { 176 mIconController = iconController; 177 mCommandQueue = commandQueue; 178 mBroadcastDispatcher = broadcastDispatcher; 179 mResources = resources; 180 mCast = castController; 181 mHotspot = hotspotController; 182 mBluetooth = bluetoothController; 183 mNextAlarmController = nextAlarmController; 184 mAlarmManager = alarmManager; 185 mUserInfoController = userInfoController; 186 mIActivityManager = iActivityManager; 187 mUserManager = userManager; 188 mRotationLockController = rotationLockController; 189 mDataSaver = dataSaverController; 190 mZenController = zenModeController; 191 mProvisionedController = deviceProvisionedController; 192 mKeyguardStateController = keyguardStateController; 193 mLocationController = locationController; 194 mPrivacyItemController = privacyItemController; 195 mSensorPrivacyController = sensorPrivacyController; 196 mRecordingController = recordingController; 197 mUiBgExecutor = uiBgExecutor; 198 mTelecomManager = telecomManager; 199 mRingerModeTracker = ringerModeTracker; 200 201 mSlotCast = resources.getString(com.android.internal.R.string.status_bar_cast); 202 mSlotHotspot = resources.getString(com.android.internal.R.string.status_bar_hotspot); 203 mSlotBluetooth = resources.getString(com.android.internal.R.string.status_bar_bluetooth); 204 mSlotTty = resources.getString(com.android.internal.R.string.status_bar_tty); 205 mSlotZen = resources.getString(com.android.internal.R.string.status_bar_zen); 206 mSlotVolume = resources.getString(com.android.internal.R.string.status_bar_volume); 207 mSlotAlarmClock = resources.getString(com.android.internal.R.string.status_bar_alarm_clock); 208 mSlotManagedProfile = resources.getString( 209 com.android.internal.R.string.status_bar_managed_profile); 210 mSlotRotate = resources.getString(com.android.internal.R.string.status_bar_rotate); 211 mSlotHeadset = resources.getString(com.android.internal.R.string.status_bar_headset); 212 mSlotDataSaver = resources.getString(com.android.internal.R.string.status_bar_data_saver); 213 mSlotLocation = resources.getString(com.android.internal.R.string.status_bar_location); 214 mSlotMicrophone = resources.getString(com.android.internal.R.string.status_bar_microphone); 215 mSlotCamera = resources.getString(com.android.internal.R.string.status_bar_camera); 216 mSlotSensorsOff = resources.getString(com.android.internal.R.string.status_bar_sensors_off); 217 mSlotScreenRecord = resources.getString( 218 com.android.internal.R.string.status_bar_screen_record); 219 220 mDisplayId = displayId; 221 mSharedPreferences = sharedPreferences; 222 mDateFormatUtil = dateFormatUtil; 223 } 224 225 /** Initialize the object after construction. */ init()226 public void init() { 227 // listen for broadcasts 228 IntentFilter filter = new IntentFilter(); 229 230 filter.addAction(AudioManager.ACTION_HEADSET_PLUG); 231 filter.addAction(Intent.ACTION_SIM_STATE_CHANGED); 232 filter.addAction(TelecomManager.ACTION_CURRENT_TTY_MODE_CHANGED); 233 filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE); 234 filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE); 235 filter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED); 236 mBroadcastDispatcher.registerReceiverWithHandler(mIntentReceiver, filter, mHandler); 237 Observer<Integer> observer = ringer -> mHandler.post(this::updateVolumeZen); 238 239 mRingerModeTracker.getRingerMode().observeForever(observer); 240 mRingerModeTracker.getRingerModeInternal().observeForever(observer); 241 242 // listen for user / profile change. 243 try { 244 mIActivityManager.registerUserSwitchObserver(mUserSwitchListener, TAG); 245 } catch (RemoteException e) { 246 // Ignore 247 } 248 249 // TTY status 250 updateTTY(); 251 252 // bluetooth status 253 updateBluetooth(); 254 255 // Alarm clock 256 mIconController.setIcon(mSlotAlarmClock, R.drawable.stat_sys_alarm, null); 257 mIconController.setIconVisibility(mSlotAlarmClock, false); 258 259 // zen 260 mIconController.setIcon(mSlotZen, R.drawable.stat_sys_dnd, null); 261 mIconController.setIconVisibility(mSlotZen, false); 262 263 // volume 264 mIconController.setIcon(mSlotVolume, R.drawable.stat_sys_ringer_vibrate, null); 265 mIconController.setIconVisibility(mSlotVolume, false); 266 updateVolumeZen(); 267 268 // cast 269 mIconController.setIcon(mSlotCast, R.drawable.stat_sys_cast, null); 270 mIconController.setIconVisibility(mSlotCast, false); 271 272 // hotspot 273 mIconController.setIcon(mSlotHotspot, R.drawable.stat_sys_hotspot, 274 mResources.getString(R.string.accessibility_status_bar_hotspot)); 275 mIconController.setIconVisibility(mSlotHotspot, mHotspot.isHotspotEnabled()); 276 277 // managed profile 278 mIconController.setIcon(mSlotManagedProfile, R.drawable.stat_sys_managed_profile_status, 279 mResources.getString(R.string.accessibility_managed_profile)); 280 mIconController.setIconVisibility(mSlotManagedProfile, mManagedProfileIconVisible); 281 282 // data saver 283 mIconController.setIcon(mSlotDataSaver, R.drawable.stat_sys_data_saver, 284 mResources.getString(R.string.accessibility_data_saver_on)); 285 mIconController.setIconVisibility(mSlotDataSaver, false); 286 287 288 // privacy items 289 String microphoneString = mResources.getString(PrivacyType.TYPE_MICROPHONE.getNameId()); 290 String microphoneDesc = mResources.getString( 291 R.string.ongoing_privacy_chip_content_multiple_apps, microphoneString); 292 mIconController.setIcon(mSlotMicrophone, PrivacyType.TYPE_MICROPHONE.getIconId(), 293 microphoneDesc); 294 mIconController.setIconVisibility(mSlotMicrophone, false); 295 296 String cameraString = mResources.getString(PrivacyType.TYPE_CAMERA.getNameId()); 297 String cameraDesc = mResources.getString( 298 R.string.ongoing_privacy_chip_content_multiple_apps, cameraString); 299 mIconController.setIcon(mSlotCamera, PrivacyType.TYPE_CAMERA.getIconId(), 300 cameraDesc); 301 mIconController.setIconVisibility(mSlotCamera, false); 302 303 mIconController.setIcon(mSlotLocation, LOCATION_STATUS_ICON_ID, 304 mResources.getString(R.string.accessibility_location_active)); 305 mIconController.setIconVisibility(mSlotLocation, false); 306 307 // sensors off 308 mIconController.setIcon(mSlotSensorsOff, R.drawable.stat_sys_sensors_off, 309 mResources.getString(R.string.accessibility_sensors_off_active)); 310 mIconController.setIconVisibility(mSlotSensorsOff, 311 mSensorPrivacyController.isSensorPrivacyEnabled()); 312 313 // screen record 314 mIconController.setIcon(mSlotScreenRecord, R.drawable.stat_sys_screen_record, null); 315 mIconController.setIconVisibility(mSlotScreenRecord, false); 316 317 mRotationLockController.addCallback(this); 318 mBluetooth.addCallback(this); 319 mProvisionedController.addCallback(this); 320 mZenController.addCallback(this); 321 mCast.addCallback(mCastCallback); 322 mHotspot.addCallback(mHotspotCallback); 323 mNextAlarmController.addCallback(mNextAlarmCallback); 324 mDataSaver.addCallback(this); 325 mKeyguardStateController.addCallback(this); 326 mPrivacyItemController.addCallback(this); 327 mSensorPrivacyController.addCallback(mSensorPrivacyListener); 328 mLocationController.addCallback(this); 329 mRecordingController.addCallback(this); 330 331 mCommandQueue.addCallback(this); 332 } 333 334 @Override onZenChanged(int zen)335 public void onZenChanged(int zen) { 336 updateVolumeZen(); 337 } 338 339 @Override onConfigChanged(ZenModeConfig config)340 public void onConfigChanged(ZenModeConfig config) { 341 updateVolumeZen(); 342 } 343 updateAlarm()344 private void updateAlarm() { 345 final AlarmClockInfo alarm = mAlarmManager.getNextAlarmClock(UserHandle.USER_CURRENT); 346 final boolean hasAlarm = alarm != null && alarm.getTriggerTime() > 0; 347 int zen = mZenController.getZen(); 348 final boolean zenNone = zen == Global.ZEN_MODE_NO_INTERRUPTIONS; 349 mIconController.setIcon(mSlotAlarmClock, zenNone ? R.drawable.stat_sys_alarm_dim 350 : R.drawable.stat_sys_alarm, buildAlarmContentDescription()); 351 mIconController.setIconVisibility(mSlotAlarmClock, mCurrentUserSetup && hasAlarm); 352 } 353 buildAlarmContentDescription()354 private String buildAlarmContentDescription() { 355 if (mNextAlarm == null) { 356 return mResources.getString(R.string.status_bar_alarm); 357 } 358 359 String skeleton = mDateFormatUtil.is24HourFormat() ? "EHm" : "Ehma"; 360 String pattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), skeleton); 361 String dateString = DateFormat.format(pattern, mNextAlarm.getTriggerTime()).toString(); 362 363 return mResources.getString(R.string.accessibility_quick_settings_alarm, dateString); 364 } 365 updateVolumeZen()366 private final void updateVolumeZen() { 367 boolean zenVisible = false; 368 int zenIconId = 0; 369 String zenDescription = null; 370 371 boolean volumeVisible = false; 372 int volumeIconId = 0; 373 String volumeDescription = null; 374 int zen = mZenController.getZen(); 375 376 if (DndTile.isVisible(mSharedPreferences) || DndTile.isCombinedIcon(mSharedPreferences)) { 377 zenVisible = zen != Global.ZEN_MODE_OFF; 378 zenIconId = R.drawable.stat_sys_dnd; 379 zenDescription = mResources.getString(R.string.quick_settings_dnd_label); 380 } else if (zen == Global.ZEN_MODE_NO_INTERRUPTIONS) { 381 zenVisible = true; 382 zenIconId = R.drawable.stat_sys_dnd; 383 zenDescription = mResources.getString(R.string.interruption_level_none); 384 } else if (zen == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS) { 385 zenVisible = true; 386 zenIconId = R.drawable.stat_sys_dnd; 387 zenDescription = mResources.getString(R.string.interruption_level_priority); 388 } 389 390 if (!ZenModeConfig.isZenOverridingRinger(zen, mZenController.getConsolidatedPolicy())) { 391 final Integer ringerModeInternal = 392 mRingerModeTracker.getRingerModeInternal().getValue(); 393 if (ringerModeInternal != null) { 394 if (ringerModeInternal == AudioManager.RINGER_MODE_VIBRATE) { 395 volumeVisible = true; 396 volumeIconId = R.drawable.stat_sys_ringer_vibrate; 397 volumeDescription = mResources.getString(R.string.accessibility_ringer_vibrate); 398 } else if (ringerModeInternal == AudioManager.RINGER_MODE_SILENT) { 399 volumeVisible = true; 400 volumeIconId = R.drawable.stat_sys_ringer_silent; 401 volumeDescription = mResources.getString(R.string.accessibility_ringer_silent); 402 } 403 } 404 } 405 406 if (zenVisible) { 407 mIconController.setIcon(mSlotZen, zenIconId, zenDescription); 408 } 409 if (zenVisible != mZenVisible) { 410 mIconController.setIconVisibility(mSlotZen, zenVisible); 411 mZenVisible = zenVisible; 412 } 413 414 if (volumeVisible) { 415 mIconController.setIcon(mSlotVolume, volumeIconId, volumeDescription); 416 } 417 if (volumeVisible != mVolumeVisible) { 418 mIconController.setIconVisibility(mSlotVolume, volumeVisible); 419 mVolumeVisible = volumeVisible; 420 } 421 updateAlarm(); 422 } 423 424 @Override onBluetoothDevicesChanged()425 public void onBluetoothDevicesChanged() { 426 updateBluetooth(); 427 } 428 429 @Override onBluetoothStateChange(boolean enabled)430 public void onBluetoothStateChange(boolean enabled) { 431 updateBluetooth(); 432 } 433 updateBluetooth()434 private final void updateBluetooth() { 435 int iconId = R.drawable.stat_sys_data_bluetooth_connected; 436 String contentDescription = 437 mResources.getString(R.string.accessibility_quick_settings_bluetooth_on); 438 boolean bluetoothVisible = false; 439 if (mBluetooth != null) { 440 if (mBluetooth.isBluetoothConnected() 441 && (mBluetooth.isBluetoothAudioActive() 442 || !mBluetooth.isBluetoothAudioProfileOnly())) { 443 contentDescription = mResources.getString( 444 R.string.accessibility_bluetooth_connected); 445 bluetoothVisible = mBluetooth.isBluetoothEnabled(); 446 } 447 } 448 449 mIconController.setIcon(mSlotBluetooth, iconId, contentDescription); 450 mIconController.setIconVisibility(mSlotBluetooth, bluetoothVisible); 451 } 452 updateTTY()453 private final void updateTTY() { 454 if (mTelecomManager == null) { 455 updateTTY(TelecomManager.TTY_MODE_OFF); 456 } else { 457 updateTTY(mTelecomManager.getCurrentTtyMode()); 458 } 459 } 460 updateTTY(int currentTtyMode)461 private final void updateTTY(int currentTtyMode) { 462 boolean enabled = currentTtyMode != TelecomManager.TTY_MODE_OFF; 463 464 if (DEBUG) Log.v(TAG, "updateTTY: enabled: " + enabled); 465 466 if (enabled) { 467 // TTY is on 468 if (DEBUG) Log.v(TAG, "updateTTY: set TTY on"); 469 mIconController.setIcon(mSlotTty, R.drawable.stat_sys_tty_mode, 470 mResources.getString(R.string.accessibility_tty_enabled)); 471 mIconController.setIconVisibility(mSlotTty, true); 472 } else { 473 // TTY is off 474 if (DEBUG) Log.v(TAG, "updateTTY: set TTY off"); 475 mIconController.setIconVisibility(mSlotTty, false); 476 } 477 } 478 updateCast()479 private void updateCast() { 480 boolean isCasting = false; 481 for (CastDevice device : mCast.getCastDevices()) { 482 if (device.state == CastDevice.STATE_CONNECTING 483 || device.state == CastDevice.STATE_CONNECTED) { 484 isCasting = true; 485 break; 486 } 487 } 488 if (DEBUG) Log.v(TAG, "updateCast: isCasting: " + isCasting); 489 mHandler.removeCallbacks(mRemoveCastIconRunnable); 490 if (isCasting && !mRecordingController.isRecording()) { // screen record has its own icon 491 mIconController.setIcon(mSlotCast, R.drawable.stat_sys_cast, 492 mResources.getString(R.string.accessibility_casting)); 493 mIconController.setIconVisibility(mSlotCast, true); 494 } else { 495 // don't turn off the screen-record icon for a few seconds, just to make sure the user 496 // has seen it 497 if (DEBUG) Log.v(TAG, "updateCast: hiding icon in 3 sec..."); 498 mHandler.postDelayed(mRemoveCastIconRunnable, 3000); 499 } 500 } 501 updateManagedProfile()502 private void updateManagedProfile() { 503 // getLastResumedActivityUserId needds to acquire the AM lock, which may be contended in 504 // some cases. Since it doesn't really matter here whether it's updated in this frame 505 // or in the next one, we call this method from our UI offload thread. 506 mUiBgExecutor.execute(() -> { 507 final int userId; 508 try { 509 userId = ActivityTaskManager.getService().getLastResumedActivityUserId(); 510 boolean isManagedProfile = mUserManager.isManagedProfile(userId); 511 mHandler.post(() -> { 512 final boolean showIcon; 513 if (isManagedProfile && (!mKeyguardStateController.isShowing() 514 || mKeyguardStateController.isOccluded())) { 515 showIcon = true; 516 mIconController.setIcon(mSlotManagedProfile, 517 R.drawable.stat_sys_managed_profile_status, 518 mResources.getString(R.string.accessibility_managed_profile)); 519 } else { 520 showIcon = false; 521 } 522 if (mManagedProfileIconVisible != showIcon) { 523 mIconController.setIconVisibility(mSlotManagedProfile, showIcon); 524 mManagedProfileIconVisible = showIcon; 525 } 526 }); 527 } catch (RemoteException e) { 528 Log.w(TAG, "updateManagedProfile: ", e); 529 } 530 }); 531 } 532 533 private final SynchronousUserSwitchObserver mUserSwitchListener = 534 new SynchronousUserSwitchObserver() { 535 @Override 536 public void onUserSwitching(int newUserId) throws RemoteException { 537 mHandler.post(() -> mUserInfoController.reloadUserInfo()); 538 } 539 540 @Override 541 public void onUserSwitchComplete(int newUserId) throws RemoteException { 542 mHandler.post(() -> { 543 updateAlarm(); 544 updateManagedProfile(); 545 }); 546 } 547 }; 548 549 private final HotspotController.Callback mHotspotCallback = new HotspotController.Callback() { 550 @Override 551 public void onHotspotChanged(boolean enabled, int numDevices) { 552 mIconController.setIconVisibility(mSlotHotspot, enabled); 553 } 554 }; 555 556 private final CastController.Callback mCastCallback = new CastController.Callback() { 557 @Override 558 public void onCastDevicesChanged() { 559 updateCast(); 560 } 561 }; 562 563 private final NextAlarmController.NextAlarmChangeCallback mNextAlarmCallback = 564 new NextAlarmController.NextAlarmChangeCallback() { 565 @Override 566 public void onNextAlarmChanged(AlarmManager.AlarmClockInfo nextAlarm) { 567 mNextAlarm = nextAlarm; 568 updateAlarm(); 569 } 570 }; 571 572 private final SensorPrivacyController.OnSensorPrivacyChangedListener mSensorPrivacyListener = 573 new SensorPrivacyController.OnSensorPrivacyChangedListener() { 574 @Override 575 public void onSensorPrivacyChanged(boolean enabled) { 576 mHandler.post(() -> { 577 mIconController.setIconVisibility(mSlotSensorsOff, enabled); 578 }); 579 } 580 }; 581 582 @Override appTransitionStarting(int displayId, long startTime, long duration, boolean forced)583 public void appTransitionStarting(int displayId, long startTime, long duration, 584 boolean forced) { 585 if (mDisplayId == displayId) { 586 updateManagedProfile(); 587 } 588 } 589 590 @Override onKeyguardShowingChanged()591 public void onKeyguardShowingChanged() { 592 updateManagedProfile(); 593 } 594 595 @Override onUserSetupChanged()596 public void onUserSetupChanged() { 597 boolean userSetup = mProvisionedController.isUserSetup( 598 mProvisionedController.getCurrentUser()); 599 if (mCurrentUserSetup == userSetup) return; 600 mCurrentUserSetup = userSetup; 601 updateAlarm(); 602 } 603 604 @Override onRotationLockStateChanged(boolean rotationLocked, boolean affordanceVisible)605 public void onRotationLockStateChanged(boolean rotationLocked, boolean affordanceVisible) { 606 boolean portrait = RotationLockTile.isCurrentOrientationLockPortrait( 607 mRotationLockController, mResources); 608 if (rotationLocked) { 609 if (portrait) { 610 mIconController.setIcon(mSlotRotate, R.drawable.stat_sys_rotate_portrait, 611 mResources.getString(R.string.accessibility_rotation_lock_on_portrait)); 612 } else { 613 mIconController.setIcon(mSlotRotate, R.drawable.stat_sys_rotate_landscape, 614 mResources.getString(R.string.accessibility_rotation_lock_on_landscape)); 615 } 616 mIconController.setIconVisibility(mSlotRotate, true); 617 } else { 618 mIconController.setIconVisibility(mSlotRotate, false); 619 } 620 } 621 updateHeadsetPlug(Intent intent)622 private void updateHeadsetPlug(Intent intent) { 623 boolean connected = intent.getIntExtra("state", 0) != 0; 624 boolean hasMic = intent.getIntExtra("microphone", 0) != 0; 625 if (connected) { 626 String contentDescription = mResources.getString(hasMic 627 ? R.string.accessibility_status_bar_headset 628 : R.string.accessibility_status_bar_headphones); 629 mIconController.setIcon(mSlotHeadset, hasMic ? R.drawable.stat_sys_headset_mic 630 : R.drawable.stat_sys_headset, contentDescription); 631 mIconController.setIconVisibility(mSlotHeadset, true); 632 } else { 633 mIconController.setIconVisibility(mSlotHeadset, false); 634 } 635 } 636 637 @Override onDataSaverChanged(boolean isDataSaving)638 public void onDataSaverChanged(boolean isDataSaving) { 639 mIconController.setIconVisibility(mSlotDataSaver, isDataSaving); 640 } 641 642 @Override // PrivacyItemController.Callback onPrivacyItemsChanged(List<PrivacyItem> privacyItems)643 public void onPrivacyItemsChanged(List<PrivacyItem> privacyItems) { 644 updatePrivacyItems(privacyItems); 645 } 646 updatePrivacyItems(List<PrivacyItem> items)647 private void updatePrivacyItems(List<PrivacyItem> items) { 648 boolean showCamera = false; 649 boolean showMicrophone = false; 650 boolean showLocation = false; 651 for (PrivacyItem item : items) { 652 if (item == null /* b/124234367 */) { 653 if (DEBUG) { 654 Log.e(TAG, "updatePrivacyItems - null item found"); 655 StringWriter out = new StringWriter(); 656 mPrivacyItemController.dump(null, new PrintWriter(out), null); 657 Log.e(TAG, out.toString()); 658 } 659 continue; 660 } 661 switch (item.getPrivacyType()) { 662 case TYPE_CAMERA: 663 showCamera = true; 664 break; 665 case TYPE_LOCATION: 666 showLocation = true; 667 break; 668 case TYPE_MICROPHONE: 669 showMicrophone = true; 670 break; 671 } 672 } 673 674 mIconController.setIconVisibility(mSlotCamera, showCamera); 675 mIconController.setIconVisibility(mSlotMicrophone, showMicrophone); 676 if (mPrivacyItemController.getAllIndicatorsAvailable()) { 677 mIconController.setIconVisibility(mSlotLocation, showLocation); 678 } 679 } 680 681 @Override onLocationActiveChanged(boolean active)682 public void onLocationActiveChanged(boolean active) { 683 if (!mPrivacyItemController.getAllIndicatorsAvailable()) updateLocationFromController(); 684 } 685 686 // Updates the status view based on the current state of location requests. updateLocationFromController()687 private void updateLocationFromController() { 688 if (mLocationController.isLocationActive()) { 689 mIconController.setIconVisibility(mSlotLocation, true); 690 } else { 691 mIconController.setIconVisibility(mSlotLocation, false); 692 } 693 } 694 695 private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { 696 @Override 697 public void onReceive(Context context, Intent intent) { 698 String action = intent.getAction(); 699 switch (action) { 700 case Intent.ACTION_SIM_STATE_CHANGED: 701 // Avoid rebroadcast because SysUI is direct boot aware. 702 if (intent.getBooleanExtra(Intent.EXTRA_REBROADCAST_ON_UNLOCK, false)) { 703 break; 704 } 705 break; 706 case TelecomManager.ACTION_CURRENT_TTY_MODE_CHANGED: 707 updateTTY(intent.getIntExtra(TelecomManager.EXTRA_CURRENT_TTY_MODE, 708 TelecomManager.TTY_MODE_OFF)); 709 break; 710 case Intent.ACTION_MANAGED_PROFILE_AVAILABLE: 711 case Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE: 712 case Intent.ACTION_MANAGED_PROFILE_REMOVED: 713 updateManagedProfile(); 714 break; 715 case AudioManager.ACTION_HEADSET_PLUG: 716 updateHeadsetPlug(intent); 717 break; 718 } 719 } 720 }; 721 722 private Runnable mRemoveCastIconRunnable = new Runnable() { 723 @Override 724 public void run() { 725 if (DEBUG) Log.v(TAG, "updateCast: hiding icon NOW"); 726 mIconController.setIconVisibility(mSlotCast, false); 727 } 728 }; 729 730 // Screen Recording 731 @Override onCountdown(long millisUntilFinished)732 public void onCountdown(long millisUntilFinished) { 733 if (DEBUG) Log.d(TAG, "screenrecord: countdown " + millisUntilFinished); 734 int countdown = (int) Math.floorDiv(millisUntilFinished + 500, 1000); 735 int resourceId = R.drawable.stat_sys_screen_record; 736 String description = Integer.toString(countdown); 737 switch (countdown) { 738 case 1: 739 resourceId = R.drawable.stat_sys_screen_record_1; 740 break; 741 case 2: 742 resourceId = R.drawable.stat_sys_screen_record_2; 743 break; 744 case 3: 745 resourceId = R.drawable.stat_sys_screen_record_3; 746 break; 747 } 748 mIconController.setIcon(mSlotScreenRecord, resourceId, description); 749 mIconController.setIconVisibility(mSlotScreenRecord, true); 750 // Set as assertive so talkback will announce the countdown 751 mIconController.setIconAccessibilityLiveRegion(mSlotScreenRecord, 752 View.ACCESSIBILITY_LIVE_REGION_ASSERTIVE); 753 } 754 755 @Override onCountdownEnd()756 public void onCountdownEnd() { 757 if (DEBUG) Log.d(TAG, "screenrecord: hiding icon during countdown"); 758 mHandler.post(() -> mIconController.setIconVisibility(mSlotScreenRecord, false)); 759 // Reset talkback priority 760 mHandler.post(() -> mIconController.setIconAccessibilityLiveRegion(mSlotScreenRecord, 761 View.ACCESSIBILITY_LIVE_REGION_NONE)); 762 } 763 764 @Override onRecordingStart()765 public void onRecordingStart() { 766 if (DEBUG) Log.d(TAG, "screenrecord: showing icon"); 767 mIconController.setIcon(mSlotScreenRecord, 768 R.drawable.stat_sys_screen_record, 769 mResources.getString(R.string.screenrecord_ongoing_screen_only)); 770 mHandler.post(() -> mIconController.setIconVisibility(mSlotScreenRecord, true)); 771 } 772 773 @Override onRecordingEnd()774 public void onRecordingEnd() { 775 // Ensure this is on the main thread 776 if (DEBUG) Log.d(TAG, "screenrecord: hiding icon"); 777 mHandler.post(() -> mIconController.setIconVisibility(mSlotScreenRecord, false)); 778 } 779 } 780