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.app.ActivityManager; 20 import android.app.ActivityManagerNative; 21 import android.app.AlarmManager; 22 import android.app.AlarmManager.AlarmClockInfo; 23 import android.app.SynchronousUserSwitchObserver; 24 import android.content.BroadcastReceiver; 25 import android.content.Context; 26 import android.content.Intent; 27 import android.content.IntentFilter; 28 import android.content.pm.UserInfo; 29 import android.media.AudioManager; 30 import android.os.Handler; 31 import android.os.RemoteException; 32 import android.os.UserHandle; 33 import android.os.UserManager; 34 import android.provider.Settings.Global; 35 import android.telecom.TelecomManager; 36 import android.util.Log; 37 38 import com.android.internal.telephony.IccCardConstants; 39 import com.android.internal.telephony.TelephonyIntents; 40 import com.android.systemui.R; 41 import com.android.systemui.qs.tiles.DndTile; 42 import com.android.systemui.qs.tiles.RotationLockTile; 43 import com.android.systemui.statusbar.policy.BluetoothController; 44 import com.android.systemui.statusbar.policy.BluetoothController.Callback; 45 import com.android.systemui.statusbar.policy.CastController; 46 import com.android.systemui.statusbar.policy.CastController.CastDevice; 47 import com.android.systemui.statusbar.policy.DataSaverController; 48 import com.android.systemui.statusbar.policy.HotspotController; 49 import com.android.systemui.statusbar.policy.RotationLockController; 50 import com.android.systemui.statusbar.policy.UserInfoController; 51 52 /** 53 * This class contains all of the policy about which icons are installed in the status 54 * bar at boot time. It goes through the normal API for icons, even though it probably 55 * strictly doesn't need to. 56 */ 57 public class PhoneStatusBarPolicy implements Callback, RotationLockController.RotationLockControllerCallback, DataSaverController.Listener { 58 private static final String TAG = "PhoneStatusBarPolicy"; 59 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 60 61 private final String mSlotCast; 62 private final String mSlotHotspot; 63 private final String mSlotBluetooth; 64 private final String mSlotTty; 65 private final String mSlotZen; 66 private final String mSlotVolume; 67 private final String mSlotAlarmClock; 68 private final String mSlotManagedProfile; 69 private final String mSlotRotate; 70 private final String mSlotHeadset; 71 private final String mSlotDataSaver; 72 73 private final Context mContext; 74 private final Handler mHandler = new Handler(); 75 private final CastController mCast; 76 private final HotspotController mHotspot; 77 private final AlarmManager mAlarmManager; 78 private final UserInfoController mUserInfoController; 79 private final UserManager mUserManager; 80 private final StatusBarIconController mIconController; 81 private final RotationLockController mRotationLockController; 82 private final DataSaverController mDataSaver; 83 private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; 84 85 // Assume it's all good unless we hear otherwise. We don't always seem 86 // to get broadcasts that it *is* there. 87 IccCardConstants.State mSimState = IccCardConstants.State.READY; 88 89 private boolean mZenVisible; 90 private boolean mVolumeVisible; 91 private boolean mCurrentUserSetup; 92 93 private int mZen; 94 95 private boolean mManagedProfileFocused = false; 96 private boolean mManagedProfileIconVisible = false; 97 private boolean mManagedProfileInQuietMode = false; 98 99 private BluetoothController mBluetooth; 100 101 public PhoneStatusBarPolicy(Context context, StatusBarIconController iconController, 102 CastController cast, HotspotController hotspot, UserInfoController userInfoController, 103 BluetoothController bluetooth, RotationLockController rotationLockController, 104 DataSaverController dataSaver) { 105 mContext = context; 106 mIconController = iconController; 107 mCast = cast; 108 mHotspot = hotspot; 109 mBluetooth = bluetooth; 110 mBluetooth.addStateChangedCallback(this); 111 mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); 112 mUserInfoController = userInfoController; 113 mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); 114 mRotationLockController = rotationLockController; 115 mDataSaver = dataSaver; 116 117 mSlotCast = context.getString(com.android.internal.R.string.status_bar_cast); 118 mSlotHotspot = context.getString(com.android.internal.R.string.status_bar_hotspot); 119 mSlotBluetooth = context.getString(com.android.internal.R.string.status_bar_bluetooth); 120 mSlotTty = context.getString(com.android.internal.R.string.status_bar_tty); 121 mSlotZen = context.getString(com.android.internal.R.string.status_bar_zen); 122 mSlotVolume = context.getString(com.android.internal.R.string.status_bar_volume); 123 mSlotAlarmClock = context.getString(com.android.internal.R.string.status_bar_alarm_clock); 124 mSlotManagedProfile = context.getString( 125 com.android.internal.R.string.status_bar_managed_profile); 126 mSlotRotate = context.getString(com.android.internal.R.string.status_bar_rotate); 127 mSlotHeadset = context.getString(com.android.internal.R.string.status_bar_headset); 128 mSlotDataSaver = context.getString(com.android.internal.R.string.status_bar_data_saver); 129 130 mRotationLockController.addRotationLockControllerCallback(this); 131 132 // listen for broadcasts 133 IntentFilter filter = new IntentFilter(); 134 filter.addAction(AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED); 135 filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION); 136 filter.addAction(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION); 137 filter.addAction(AudioManager.ACTION_HEADSET_PLUG); 138 filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED); 139 filter.addAction(TelecomManager.ACTION_CURRENT_TTY_MODE_CHANGED); 140 filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE); 141 filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE); 142 filter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED); 143 mContext.registerReceiver(mIntentReceiver, filter, null, mHandler); 144 145 // listen for user / profile change. 146 try { 147 ActivityManagerNative.getDefault().registerUserSwitchObserver(mUserSwitchListener, TAG); 148 } catch (RemoteException e) { 149 // Ignore 150 } 151 152 // TTY status 153 mIconController.setIcon(mSlotTty, R.drawable.stat_sys_tty_mode, null); 154 mIconController.setIconVisibility(mSlotTty, false); 155 156 // bluetooth status 157 updateBluetooth(); 158 159 // Alarm clock 160 mIconController.setIcon(mSlotAlarmClock, R.drawable.stat_sys_alarm, null); 161 mIconController.setIconVisibility(mSlotAlarmClock, false); 162 163 // zen 164 mIconController.setIcon(mSlotZen, R.drawable.stat_sys_zen_important, null); 165 mIconController.setIconVisibility(mSlotZen, false); 166 167 // volume 168 mIconController.setIcon(mSlotVolume, R.drawable.stat_sys_ringer_vibrate, null); 169 mIconController.setIconVisibility(mSlotVolume, false); 170 updateVolumeZen(); 171 172 // cast 173 mIconController.setIcon(mSlotCast, R.drawable.stat_sys_cast, null); 174 mIconController.setIconVisibility(mSlotCast, false); 175 mCast.addCallback(mCastCallback); 176 177 // hotspot 178 mIconController.setIcon(mSlotHotspot, R.drawable.stat_sys_hotspot, 179 mContext.getString(R.string.accessibility_status_bar_hotspot)); 180 mIconController.setIconVisibility(mSlotHotspot, mHotspot.isHotspotEnabled()); 181 mHotspot.addCallback(mHotspotCallback); 182 183 // managed profile 184 mIconController.setIcon(mSlotManagedProfile, R.drawable.stat_sys_managed_profile_status, 185 mContext.getString(R.string.accessibility_managed_profile)); 186 mIconController.setIconVisibility(mSlotManagedProfile, mManagedProfileIconVisible); 187 188 // data saver 189 mIconController.setIcon(mSlotDataSaver, R.drawable.stat_sys_data_saver, 190 context.getString(R.string.accessibility_data_saver_on)); 191 mIconController.setIconVisibility(mSlotDataSaver, false); 192 mDataSaver.addListener(this); 193 } 194 195 public void setStatusBarKeyguardViewManager( 196 StatusBarKeyguardViewManager statusBarKeyguardViewManager) { 197 mStatusBarKeyguardViewManager = statusBarKeyguardViewManager; 198 } 199 200 public void setZenMode(int zen) { 201 mZen = zen; 202 updateVolumeZen(); 203 } 204 205 private void updateAlarm() { 206 final AlarmClockInfo alarm = mAlarmManager.getNextAlarmClock(UserHandle.USER_CURRENT); 207 final boolean hasAlarm = alarm != null && alarm.getTriggerTime() > 0; 208 final boolean zenNone = mZen == Global.ZEN_MODE_NO_INTERRUPTIONS; 209 mIconController.setIcon(mSlotAlarmClock, zenNone ? R.drawable.stat_sys_alarm_dim 210 : R.drawable.stat_sys_alarm, null); 211 mIconController.setIconVisibility(mSlotAlarmClock, mCurrentUserSetup && hasAlarm); 212 } 213 214 private final void updateSimState(Intent intent) { 215 String stateExtra = intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE); 216 if (IccCardConstants.INTENT_VALUE_ICC_ABSENT.equals(stateExtra)) { 217 mSimState = IccCardConstants.State.ABSENT; 218 } else if (IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR.equals(stateExtra)) { 219 mSimState = IccCardConstants.State.CARD_IO_ERROR; 220 } else if (IccCardConstants.INTENT_VALUE_ICC_CARD_RESTRICTED.equals(stateExtra)) { 221 mSimState = IccCardConstants.State.CARD_RESTRICTED; 222 } else if (IccCardConstants.INTENT_VALUE_ICC_READY.equals(stateExtra)) { 223 mSimState = IccCardConstants.State.READY; 224 } else if (IccCardConstants.INTENT_VALUE_ICC_LOCKED.equals(stateExtra)) { 225 final String lockedReason = 226 intent.getStringExtra(IccCardConstants.INTENT_KEY_LOCKED_REASON); 227 if (IccCardConstants.INTENT_VALUE_LOCKED_ON_PIN.equals(lockedReason)) { 228 mSimState = IccCardConstants.State.PIN_REQUIRED; 229 } else if (IccCardConstants.INTENT_VALUE_LOCKED_ON_PUK.equals(lockedReason)) { 230 mSimState = IccCardConstants.State.PUK_REQUIRED; 231 } else { 232 mSimState = IccCardConstants.State.NETWORK_LOCKED; 233 } 234 } else { 235 mSimState = IccCardConstants.State.UNKNOWN; 236 } 237 } 238 239 private final void updateVolumeZen() { 240 AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); 241 242 boolean zenVisible = false; 243 int zenIconId = 0; 244 String zenDescription = null; 245 246 boolean volumeVisible = false; 247 int volumeIconId = 0; 248 String volumeDescription = null; 249 250 if (DndTile.isVisible(mContext) || DndTile.isCombinedIcon(mContext)) { 251 zenVisible = mZen != Global.ZEN_MODE_OFF; 252 zenIconId = mZen == Global.ZEN_MODE_NO_INTERRUPTIONS 253 ? R.drawable.stat_sys_dnd_total_silence : R.drawable.stat_sys_dnd; 254 zenDescription = mContext.getString(R.string.quick_settings_dnd_label); 255 } else if (mZen == Global.ZEN_MODE_NO_INTERRUPTIONS) { 256 zenVisible = true; 257 zenIconId = R.drawable.stat_sys_zen_none; 258 zenDescription = mContext.getString(R.string.interruption_level_none); 259 } else if (mZen == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS) { 260 zenVisible = true; 261 zenIconId = R.drawable.stat_sys_zen_important; 262 zenDescription = mContext.getString(R.string.interruption_level_priority); 263 } 264 265 if (DndTile.isVisible(mContext) && !DndTile.isCombinedIcon(mContext) 266 && audioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_SILENT) { 267 volumeVisible = true; 268 volumeIconId = R.drawable.stat_sys_ringer_silent; 269 volumeDescription = mContext.getString(R.string.accessibility_ringer_silent); 270 } else if (mZen != Global.ZEN_MODE_NO_INTERRUPTIONS && mZen != Global.ZEN_MODE_ALARMS && 271 audioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_VIBRATE) { 272 volumeVisible = true; 273 volumeIconId = R.drawable.stat_sys_ringer_vibrate; 274 volumeDescription = mContext.getString(R.string.accessibility_ringer_vibrate); 275 } 276 277 if (zenVisible) { 278 mIconController.setIcon(mSlotZen, zenIconId, zenDescription); 279 } 280 if (zenVisible != mZenVisible) { 281 mIconController.setIconVisibility(mSlotZen, zenVisible); 282 mZenVisible = zenVisible; 283 } 284 285 if (volumeVisible) { 286 mIconController.setIcon(mSlotVolume, volumeIconId, volumeDescription); 287 } 288 if (volumeVisible != mVolumeVisible) { 289 mIconController.setIconVisibility(mSlotVolume, volumeVisible); 290 mVolumeVisible = volumeVisible; 291 } 292 updateAlarm(); 293 } 294 295 @Override 296 public void onBluetoothDevicesChanged() { 297 updateBluetooth(); 298 } 299 300 @Override 301 public void onBluetoothStateChange(boolean enabled) { 302 updateBluetooth(); 303 } 304 305 private final void updateBluetooth() { 306 int iconId = R.drawable.stat_sys_data_bluetooth; 307 String contentDescription = 308 mContext.getString(R.string.accessibility_quick_settings_bluetooth_on); 309 boolean bluetoothEnabled = false; 310 if (mBluetooth != null) { 311 bluetoothEnabled = mBluetooth.isBluetoothEnabled(); 312 if (mBluetooth.isBluetoothConnected()) { 313 iconId = R.drawable.stat_sys_data_bluetooth_connected; 314 contentDescription = mContext.getString(R.string.accessibility_bluetooth_connected); 315 } 316 } 317 318 mIconController.setIcon(mSlotBluetooth, iconId, contentDescription); 319 mIconController.setIconVisibility(mSlotBluetooth, bluetoothEnabled); 320 } 321 322 private final void updateTTY(Intent intent) { 323 int currentTtyMode = intent.getIntExtra(TelecomManager.EXTRA_CURRENT_TTY_MODE, 324 TelecomManager.TTY_MODE_OFF); 325 boolean enabled = currentTtyMode != TelecomManager.TTY_MODE_OFF; 326 327 if (DEBUG) Log.v(TAG, "updateTTY: enabled: " + enabled); 328 329 if (enabled) { 330 // TTY is on 331 if (DEBUG) Log.v(TAG, "updateTTY: set TTY on"); 332 mIconController.setIcon(mSlotTty, R.drawable.stat_sys_tty_mode, 333 mContext.getString(R.string.accessibility_tty_enabled)); 334 mIconController.setIconVisibility(mSlotTty, true); 335 } else { 336 // TTY is off 337 if (DEBUG) Log.v(TAG, "updateTTY: set TTY off"); 338 mIconController.setIconVisibility(mSlotTty, false); 339 } 340 } 341 342 private void updateCast() { 343 boolean isCasting = false; 344 for (CastDevice device : mCast.getCastDevices()) { 345 if (device.state == CastDevice.STATE_CONNECTING 346 || device.state == CastDevice.STATE_CONNECTED) { 347 isCasting = true; 348 break; 349 } 350 } 351 if (DEBUG) Log.v(TAG, "updateCast: isCasting: " + isCasting); 352 mHandler.removeCallbacks(mRemoveCastIconRunnable); 353 if (isCasting) { 354 mIconController.setIcon(mSlotCast, R.drawable.stat_sys_cast, 355 mContext.getString(R.string.accessibility_casting)); 356 mIconController.setIconVisibility(mSlotCast, true); 357 } else { 358 // don't turn off the screen-record icon for a few seconds, just to make sure the user 359 // has seen it 360 if (DEBUG) Log.v(TAG, "updateCast: hiding icon in 3 sec..."); 361 mHandler.postDelayed(mRemoveCastIconRunnable, 3000); 362 } 363 } 364 365 private void updateQuietState() { 366 mManagedProfileInQuietMode = false; 367 int currentUserId = ActivityManager.getCurrentUser(); 368 for (UserInfo ui : mUserManager.getEnabledProfiles(currentUserId)) { 369 if (ui.isManagedProfile() && ui.isQuietModeEnabled()) { 370 mManagedProfileInQuietMode = true; 371 return; 372 } 373 } 374 } 375 376 private void profileChanged(int userId) { 377 UserInfo user = null; 378 if (userId == UserHandle.USER_CURRENT) { 379 try { 380 user = ActivityManagerNative.getDefault().getCurrentUser(); 381 } catch (RemoteException e) { 382 // Ignore 383 } 384 } else { 385 user = mUserManager.getUserInfo(userId); 386 } 387 388 mManagedProfileFocused = user != null && user.isManagedProfile(); 389 if (DEBUG) Log.v(TAG, "profileChanged: mManagedProfileFocused: " + mManagedProfileFocused); 390 // Actually update the icon later when transition starts. 391 } 392 393 private void updateManagedProfile() { 394 if (DEBUG) Log.v(TAG, "updateManagedProfile: mManagedProfileFocused: " 395 + mManagedProfileFocused); 396 final boolean showIcon; 397 if (mManagedProfileFocused && !mStatusBarKeyguardViewManager.isShowing()) { 398 showIcon = true; 399 mIconController.setIcon(mSlotManagedProfile, 400 R.drawable.stat_sys_managed_profile_status, 401 mContext.getString(R.string.accessibility_managed_profile)); 402 } else if (mManagedProfileInQuietMode) { 403 showIcon = true; 404 mIconController.setIcon(mSlotManagedProfile, 405 R.drawable.stat_sys_managed_profile_status_off, 406 mContext.getString(R.string.accessibility_managed_profile)); 407 } else { 408 showIcon = false; 409 } 410 if (mManagedProfileIconVisible != showIcon) { 411 mIconController.setIconVisibility(mSlotManagedProfile, showIcon); 412 mManagedProfileIconVisible = showIcon; 413 } 414 } 415 416 private final SynchronousUserSwitchObserver mUserSwitchListener = 417 new SynchronousUserSwitchObserver() { 418 @Override 419 public void onUserSwitching(int newUserId) throws RemoteException { 420 mHandler.post(new Runnable() { 421 @Override 422 public void run() { 423 mUserInfoController.reloadUserInfo(); 424 } 425 }); 426 } 427 428 @Override 429 public void onUserSwitchComplete(int newUserId) throws RemoteException { 430 mHandler.post(new Runnable() { 431 @Override 432 public void run() { 433 updateAlarm(); 434 profileChanged(newUserId); 435 updateQuietState(); 436 updateManagedProfile(); 437 } 438 }); 439 } 440 441 @Override 442 public void onForegroundProfileSwitch(int newProfileId) { 443 mHandler.post(new Runnable() { 444 @Override 445 public void run() { 446 profileChanged(newProfileId); 447 } 448 }); 449 } 450 }; 451 452 private final HotspotController.Callback mHotspotCallback = new HotspotController.Callback() { 453 @Override 454 public void onHotspotChanged(boolean enabled) { 455 mIconController.setIconVisibility(mSlotHotspot, enabled); 456 } 457 }; 458 459 private final CastController.Callback mCastCallback = new CastController.Callback() { 460 @Override 461 public void onCastDevicesChanged() { 462 updateCast(); 463 } 464 }; 465 466 public void appTransitionStarting(long startTime, long duration) { 467 updateManagedProfile(); 468 } 469 470 public void notifyKeyguardShowingChanged() { 471 updateManagedProfile(); 472 } 473 474 public void setCurrentUserSetup(boolean userSetup) { 475 if (mCurrentUserSetup == userSetup) return; 476 mCurrentUserSetup = userSetup; 477 updateAlarm(); 478 updateQuietState(); 479 } 480 481 @Override 482 public void onRotationLockStateChanged(boolean rotationLocked, boolean affordanceVisible) { 483 boolean portrait = RotationLockTile.isCurrentOrientationLockPortrait( 484 mRotationLockController, mContext); 485 if (rotationLocked) { 486 if (portrait) { 487 mIconController.setIcon(mSlotRotate, R.drawable.stat_sys_rotate_portrait, 488 mContext.getString(R.string.accessibility_rotation_lock_on_portrait)); 489 } else { 490 mIconController.setIcon(mSlotRotate, R.drawable.stat_sys_rotate_landscape, 491 mContext.getString(R.string.accessibility_rotation_lock_on_landscape)); 492 } 493 mIconController.setIconVisibility(mSlotRotate, true); 494 } else { 495 mIconController.setIconVisibility(mSlotRotate, false); 496 } 497 } 498 499 private void updateHeadsetPlug(Intent intent) { 500 boolean connected = intent.getIntExtra("state", 0) != 0; 501 boolean hasMic = intent.getIntExtra("microphone", 0) != 0; 502 if (connected) { 503 String contentDescription = mContext.getString(hasMic 504 ? R.string.accessibility_status_bar_headset 505 : R.string.accessibility_status_bar_headphones); 506 mIconController.setIcon(mSlotHeadset, hasMic ? R.drawable.ic_headset_mic 507 : R.drawable.ic_headset, contentDescription); 508 mIconController.setIconVisibility(mSlotHeadset, true); 509 } else { 510 mIconController.setIconVisibility(mSlotHeadset, false); 511 } 512 } 513 514 @Override 515 public void onDataSaverChanged(boolean isDataSaving) { 516 mIconController.setIconVisibility(mSlotDataSaver, isDataSaving); 517 } 518 519 private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { 520 @Override 521 public void onReceive(Context context, Intent intent) { 522 String action = intent.getAction(); 523 if (action.equals(AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED)) { 524 updateAlarm(); 525 } else if (action.equals(AudioManager.RINGER_MODE_CHANGED_ACTION) || 526 action.equals(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION)) { 527 updateVolumeZen(); 528 } else if (action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) { 529 updateSimState(intent); 530 } else if (action.equals(TelecomManager.ACTION_CURRENT_TTY_MODE_CHANGED)) { 531 updateTTY(intent); 532 } else if (action.equals(Intent.ACTION_MANAGED_PROFILE_AVAILABLE) || 533 action.equals(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE) || 534 action.equals(Intent.ACTION_MANAGED_PROFILE_REMOVED)) { 535 updateQuietState(); 536 updateManagedProfile(); 537 } else if (action.equals(AudioManager.ACTION_HEADSET_PLUG)) { 538 updateHeadsetPlug(intent); 539 } 540 } 541 }; 542 543 private Runnable mRemoveCastIconRunnable = new Runnable() { 544 @Override 545 public void run() { 546 if (DEBUG) Log.v(TAG, "updateCast: hiding icon NOW"); 547 mIconController.setIconVisibility(mSlotCast, false); 548 } 549 }; 550 } 551