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
17package com.android.systemui.statusbar.phone;
18
19import android.bluetooth.BluetoothAdapter;
20import android.bluetooth.BluetoothAdapter.BluetoothStateChangeCallback;
21import android.content.BroadcastReceiver;
22import android.content.ContentResolver;
23import android.content.Context;
24import android.content.Intent;
25import android.content.IntentFilter;
26import android.content.pm.PackageManager;
27import android.content.res.Resources;
28import android.database.ContentObserver;
29import android.graphics.drawable.Drawable;
30import android.media.MediaRouter;
31import android.media.MediaRouter.RouteInfo;
32import android.net.ConnectivityManager;
33import android.os.Handler;
34import android.os.UserHandle;
35import android.provider.Settings;
36import android.provider.Settings.SettingNotFoundException;
37import android.text.TextUtils;
38import android.view.LayoutInflater;
39import android.view.View;
40import android.view.inputmethod.InputMethodInfo;
41import android.view.inputmethod.InputMethodManager;
42import android.view.inputmethod.InputMethodSubtype;
43
44import com.android.systemui.R;
45import com.android.systemui.settings.BrightnessController.BrightnessStateChangeCallback;
46import com.android.systemui.settings.CurrentUserTracker;
47import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
48import com.android.systemui.statusbar.policy.LocationController.LocationSettingsChangeCallback;
49import com.android.systemui.statusbar.policy.NetworkController.NetworkSignalChangedCallback;
50import com.android.systemui.statusbar.policy.RotationLockController;
51import com.android.systemui.statusbar.policy.RotationLockController.RotationLockControllerCallback;
52
53import java.util.List;
54
55class 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