• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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