Home | History | Annotate | Download | only in phone
      1 /*
      2  * Copyright (C) 2012 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.bluetooth.BluetoothAdapter;
     20 import android.bluetooth.BluetoothAdapter.BluetoothStateChangeCallback;
     21 import android.content.BroadcastReceiver;
     22 import android.content.ContentResolver;
     23 import android.content.Context;
     24 import android.content.Intent;
     25 import android.content.IntentFilter;
     26 import android.content.pm.PackageManager;
     27 import android.content.res.Resources;
     28 import android.database.ContentObserver;
     29 import android.graphics.drawable.Drawable;
     30 import android.media.MediaRouter;
     31 import android.media.MediaRouter.RouteInfo;
     32 import android.net.ConnectivityManager;
     33 import android.os.Handler;
     34 import android.os.UserHandle;
     35 import android.provider.Settings;
     36 import android.provider.Settings.SettingNotFoundException;
     37 import android.text.TextUtils;
     38 import android.view.LayoutInflater;
     39 import android.view.View;
     40 import android.view.inputmethod.InputMethodInfo;
     41 import android.view.inputmethod.InputMethodManager;
     42 import android.view.inputmethod.InputMethodSubtype;
     43 
     44 import com.android.systemui.R;
     45 import com.android.systemui.settings.BrightnessController.BrightnessStateChangeCallback;
     46 import com.android.systemui.settings.CurrentUserTracker;
     47 import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
     48 import com.android.systemui.statusbar.policy.LocationController.LocationSettingsChangeCallback;
     49 import com.android.systemui.statusbar.policy.NetworkController.NetworkSignalChangedCallback;
     50 import com.android.systemui.statusbar.policy.RotationLockController;
     51 import com.android.systemui.statusbar.policy.RotationLockController.RotationLockControllerCallback;
     52 
     53 import java.util.List;
     54 
     55 class QuickSettingsModel implements BluetoothStateChangeCallback,
     56         NetworkSignalChangedCallback,
     57         BatteryStateChangeCallback,
     58         BrightnessStateChangeCallback,
     59         RotationLockControllerCallback,
     60         LocationSettingsChangeCallback {
     61     // Sett InputMethoManagerService
     62     private static final String TAG_TRY_SUPPRESSING_IME_SWITCHER = "TrySuppressingImeSwitcher";
     63 
     64     /** Represents the state of a given attribute. */
     65     static class State {
     66         int iconId;
     67         String label;
     68         boolean enabled = false;
     69     }
     70     static class BatteryState extends State {
     71         int batteryLevel;
     72         boolean pluggedIn;
     73     }
     74     static class ActivityState extends State {
     75         boolean activityIn;
     76         boolean activityOut;
     77     }
     78     static class RSSIState extends ActivityState {
     79         int signalIconId;
     80         String signalContentDescription;
     81         int dataTypeIconId;
     82         String dataContentDescription;
     83     }
     84     static class WifiState extends ActivityState {
     85         String signalContentDescription;
     86         boolean connected;
     87     }
     88     static class UserState extends State {
     89         Drawable avatar;
     90     }
     91     static class BrightnessState extends State {
     92         boolean autoBrightness;
     93     }
     94     public static class BluetoothState extends State {
     95         boolean connected = false;
     96         String stateContentDescription;
     97     }
     98     public static class RotationLockState extends State {
     99         boolean visible = false;
    100     }
    101 
    102     /** The callback to update a given tile. */
    103     interface RefreshCallback {
    104         public void refreshView(QuickSettingsTileView view, State state);
    105     }
    106 
    107     public static class BasicRefreshCallback implements RefreshCallback {
    108         private final QuickSettingsBasicTile mView;
    109         private boolean mShowWhenEnabled;
    110 
    111         public BasicRefreshCallback(QuickSettingsBasicTile v) {
    112             mView = v;
    113         }
    114         public void refreshView(QuickSettingsTileView ignored, State state) {
    115             if (mShowWhenEnabled) {
    116                 mView.setVisibility(state.enabled ? View.VISIBLE : View.GONE);
    117             }
    118             if (state.iconId != 0) {
    119                 mView.setImageDrawable(null); // needed to flush any cached IDs
    120                 mView.setImageResource(state.iconId);
    121             }
    122             if (state.label != null) {
    123                 mView.setText(state.label);
    124             }
    125         }
    126         public BasicRefreshCallback setShowWhenEnabled(boolean swe) {
    127             mShowWhenEnabled = swe;
    128             return this;
    129         }
    130     }
    131 
    132     /** Broadcast receive to determine if there is an alarm set. */
    133     private BroadcastReceiver mAlarmIntentReceiver = new BroadcastReceiver() {
    134         @Override
    135         public void onReceive(Context context, Intent intent) {
    136             String action = intent.getAction();
    137             if (action.equals(Intent.ACTION_ALARM_CHANGED)) {
    138                 onAlarmChanged(intent);
    139                 onNextAlarmChanged();
    140             }
    141         }
    142     };
    143 
    144     /** ContentObserver to determine the next alarm */
    145     private class NextAlarmObserver extends ContentObserver {
    146         public NextAlarmObserver(Handler handler) {
    147             super(handler);
    148         }
    149 
    150         @Override public void onChange(boolean selfChange) {
    151             onNextAlarmChanged();
    152         }
    153 
    154         public void startObserving() {
    155             final ContentResolver cr = mContext.getContentResolver();
    156             cr.registerContentObserver(
    157                     Settings.System.getUriFor(Settings.System.NEXT_ALARM_FORMATTED), false, this,
    158                     UserHandle.USER_ALL);
    159         }
    160     }
    161 
    162     /** ContentObserver to watch adb */
    163     private class BugreportObserver extends ContentObserver {
    164         public BugreportObserver(Handler handler) {
    165             super(handler);
    166         }
    167 
    168         @Override public void onChange(boolean selfChange) {
    169             onBugreportChanged();
    170         }
    171 
    172         public void startObserving() {
    173             final ContentResolver cr = mContext.getContentResolver();
    174             cr.registerContentObserver(
    175                     Settings.Global.getUriFor(Settings.Global.BUGREPORT_IN_POWER_MENU), false, this);
    176         }
    177     }
    178 
    179     /** ContentObserver to watch brightness **/
    180     private class BrightnessObserver extends ContentObserver {
    181         public BrightnessObserver(Handler handler) {
    182             super(handler);
    183         }
    184 
    185         @Override
    186         public void onChange(boolean selfChange) {
    187             onBrightnessLevelChanged();
    188         }
    189 
    190         public void startObserving() {
    191             final ContentResolver cr = mContext.getContentResolver();
    192             cr.unregisterContentObserver(this);
    193             cr.registerContentObserver(
    194                     Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_MODE),
    195                     false, this, mUserTracker.getCurrentUserId());
    196             cr.registerContentObserver(
    197                     Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS),
    198                     false, this, mUserTracker.getCurrentUserId());
    199         }
    200     }
    201 
    202     /** Callback for changes to remote display routes. */
    203     private class RemoteDisplayRouteCallback extends MediaRouter.SimpleCallback {
    204         @Override
    205         public void onRouteAdded(MediaRouter router, RouteInfo route) {
    206             updateRemoteDisplays();
    207         }
    208         @Override
    209         public void onRouteChanged(MediaRouter router, RouteInfo route) {
    210             updateRemoteDisplays();
    211         }
    212         @Override
    213         public void onRouteRemoved(MediaRouter router, RouteInfo route) {
    214             updateRemoteDisplays();
    215         }
    216         @Override
    217         public void onRouteSelected(MediaRouter router, int type, RouteInfo route) {
    218             updateRemoteDisplays();
    219         }
    220         @Override
    221         public void onRouteUnselected(MediaRouter router, int type, RouteInfo route) {
    222             updateRemoteDisplays();
    223         }
    224     }
    225 
    226     private final Context mContext;
    227     private final Handler mHandler;
    228     private final CurrentUserTracker mUserTracker;
    229     private final NextAlarmObserver mNextAlarmObserver;
    230     private final BugreportObserver mBugreportObserver;
    231     private final BrightnessObserver mBrightnessObserver;
    232 
    233     private final MediaRouter mMediaRouter;
    234     private final RemoteDisplayRouteCallback mRemoteDisplayRouteCallback;
    235 
    236     private final boolean mHasMobileData;
    237 
    238     private QuickSettingsTileView mUserTile;
    239     private RefreshCallback mUserCallback;
    240     private UserState mUserState = new UserState();
    241 
    242     private QuickSettingsTileView mTimeTile;
    243     private RefreshCallback mTimeCallback;
    244     private State mTimeState = new State();
    245 
    246     private QuickSettingsTileView mAlarmTile;
    247     private RefreshCallback mAlarmCallback;
    248     private State mAlarmState = new State();
    249 
    250     private QuickSettingsTileView mAirplaneModeTile;
    251     private RefreshCallback mAirplaneModeCallback;
    252     private State mAirplaneModeState = new State();
    253 
    254     private QuickSettingsTileView mWifiTile;
    255     private RefreshCallback mWifiCallback;
    256     private WifiState mWifiState = new WifiState();
    257 
    258     private QuickSettingsTileView mRemoteDisplayTile;
    259     private RefreshCallback mRemoteDisplayCallback;
    260     private State mRemoteDisplayState = new State();
    261 
    262     private QuickSettingsTileView mRSSITile;
    263     private RefreshCallback mRSSICallback;
    264     private RSSIState mRSSIState = new RSSIState();
    265 
    266     private QuickSettingsTileView mBluetoothTile;
    267     private RefreshCallback mBluetoothCallback;
    268     private BluetoothState mBluetoothState = new BluetoothState();
    269 
    270     private QuickSettingsTileView mBatteryTile;
    271     private RefreshCallback mBatteryCallback;
    272     private BatteryState mBatteryState = new BatteryState();
    273 
    274     private QuickSettingsTileView mLocationTile;
    275     private RefreshCallback mLocationCallback;
    276     private State mLocationState = new State();
    277 
    278     private QuickSettingsTileView mImeTile;
    279     private RefreshCallback mImeCallback = null;
    280     private State mImeState = new State();
    281 
    282     private QuickSettingsTileView mRotationLockTile;
    283     private RefreshCallback mRotationLockCallback;
    284     private RotationLockState mRotationLockState = new RotationLockState();
    285 
    286     private QuickSettingsTileView mBrightnessTile;
    287     private RefreshCallback mBrightnessCallback;
    288     private BrightnessState mBrightnessState = new BrightnessState();
    289 
    290     private QuickSettingsTileView mBugreportTile;
    291     private RefreshCallback mBugreportCallback;
    292     private State mBugreportState = new State();
    293 
    294     private QuickSettingsTileView mSettingsTile;
    295     private RefreshCallback mSettingsCallback;
    296     private State mSettingsState = new State();
    297 
    298     private QuickSettingsTileView mSslCaCertWarningTile;
    299     private RefreshCallback mSslCaCertWarningCallback;
    300     private State mSslCaCertWarningState = new State();
    301 
    302     private RotationLockController mRotationLockController;
    303 
    304     public QuickSettingsModel(Context context) {
    305         mContext = context;
    306         mHandler = new Handler();
    307         mUserTracker = new CurrentUserTracker(mContext) {
    308             @Override
    309             public void onUserSwitched(int newUserId) {
    310                 mBrightnessObserver.startObserving();
    311                 refreshRotationLockTile();
    312                 onBrightnessLevelChanged();
    313                 onNextAlarmChanged();
    314                 onBugreportChanged();
    315                 rebindMediaRouterAsCurrentUser();
    316             }
    317         };
    318 
    319         mNextAlarmObserver = new NextAlarmObserver(mHandler);
    320         mNextAlarmObserver.startObserving();
    321         mBugreportObserver = new BugreportObserver(mHandler);
    322         mBugreportObserver.startObserving();
    323         mBrightnessObserver = new BrightnessObserver(mHandler);
    324         mBrightnessObserver.startObserving();
    325 
    326         mMediaRouter = (MediaRouter)context.getSystemService(Context.MEDIA_ROUTER_SERVICE);
    327         rebindMediaRouterAsCurrentUser();
    328 
    329         mRemoteDisplayRouteCallback = new RemoteDisplayRouteCallback();
    330 
    331         ConnectivityManager cm = (ConnectivityManager)
    332                 context.getSystemService(Context.CONNECTIVITY_SERVICE);
    333         mHasMobileData = cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
    334 
    335         IntentFilter alarmIntentFilter = new IntentFilter();
    336         alarmIntentFilter.addAction(Intent.ACTION_ALARM_CHANGED);
    337         context.registerReceiver(mAlarmIntentReceiver, alarmIntentFilter);
    338     }
    339 
    340     void updateResources() {
    341         refreshSettingsTile();
    342         refreshBatteryTile();
    343         refreshBluetoothTile();
    344         refreshBrightnessTile();
    345         refreshRotationLockTile();
    346         refreshRssiTile();
    347         refreshLocationTile();
    348     }
    349 
    350     // Settings
    351     void addSettingsTile(QuickSettingsTileView view, RefreshCallback cb) {
    352         mSettingsTile = view;
    353         mSettingsCallback = cb;
    354         refreshSettingsTile();
    355     }
    356     void refreshSettingsTile() {
    357         Resources r = mContext.getResources();
    358         mSettingsState.label = r.getString(R.string.quick_settings_settings_label);
    359         mSettingsCallback.refreshView(mSettingsTile, mSettingsState);
    360     }
    361 
    362     // User
    363     void addUserTile(QuickSettingsTileView view, RefreshCallback cb) {
    364         mUserTile = view;
    365         mUserCallback = cb;
    366         mUserCallback.refreshView(mUserTile, mUserState);
    367     }
    368     void setUserTileInfo(String name, Drawable avatar) {
    369         mUserState.label = name;
    370         mUserState.avatar = avatar;
    371         mUserCallback.refreshView(mUserTile, mUserState);
    372     }
    373 
    374     // Time
    375     void addTimeTile(QuickSettingsTileView view, RefreshCallback cb) {
    376         mTimeTile = view;
    377         mTimeCallback = cb;
    378         mTimeCallback.refreshView(view, mTimeState);
    379     }
    380 
    381     // Alarm
    382     void addAlarmTile(QuickSettingsTileView view, RefreshCallback cb) {
    383         mAlarmTile = view;
    384         mAlarmCallback = cb;
    385         mAlarmCallback.refreshView(view, mAlarmState);
    386     }
    387     void onAlarmChanged(Intent intent) {
    388         mAlarmState.enabled = intent.getBooleanExtra("alarmSet", false);
    389         mAlarmCallback.refreshView(mAlarmTile, mAlarmState);
    390     }
    391     void onNextAlarmChanged() {
    392         final String alarmText = Settings.System.getStringForUser(mContext.getContentResolver(),
    393                 Settings.System.NEXT_ALARM_FORMATTED,
    394                 UserHandle.USER_CURRENT);
    395         mAlarmState.label = alarmText;
    396 
    397         // When switching users, this is the only clue we're going to get about whether the
    398         // alarm is actually set, since we won't get the ACTION_ALARM_CHANGED broadcast
    399         mAlarmState.enabled = ! TextUtils.isEmpty(alarmText);
    400 
    401         mAlarmCallback.refreshView(mAlarmTile, mAlarmState);
    402     }
    403 
    404     // Airplane Mode
    405     void addAirplaneModeTile(QuickSettingsTileView view, RefreshCallback cb) {
    406         mAirplaneModeTile = view;
    407         mAirplaneModeTile.setOnClickListener(new View.OnClickListener() {
    408             @Override
    409             public void onClick(View v) {
    410                 if (mAirplaneModeState.enabled) {
    411                     setAirplaneModeState(false);
    412                 } else {
    413                     setAirplaneModeState(true);
    414                 }
    415             }
    416         });
    417         mAirplaneModeCallback = cb;
    418         int airplaneMode = Settings.Global.getInt(mContext.getContentResolver(),
    419                 Settings.Global.AIRPLANE_MODE_ON, 0);
    420         onAirplaneModeChanged(airplaneMode != 0);
    421     }
    422     private void setAirplaneModeState(boolean enabled) {
    423         // TODO: Sets the view to be "awaiting" if not already awaiting
    424 
    425         // Change the system setting
    426         Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON,
    427                                 enabled ? 1 : 0);
    428 
    429         // Post the intent
    430         Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
    431         intent.putExtra("state", enabled);
    432         mContext.sendBroadcast(intent);
    433     }
    434     // NetworkSignalChanged callback
    435     @Override
    436     public void onAirplaneModeChanged(boolean enabled) {
    437         // TODO: If view is in awaiting state, disable
    438         Resources r = mContext.getResources();
    439         mAirplaneModeState.enabled = enabled;
    440         mAirplaneModeState.iconId = (enabled ?
    441                 R.drawable.ic_qs_airplane_on :
    442                 R.drawable.ic_qs_airplane_off);
    443         mAirplaneModeState.label = r.getString(R.string.quick_settings_airplane_mode_label);
    444         mAirplaneModeCallback.refreshView(mAirplaneModeTile, mAirplaneModeState);
    445     }
    446 
    447     // Wifi
    448     void addWifiTile(QuickSettingsTileView view, RefreshCallback cb) {
    449         mWifiTile = view;
    450         mWifiCallback = cb;
    451         mWifiCallback.refreshView(mWifiTile, mWifiState);
    452     }
    453     // Remove the double quotes that the SSID may contain
    454     public static String removeDoubleQuotes(String string) {
    455         if (string == null) return null;
    456         final int length = string.length();
    457         if ((length > 1) && (string.charAt(0) == '"') && (string.charAt(length - 1) == '"')) {
    458             return string.substring(1, length - 1);
    459         }
    460         return string;
    461     }
    462     // Remove the period from the network name
    463     public static String removeTrailingPeriod(String string) {
    464         if (string == null) return null;
    465         final int length = string.length();
    466         if (string.endsWith(".")) {
    467             return string.substring(0, length - 1);
    468         }
    469         return string;
    470     }
    471     // NetworkSignalChanged callback
    472     @Override
    473     public void onWifiSignalChanged(boolean enabled, int wifiSignalIconId,
    474             boolean activityIn, boolean activityOut,
    475             String wifiSignalContentDescription, String enabledDesc) {
    476         // TODO: If view is in awaiting state, disable
    477         Resources r = mContext.getResources();
    478 
    479         boolean wifiConnected = enabled && (wifiSignalIconId > 0) && (enabledDesc != null);
    480         boolean wifiNotConnected = (wifiSignalIconId > 0) && (enabledDesc == null);
    481         mWifiState.enabled = enabled;
    482         mWifiState.connected = wifiConnected;
    483         mWifiState.activityIn = enabled && activityIn;
    484         mWifiState.activityOut = enabled && activityOut;
    485         if (wifiConnected) {
    486             mWifiState.iconId = wifiSignalIconId;
    487             mWifiState.label = removeDoubleQuotes(enabledDesc);
    488             mWifiState.signalContentDescription = wifiSignalContentDescription;
    489         } else if (wifiNotConnected) {
    490             mWifiState.iconId = R.drawable.ic_qs_wifi_0;
    491             mWifiState.label = r.getString(R.string.quick_settings_wifi_label);
    492             mWifiState.signalContentDescription = r.getString(R.string.accessibility_no_wifi);
    493         } else {
    494             mWifiState.iconId = R.drawable.ic_qs_wifi_no_network;
    495             mWifiState.label = r.getString(R.string.quick_settings_wifi_off_label);
    496             mWifiState.signalContentDescription = r.getString(R.string.accessibility_wifi_off);
    497         }
    498         mWifiCallback.refreshView(mWifiTile, mWifiState);
    499     }
    500 
    501     boolean deviceHasMobileData() {
    502         return mHasMobileData;
    503     }
    504 
    505     // RSSI
    506     void addRSSITile(QuickSettingsTileView view, RefreshCallback cb) {
    507         mRSSITile = view;
    508         mRSSICallback = cb;
    509         mRSSICallback.refreshView(mRSSITile, mRSSIState);
    510     }
    511     // NetworkSignalChanged callback
    512     @Override
    513     public void onMobileDataSignalChanged(
    514             boolean enabled, int mobileSignalIconId, String signalContentDescription,
    515             int dataTypeIconId, boolean activityIn, boolean activityOut,
    516             String dataContentDescription,String enabledDesc) {
    517         if (deviceHasMobileData()) {
    518             // TODO: If view is in awaiting state, disable
    519             Resources r = mContext.getResources();
    520             mRSSIState.signalIconId = enabled && (mobileSignalIconId > 0)
    521                     ? mobileSignalIconId
    522                     : R.drawable.ic_qs_signal_no_signal;
    523             mRSSIState.signalContentDescription = enabled && (mobileSignalIconId > 0)
    524                     ? signalContentDescription
    525                     : r.getString(R.string.accessibility_no_signal);
    526             mRSSIState.dataTypeIconId = enabled && (dataTypeIconId > 0) && !mWifiState.enabled
    527                     ? dataTypeIconId
    528                     : 0;
    529             mRSSIState.activityIn = enabled && activityIn;
    530             mRSSIState.activityOut = enabled && activityOut;
    531             mRSSIState.dataContentDescription = enabled && (dataTypeIconId > 0) && !mWifiState.enabled
    532                     ? dataContentDescription
    533                     : r.getString(R.string.accessibility_no_data);
    534             mRSSIState.label = enabled
    535                     ? removeTrailingPeriod(enabledDesc)
    536                     : r.getString(R.string.quick_settings_rssi_emergency_only);
    537             mRSSICallback.refreshView(mRSSITile, mRSSIState);
    538         }
    539     }
    540 
    541     void refreshRssiTile() {
    542         if (mRSSITile != null) {
    543             // We reinflate the original view due to potential styling changes that may have
    544             // taken place due to a configuration change.
    545             mRSSITile.reinflateContent(LayoutInflater.from(mContext));
    546         }
    547     }
    548 
    549     // Bluetooth
    550     void addBluetoothTile(QuickSettingsTileView view, RefreshCallback cb) {
    551         mBluetoothTile = view;
    552         mBluetoothCallback = cb;
    553 
    554         final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
    555         mBluetoothState.enabled = adapter.isEnabled();
    556         mBluetoothState.connected =
    557                 (adapter.getConnectionState() == BluetoothAdapter.STATE_CONNECTED);
    558         onBluetoothStateChange(mBluetoothState);
    559     }
    560     boolean deviceSupportsBluetooth() {
    561         return (BluetoothAdapter.getDefaultAdapter() != null);
    562     }
    563     // BluetoothController callback
    564     @Override
    565     public void onBluetoothStateChange(boolean on) {
    566         mBluetoothState.enabled = on;
    567         onBluetoothStateChange(mBluetoothState);
    568     }
    569     public void onBluetoothStateChange(BluetoothState bluetoothStateIn) {
    570         // TODO: If view is in awaiting state, disable
    571         Resources r = mContext.getResources();
    572         mBluetoothState.enabled = bluetoothStateIn.enabled;
    573         mBluetoothState.connected = bluetoothStateIn.connected;
    574         if (mBluetoothState.enabled) {
    575             if (mBluetoothState.connected) {
    576                 mBluetoothState.iconId = R.drawable.ic_qs_bluetooth_on;
    577                 mBluetoothState.stateContentDescription = r.getString(R.string.accessibility_desc_connected);
    578             } else {
    579                 mBluetoothState.iconId = R.drawable.ic_qs_bluetooth_not_connected;
    580                 mBluetoothState.stateContentDescription = r.getString(R.string.accessibility_desc_on);
    581             }
    582             mBluetoothState.label = r.getString(R.string.quick_settings_bluetooth_label);
    583         } else {
    584             mBluetoothState.iconId = R.drawable.ic_qs_bluetooth_off;
    585             mBluetoothState.label = r.getString(R.string.quick_settings_bluetooth_off_label);
    586             mBluetoothState.stateContentDescription = r.getString(R.string.accessibility_desc_off);
    587         }
    588         mBluetoothCallback.refreshView(mBluetoothTile, mBluetoothState);
    589     }
    590     void refreshBluetoothTile() {
    591         if (mBluetoothTile != null) {
    592             onBluetoothStateChange(mBluetoothState.enabled);
    593         }
    594     }
    595 
    596     // Battery
    597     void addBatteryTile(QuickSettingsTileView view, RefreshCallback cb) {
    598         mBatteryTile = view;
    599         mBatteryCallback = cb;
    600         mBatteryCallback.refreshView(mBatteryTile, mBatteryState);
    601     }
    602     // BatteryController callback
    603     @Override
    604     public void onBatteryLevelChanged(int level, boolean pluggedIn) {
    605         mBatteryState.batteryLevel = level;
    606         mBatteryState.pluggedIn = pluggedIn;
    607         mBatteryCallback.refreshView(mBatteryTile, mBatteryState);
    608     }
    609     void refreshBatteryTile() {
    610         mBatteryCallback.refreshView(mBatteryTile, mBatteryState);
    611     }
    612 
    613     // Location
    614     void addLocationTile(QuickSettingsTileView view, RefreshCallback cb) {
    615         mLocationTile = view;
    616         mLocationCallback = cb;
    617         mLocationCallback.refreshView(mLocationTile, mLocationState);
    618     }
    619 
    620     void refreshLocationTile() {
    621         if (mLocationTile != null) {
    622             onLocationSettingsChanged(mLocationState.enabled);
    623         }
    624     }
    625 
    626     @Override
    627     public void onLocationSettingsChanged(boolean locationEnabled) {
    628         int textResId = locationEnabled ? R.string.quick_settings_location_label
    629                 : R.string.quick_settings_location_off_label;
    630         String label = mContext.getText(textResId).toString();
    631         int locationIconId = locationEnabled
    632                 ? R.drawable.ic_qs_location_on : R.drawable.ic_qs_location_off;
    633         mLocationState.enabled = locationEnabled;
    634         mLocationState.label = label;
    635         mLocationState.iconId = locationIconId;
    636         mLocationCallback.refreshView(mLocationTile, mLocationState);
    637     }
    638 
    639     // Bug report
    640     void addBugreportTile(QuickSettingsTileView view, RefreshCallback cb) {
    641         mBugreportTile = view;
    642         mBugreportCallback = cb;
    643         onBugreportChanged();
    644     }
    645     // SettingsObserver callback
    646     public void onBugreportChanged() {
    647         final ContentResolver cr = mContext.getContentResolver();
    648         boolean enabled = false;
    649         try {
    650             enabled = (Settings.Global.getInt(cr, Settings.Global.BUGREPORT_IN_POWER_MENU) != 0);
    651         } catch (SettingNotFoundException e) {
    652         }
    653 
    654         mBugreportState.enabled = enabled && mUserTracker.isCurrentUserOwner();
    655         mBugreportCallback.refreshView(mBugreportTile, mBugreportState);
    656     }
    657 
    658     // Remote Display
    659     void addRemoteDisplayTile(QuickSettingsTileView view, RefreshCallback cb) {
    660         mRemoteDisplayTile = view;
    661         mRemoteDisplayCallback = cb;
    662         final int[] count = new int[1];
    663         mRemoteDisplayTile.setOnPrepareListener(new QuickSettingsTileView.OnPrepareListener() {
    664             @Override
    665             public void onPrepare() {
    666                 mMediaRouter.addCallback(MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY,
    667                         mRemoteDisplayRouteCallback,
    668                         MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY);
    669                 updateRemoteDisplays();
    670             }
    671             @Override
    672             public void onUnprepare() {
    673                 mMediaRouter.removeCallback(mRemoteDisplayRouteCallback);
    674             }
    675         });
    676 
    677         updateRemoteDisplays();
    678     }
    679 
    680     private void rebindMediaRouterAsCurrentUser() {
    681         mMediaRouter.rebindAsUser(mUserTracker.getCurrentUserId());
    682     }
    683 
    684     private void updateRemoteDisplays() {
    685         MediaRouter.RouteInfo connectedRoute = mMediaRouter.getSelectedRoute(
    686                 MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY);
    687         boolean enabled = connectedRoute != null
    688                 && connectedRoute.matchesTypes(MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY);
    689         boolean connecting;
    690         if (enabled) {
    691             connecting = connectedRoute.isConnecting();
    692         } else {
    693             connectedRoute = null;
    694             connecting = false;
    695             enabled = mMediaRouter.isRouteAvailable(MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY,
    696                     MediaRouter.AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE);
    697         }
    698 
    699         mRemoteDisplayState.enabled = enabled;
    700         if (connectedRoute != null) {
    701             mRemoteDisplayState.label = connectedRoute.getName().toString();
    702             mRemoteDisplayState.iconId = connecting ?
    703                     R.drawable.ic_qs_cast_connecting : R.drawable.ic_qs_cast_connected;
    704         } else {
    705             mRemoteDisplayState.label = mContext.getString(
    706                     R.string.quick_settings_remote_display_no_connection_label);
    707             mRemoteDisplayState.iconId = R.drawable.ic_qs_cast_available;
    708         }
    709         mRemoteDisplayCallback.refreshView(mRemoteDisplayTile, mRemoteDisplayState);
    710     }
    711 
    712     // IME
    713     void addImeTile(QuickSettingsTileView view, RefreshCallback cb) {
    714         mImeTile = view;
    715         mImeCallback = cb;
    716         mImeCallback.refreshView(mImeTile, mImeState);
    717     }
    718     /* This implementation is taken from
    719        InputMethodManagerService.needsToShowImeSwitchOngoingNotification(). */
    720     private boolean needsToShowImeSwitchOngoingNotification(InputMethodManager imm) {
    721         List<InputMethodInfo> imis = imm.getEnabledInputMethodList();
    722         final int N = imis.size();
    723         if (N > 2) return true;
    724         if (N < 1) return false;
    725         int nonAuxCount = 0;
    726         int auxCount = 0;
    727         InputMethodSubtype nonAuxSubtype = null;
    728         InputMethodSubtype auxSubtype = null;
    729         for(int i = 0; i < N; ++i) {
    730             final InputMethodInfo imi = imis.get(i);
    731             final List<InputMethodSubtype> subtypes = imm.getEnabledInputMethodSubtypeList(imi,
    732                     true);
    733             final int subtypeCount = subtypes.size();
    734             if (subtypeCount == 0) {
    735                 ++nonAuxCount;
    736             } else {
    737                 for (int j = 0; j < subtypeCount; ++j) {
    738                     final InputMethodSubtype subtype = subtypes.get(j);
    739                     if (!subtype.isAuxiliary()) {
    740                         ++nonAuxCount;
    741                         nonAuxSubtype = subtype;
    742                     } else {
    743                         ++auxCount;
    744                         auxSubtype = subtype;
    745                     }
    746                 }
    747             }
    748         }
    749         if (nonAuxCount > 1 || auxCount > 1) {
    750             return true;
    751         } else if (nonAuxCount == 1 && auxCount == 1) {
    752             if (nonAuxSubtype != null && auxSubtype != null
    753                     && (nonAuxSubtype.getLocale().equals(auxSubtype.getLocale())
    754                             || auxSubtype.overridesImplicitlyEnabledSubtype()
    755                             || nonAuxSubtype.overridesImplicitlyEnabledSubtype())
    756                     && nonAuxSubtype.containsExtraValueKey(TAG_TRY_SUPPRESSING_IME_SWITCHER)) {
    757                 return false;
    758             }
    759             return true;
    760         }
    761         return false;
    762     }
    763     void onImeWindowStatusChanged(boolean visible) {
    764         InputMethodManager imm =
    765                 (InputMethodManager) mContext.getSystemService(Context.INPUT_METHOD_SERVICE);
    766         List<InputMethodInfo> imis = imm.getInputMethodList();
    767 
    768         mImeState.enabled = (visible && needsToShowImeSwitchOngoingNotification(imm));
    769         mImeState.label = getCurrentInputMethodName(mContext, mContext.getContentResolver(),
    770                 imm, imis, mContext.getPackageManager());
    771         if (mImeCallback != null) {
    772             mImeCallback.refreshView(mImeTile, mImeState);
    773         }
    774     }
    775     private static String getCurrentInputMethodName(Context context, ContentResolver resolver,
    776             InputMethodManager imm, List<InputMethodInfo> imis, PackageManager pm) {
    777         if (resolver == null || imis == null) return null;
    778         final String currentInputMethodId = Settings.Secure.getString(resolver,
    779                 Settings.Secure.DEFAULT_INPUT_METHOD);
    780         if (TextUtils.isEmpty(currentInputMethodId)) return null;
    781         for (InputMethodInfo imi : imis) {
    782             if (currentInputMethodId.equals(imi.getId())) {
    783                 final InputMethodSubtype subtype = imm.getCurrentInputMethodSubtype();
    784                 final CharSequence summary = subtype != null
    785                         ? subtype.getDisplayName(context, imi.getPackageName(),
    786                                 imi.getServiceInfo().applicationInfo)
    787                         : context.getString(R.string.quick_settings_ime_label);
    788                 return summary.toString();
    789             }
    790         }
    791         return null;
    792     }
    793 
    794     // Rotation lock
    795     void addRotationLockTile(QuickSettingsTileView view,
    796             RotationLockController rotationLockController,
    797             RefreshCallback cb) {
    798         mRotationLockTile = view;
    799         mRotationLockCallback = cb;
    800         mRotationLockController = rotationLockController;
    801         onRotationLockChanged();
    802     }
    803     void onRotationLockChanged() {
    804         onRotationLockStateChanged(mRotationLockController.isRotationLocked(),
    805                 mRotationLockController.isRotationLockAffordanceVisible());
    806     }
    807     @Override
    808     public void onRotationLockStateChanged(boolean rotationLocked, boolean affordanceVisible) {
    809         mRotationLockState.visible = affordanceVisible;
    810         mRotationLockState.enabled = rotationLocked;
    811         mRotationLockState.iconId = rotationLocked
    812                 ? R.drawable.ic_qs_rotation_locked
    813                 : R.drawable.ic_qs_auto_rotate;
    814         mRotationLockState.label = rotationLocked
    815                 ? mContext.getString(R.string.quick_settings_rotation_locked_label)
    816                 : mContext.getString(R.string.quick_settings_rotation_unlocked_label);
    817         mRotationLockCallback.refreshView(mRotationLockTile, mRotationLockState);
    818     }
    819     void refreshRotationLockTile() {
    820         if (mRotationLockTile != null) {
    821             onRotationLockChanged();
    822         }
    823     }
    824 
    825     // Brightness
    826     void addBrightnessTile(QuickSettingsTileView view, RefreshCallback cb) {
    827         mBrightnessTile = view;
    828         mBrightnessCallback = cb;
    829         onBrightnessLevelChanged();
    830     }
    831     @Override
    832     public void onBrightnessLevelChanged() {
    833         Resources r = mContext.getResources();
    834         int mode = Settings.System.getIntForUser(mContext.getContentResolver(),
    835                 Settings.System.SCREEN_BRIGHTNESS_MODE,
    836                 Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL,
    837                 mUserTracker.getCurrentUserId());
    838         mBrightnessState.autoBrightness =
    839                 (mode == Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
    840         mBrightnessState.iconId = mBrightnessState.autoBrightness
    841                 ? R.drawable.ic_qs_brightness_auto_on
    842                 : R.drawable.ic_qs_brightness_auto_off;
    843         mBrightnessState.label = r.getString(R.string.quick_settings_brightness_label);
    844         mBrightnessCallback.refreshView(mBrightnessTile, mBrightnessState);
    845     }
    846     void refreshBrightnessTile() {
    847         onBrightnessLevelChanged();
    848     }
    849 
    850     // SSL CA Cert warning.
    851     public void addSslCaCertWarningTile(QuickSettingsTileView view, RefreshCallback cb) {
    852         mSslCaCertWarningTile = view;
    853         mSslCaCertWarningCallback = cb;
    854         // Set a sane default while we wait for the AsyncTask to finish (no cert).
    855         setSslCaCertWarningTileInfo(false, true);
    856     }
    857     public void setSslCaCertWarningTileInfo(boolean hasCert, boolean isManaged) {
    858         Resources r = mContext.getResources();
    859         mSslCaCertWarningState.enabled = hasCert;
    860         if (isManaged) {
    861             mSslCaCertWarningState.iconId = R.drawable.ic_qs_certificate_info;
    862         } else {
    863             mSslCaCertWarningState.iconId = android.R.drawable.stat_notify_error;
    864         }
    865         mSslCaCertWarningState.label = r.getString(R.string.ssl_ca_cert_warning);
    866         mSslCaCertWarningCallback.refreshView(mSslCaCertWarningTile, mSslCaCertWarningState);
    867     }
    868 }
    869