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.ActivityManagerNative; 20 import android.app.AlarmManager; 21 import android.app.AlarmManager.AlarmClockInfo; 22 import android.app.IUserSwitchObserver; 23 import android.app.StatusBarManager; 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.IRemoteCallback; 32 import android.os.RemoteException; 33 import android.os.UserHandle; 34 import android.os.UserManager; 35 import android.provider.Settings.Global; 36 import android.telecom.TelecomManager; 37 import android.util.Log; 38 39 import com.android.internal.telephony.IccCardConstants; 40 import com.android.internal.telephony.TelephonyIntents; 41 import com.android.systemui.R; 42 import com.android.systemui.qs.tiles.DndTile; 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.HotspotController; 48 import com.android.systemui.statusbar.policy.UserInfoController; 49 50 /** 51 * This class contains all of the policy about which icons are installed in the status 52 * bar at boot time. It goes through the normal API for icons, even though it probably 53 * strictly doesn't need to. 54 */ 55 public class PhoneStatusBarPolicy implements Callback { 56 private static final String TAG = "PhoneStatusBarPolicy"; 57 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 58 59 private static final String SLOT_CAST = "cast"; 60 private static final String SLOT_HOTSPOT = "hotspot"; 61 private static final String SLOT_BLUETOOTH = "bluetooth"; 62 private static final String SLOT_TTY = "tty"; 63 private static final String SLOT_ZEN = "zen"; 64 private static final String SLOT_VOLUME = "volume"; 65 private static final String SLOT_ALARM_CLOCK = "alarm_clock"; 66 private static final String SLOT_MANAGED_PROFILE = "managed_profile"; 67 68 private final Context mContext; 69 private final StatusBarManager mService; 70 private final Handler mHandler = new Handler(); 71 private final CastController mCast; 72 private final HotspotController mHotspot; 73 private final AlarmManager mAlarmManager; 74 private final UserInfoController mUserInfoController; 75 76 // Assume it's all good unless we hear otherwise. We don't always seem 77 // to get broadcasts that it *is* there. 78 IccCardConstants.State mSimState = IccCardConstants.State.READY; 79 80 private boolean mZenVisible; 81 private boolean mVolumeVisible; 82 private boolean mCurrentUserSetup; 83 84 private int mZen; 85 86 private boolean mManagedProfileFocused = false; 87 private boolean mManagedProfileIconVisible = true; 88 89 private boolean mKeyguardVisible = true; 90 private BluetoothController mBluetooth; 91 92 private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { 93 @Override 94 public void onReceive(Context context, Intent intent) { 95 String action = intent.getAction(); 96 if (action.equals(AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED)) { 97 updateAlarm(); 98 } 99 else if (action.equals(AudioManager.RINGER_MODE_CHANGED_ACTION) || 100 action.equals(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION)) { 101 updateVolumeZen(); 102 } 103 else if (action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) { 104 updateSimState(intent); 105 } 106 else if (action.equals(TelecomManager.ACTION_CURRENT_TTY_MODE_CHANGED)) { 107 updateTTY(intent); 108 } 109 } 110 }; 111 112 private Runnable mRemoveCastIconRunnable = new Runnable() { 113 @Override 114 public void run() { 115 if (DEBUG) Log.v(TAG, "updateCast: hiding icon NOW"); 116 mService.setIconVisibility(SLOT_CAST, false); 117 } 118 }; 119 PhoneStatusBarPolicy(Context context, CastController cast, HotspotController hotspot, UserInfoController userInfoController, BluetoothController bluetooth)120 public PhoneStatusBarPolicy(Context context, CastController cast, HotspotController hotspot, 121 UserInfoController userInfoController, BluetoothController bluetooth) { 122 mContext = context; 123 mCast = cast; 124 mHotspot = hotspot; 125 mBluetooth = bluetooth; 126 mBluetooth.addStateChangedCallback(this); 127 mService = (StatusBarManager) context.getSystemService(Context.STATUS_BAR_SERVICE); 128 mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); 129 mUserInfoController = userInfoController; 130 131 // listen for broadcasts 132 IntentFilter filter = new IntentFilter(); 133 filter.addAction(AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED); 134 filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION); 135 filter.addAction(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION); 136 filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED); 137 filter.addAction(TelecomManager.ACTION_CURRENT_TTY_MODE_CHANGED); 138 mContext.registerReceiver(mIntentReceiver, filter, null, mHandler); 139 140 // listen for user / profile change. 141 try { 142 ActivityManagerNative.getDefault().registerUserSwitchObserver(mUserSwitchListener); 143 } catch (RemoteException e) { 144 // Ignore 145 } 146 147 // TTY status 148 mService.setIcon(SLOT_TTY, R.drawable.stat_sys_tty_mode, 0, null); 149 mService.setIconVisibility(SLOT_TTY, false); 150 151 // bluetooth status 152 updateBluetooth(); 153 154 // Alarm clock 155 mService.setIcon(SLOT_ALARM_CLOCK, R.drawable.stat_sys_alarm, 0, null); 156 mService.setIconVisibility(SLOT_ALARM_CLOCK, false); 157 158 // zen 159 mService.setIcon(SLOT_ZEN, R.drawable.stat_sys_zen_important, 0, null); 160 mService.setIconVisibility(SLOT_ZEN, false); 161 162 // volume 163 mService.setIcon(SLOT_VOLUME, R.drawable.stat_sys_ringer_vibrate, 0, null); 164 mService.setIconVisibility(SLOT_VOLUME, false); 165 updateVolumeZen(); 166 167 // cast 168 mService.setIcon(SLOT_CAST, R.drawable.stat_sys_cast, 0, null); 169 mService.setIconVisibility(SLOT_CAST, false); 170 mCast.addCallback(mCastCallback); 171 172 // hotspot 173 mService.setIcon(SLOT_HOTSPOT, R.drawable.stat_sys_hotspot, 0, 174 mContext.getString(R.string.accessibility_status_bar_hotspot)); 175 mService.setIconVisibility(SLOT_HOTSPOT, mHotspot.isHotspotEnabled()); 176 mHotspot.addCallback(mHotspotCallback); 177 178 // managed profile 179 mService.setIcon(SLOT_MANAGED_PROFILE, R.drawable.stat_sys_managed_profile_status, 0, 180 mContext.getString(R.string.accessibility_managed_profile)); 181 mService.setIconVisibility(SLOT_MANAGED_PROFILE, false); 182 } 183 setZenMode(int zen)184 public void setZenMode(int zen) { 185 mZen = zen; 186 updateVolumeZen(); 187 } 188 updateAlarm()189 private void updateAlarm() { 190 final AlarmClockInfo alarm = mAlarmManager.getNextAlarmClock(UserHandle.USER_CURRENT); 191 final boolean hasAlarm = alarm != null && alarm.getTriggerTime() > 0; 192 final boolean zenNone = mZen == Global.ZEN_MODE_NO_INTERRUPTIONS; 193 mService.setIcon(SLOT_ALARM_CLOCK, zenNone ? R.drawable.stat_sys_alarm_dim 194 : R.drawable.stat_sys_alarm, 0, null); 195 mService.setIconVisibility(SLOT_ALARM_CLOCK, mCurrentUserSetup && hasAlarm); 196 } 197 updateSimState(Intent intent)198 private final void updateSimState(Intent intent) { 199 String stateExtra = intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE); 200 if (IccCardConstants.INTENT_VALUE_ICC_ABSENT.equals(stateExtra)) { 201 mSimState = IccCardConstants.State.ABSENT; 202 } 203 else if (IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR.equals(stateExtra)) { 204 mSimState = IccCardConstants.State.CARD_IO_ERROR; 205 } 206 else if (IccCardConstants.INTENT_VALUE_ICC_READY.equals(stateExtra)) { 207 mSimState = IccCardConstants.State.READY; 208 } 209 else if (IccCardConstants.INTENT_VALUE_ICC_LOCKED.equals(stateExtra)) { 210 final String lockedReason = 211 intent.getStringExtra(IccCardConstants.INTENT_KEY_LOCKED_REASON); 212 if (IccCardConstants.INTENT_VALUE_LOCKED_ON_PIN.equals(lockedReason)) { 213 mSimState = IccCardConstants.State.PIN_REQUIRED; 214 } 215 else if (IccCardConstants.INTENT_VALUE_LOCKED_ON_PUK.equals(lockedReason)) { 216 mSimState = IccCardConstants.State.PUK_REQUIRED; 217 } 218 else { 219 mSimState = IccCardConstants.State.NETWORK_LOCKED; 220 } 221 } else { 222 mSimState = IccCardConstants.State.UNKNOWN; 223 } 224 } 225 updateVolumeZen()226 private final void updateVolumeZen() { 227 AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); 228 229 boolean zenVisible = false; 230 int zenIconId = 0; 231 String zenDescription = null; 232 233 boolean volumeVisible = false; 234 int volumeIconId = 0; 235 String volumeDescription = null; 236 237 if (DndTile.isVisible(mContext) || DndTile.isCombinedIcon(mContext)) { 238 zenVisible = mZen != Global.ZEN_MODE_OFF; 239 zenIconId = mZen == Global.ZEN_MODE_NO_INTERRUPTIONS 240 ? R.drawable.stat_sys_dnd_total_silence : R.drawable.stat_sys_dnd; 241 zenDescription = mContext.getString(R.string.quick_settings_dnd_label); 242 } else if (mZen == Global.ZEN_MODE_NO_INTERRUPTIONS) { 243 zenVisible = true; 244 zenIconId = R.drawable.stat_sys_zen_none; 245 zenDescription = mContext.getString(R.string.interruption_level_none); 246 } else if (mZen == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS) { 247 zenVisible = true; 248 zenIconId = R.drawable.stat_sys_zen_important; 249 zenDescription = mContext.getString(R.string.interruption_level_priority); 250 } 251 252 if (DndTile.isVisible(mContext) && !DndTile.isCombinedIcon(mContext) 253 && audioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_SILENT) { 254 volumeVisible = true; 255 volumeIconId = R.drawable.stat_sys_ringer_silent; 256 volumeDescription = mContext.getString(R.string.accessibility_ringer_silent); 257 } else if (mZen != Global.ZEN_MODE_NO_INTERRUPTIONS && mZen != Global.ZEN_MODE_ALARMS && 258 audioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_VIBRATE) { 259 volumeVisible = true; 260 volumeIconId = R.drawable.stat_sys_ringer_vibrate; 261 volumeDescription = mContext.getString(R.string.accessibility_ringer_vibrate); 262 } 263 264 if (zenVisible) { 265 mService.setIcon(SLOT_ZEN, zenIconId, 0, zenDescription); 266 } 267 if (zenVisible != mZenVisible) { 268 mService.setIconVisibility(SLOT_ZEN, zenVisible); 269 mZenVisible = zenVisible; 270 } 271 272 if (volumeVisible) { 273 mService.setIcon(SLOT_VOLUME, volumeIconId, 0, volumeDescription); 274 } 275 if (volumeVisible != mVolumeVisible) { 276 mService.setIconVisibility(SLOT_VOLUME, volumeVisible); 277 mVolumeVisible = volumeVisible; 278 } 279 updateAlarm(); 280 } 281 282 @Override onBluetoothDevicesChanged()283 public void onBluetoothDevicesChanged() { 284 updateBluetooth(); 285 } 286 287 @Override onBluetoothStateChange(boolean enabled)288 public void onBluetoothStateChange(boolean enabled) { 289 updateBluetooth(); 290 } 291 updateBluetooth()292 private final void updateBluetooth() { 293 int iconId = R.drawable.stat_sys_data_bluetooth; 294 String contentDescription = 295 mContext.getString(R.string.accessibility_quick_settings_bluetooth_on); 296 boolean bluetoothEnabled = false; 297 if (mBluetooth != null) { 298 bluetoothEnabled = mBluetooth.isBluetoothEnabled(); 299 if (mBluetooth.isBluetoothConnected()) { 300 iconId = R.drawable.stat_sys_data_bluetooth_connected; 301 contentDescription = mContext.getString(R.string.accessibility_bluetooth_connected); 302 } 303 } 304 305 mService.setIcon(SLOT_BLUETOOTH, iconId, 0, contentDescription); 306 mService.setIconVisibility(SLOT_BLUETOOTH, bluetoothEnabled); 307 } 308 updateTTY(Intent intent)309 private final void updateTTY(Intent intent) { 310 int currentTtyMode = intent.getIntExtra(TelecomManager.EXTRA_CURRENT_TTY_MODE, 311 TelecomManager.TTY_MODE_OFF); 312 boolean enabled = currentTtyMode != TelecomManager.TTY_MODE_OFF; 313 314 if (DEBUG) Log.v(TAG, "updateTTY: enabled: " + enabled); 315 316 if (enabled) { 317 // TTY is on 318 if (DEBUG) Log.v(TAG, "updateTTY: set TTY on"); 319 mService.setIcon(SLOT_TTY, R.drawable.stat_sys_tty_mode, 0, 320 mContext.getString(R.string.accessibility_tty_enabled)); 321 mService.setIconVisibility(SLOT_TTY, true); 322 } else { 323 // TTY is off 324 if (DEBUG) Log.v(TAG, "updateTTY: set TTY off"); 325 mService.setIconVisibility(SLOT_TTY, false); 326 } 327 } 328 updateCast()329 private void updateCast() { 330 boolean isCasting = false; 331 for (CastDevice device : mCast.getCastDevices()) { 332 if (device.state == CastDevice.STATE_CONNECTING 333 || device.state == CastDevice.STATE_CONNECTED) { 334 isCasting = true; 335 break; 336 } 337 } 338 if (DEBUG) Log.v(TAG, "updateCast: isCasting: " + isCasting); 339 mHandler.removeCallbacks(mRemoveCastIconRunnable); 340 if (isCasting) { 341 mService.setIcon(SLOT_CAST, R.drawable.stat_sys_cast, 0, 342 mContext.getString(R.string.accessibility_casting)); 343 mService.setIconVisibility(SLOT_CAST, true); 344 } else { 345 // don't turn off the screen-record icon for a few seconds, just to make sure the user 346 // has seen it 347 if (DEBUG) Log.v(TAG, "updateCast: hiding icon in 3 sec..."); 348 mHandler.postDelayed(mRemoveCastIconRunnable, 3000); 349 } 350 } 351 profileChanged(int userId)352 private void profileChanged(int userId) { 353 UserManager userManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); 354 UserInfo user = null; 355 if (userId == UserHandle.USER_CURRENT) { 356 try { 357 user = ActivityManagerNative.getDefault().getCurrentUser(); 358 } catch (RemoteException e) { 359 // Ignore 360 } 361 } else { 362 user = userManager.getUserInfo(userId); 363 } 364 365 mManagedProfileFocused = user != null && user.isManagedProfile(); 366 if (DEBUG) Log.v(TAG, "profileChanged: mManagedProfileFocused: " + mManagedProfileFocused); 367 // Actually update the icon later when transition starts. 368 } 369 updateManagedProfile()370 private void updateManagedProfile() { 371 if (DEBUG) Log.v(TAG, "updateManagedProfile: mManagedProfileFocused: " 372 + mManagedProfileFocused 373 + " mKeyguardVisible: " + mKeyguardVisible); 374 boolean showIcon = mManagedProfileFocused && !mKeyguardVisible; 375 if (mManagedProfileIconVisible != showIcon) { 376 mService.setIconVisibility(SLOT_MANAGED_PROFILE, showIcon); 377 mManagedProfileIconVisible = showIcon; 378 } 379 } 380 381 private final IUserSwitchObserver.Stub mUserSwitchListener = 382 new IUserSwitchObserver.Stub() { 383 @Override 384 public void onUserSwitching(int newUserId, IRemoteCallback reply) { 385 mUserInfoController.reloadUserInfo(); 386 if (reply != null) { 387 try { 388 reply.sendResult(null); 389 } catch (RemoteException e) { 390 } 391 } 392 } 393 394 @Override 395 public void onUserSwitchComplete(int newUserId) throws RemoteException { 396 updateAlarm(); 397 profileChanged(newUserId); 398 } 399 400 @Override 401 public void onForegroundProfileSwitch(int newProfileId) { 402 profileChanged(newProfileId); 403 } 404 }; 405 406 private final HotspotController.Callback mHotspotCallback = new HotspotController.Callback() { 407 @Override 408 public void onHotspotChanged(boolean enabled) { 409 mService.setIconVisibility(SLOT_HOTSPOT, enabled); 410 } 411 }; 412 413 private final CastController.Callback mCastCallback = new CastController.Callback() { 414 @Override 415 public void onCastDevicesChanged() { 416 updateCast(); 417 } 418 }; 419 appTransitionStarting(long startTime, long duration)420 public void appTransitionStarting(long startTime, long duration) { 421 updateManagedProfile(); 422 } 423 setKeyguardShowing(boolean visible)424 public void setKeyguardShowing(boolean visible) { 425 mKeyguardVisible = visible; 426 updateManagedProfile(); 427 } 428 setCurrentUserSetup(boolean userSetup)429 public void setCurrentUserSetup(boolean userSetup) { 430 if (mCurrentUserSetup == userSetup) return; 431 mCurrentUserSetup = userSetup; 432 updateAlarm(); 433 } 434 } 435