QuickSettingsModel.java revision 4f49d9450fe919868f9a9a9a44db59ca2497150c
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.hardware.display.WifiDisplayStatus;
31import android.os.Handler;
32import android.provider.Settings;
33import android.text.TextUtils;
34import android.view.View;
35import android.view.inputmethod.InputMethodInfo;
36import android.view.inputmethod.InputMethodManager;
37import android.view.inputmethod.InputMethodSubtype;
38
39import com.android.internal.view.RotationPolicy;
40import com.android.systemui.R;
41import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
42import com.android.systemui.statusbar.policy.LocationController.LocationGpsStateChangeCallback;
43import com.android.systemui.statusbar.policy.NetworkController.NetworkSignalChangedCallback;
44
45import java.util.List;
46
47
48class QuickSettingsModel implements BluetoothStateChangeCallback,
49        NetworkSignalChangedCallback,
50        BatteryStateChangeCallback,
51        LocationGpsStateChangeCallback {
52
53    /** Represents the state of a given attribute. */
54    static class State {
55        int iconId;
56        String label;
57        boolean enabled = false;
58    }
59    static class BatteryState extends State {
60        int batteryLevel;
61        boolean pluggedIn;
62    }
63    static class RSSIState extends State {
64        int signalIconId;
65        int dataTypeIconId;
66    }
67    static class UserState extends State {
68        Drawable avatar;
69    }
70
71    /** The callback to update a given tile. */
72    interface RefreshCallback {
73        public void refreshView(QuickSettingsTileView view, State state);
74    }
75
76    /** Broadcast receive to determine if there is an alarm set. */
77    private BroadcastReceiver mAlarmIntentReceiver = new BroadcastReceiver() {
78        @Override
79        public void onReceive(Context context, Intent intent) {
80            String action = intent.getAction();
81            if (action.equals(Intent.ACTION_ALARM_CHANGED)) {
82                onAlarmChanged(intent);
83                onNextAlarmChanged();
84            }
85        }
86    };
87
88    /** ContentObserver to determine the next alarm */
89    private class NextAlarmObserver extends ContentObserver {
90        public NextAlarmObserver(Handler handler) {
91            super(handler);
92        }
93
94        @Override public void onChange(boolean selfChange) {
95            onNextAlarmChanged();
96        }
97
98        public void startObserving() {
99            final ContentResolver cr = mContext.getContentResolver();
100            cr.registerContentObserver(
101                    Settings.System.getUriFor(Settings.System.NEXT_ALARM_FORMATTED), false, this);
102        }
103    }
104
105    private Context mContext;
106    private Handler mHandler;
107    private NextAlarmObserver mNextAlarmObserver;
108
109    private QuickSettingsTileView mUserTile;
110    private RefreshCallback mUserCallback;
111    private UserState mUserState = new UserState();
112
113    private QuickSettingsTileView mTimeTile;
114    private RefreshCallback mTimeAlarmCallback;
115    private State mTimeAlarmState = new State();
116
117    private QuickSettingsTileView mAirplaneModeTile;
118    private RefreshCallback mAirplaneModeCallback;
119    private State mAirplaneModeState = new State();
120
121    private QuickSettingsTileView mWifiTile;
122    private RefreshCallback mWifiCallback;
123    private State mWifiState = new State();
124
125    private QuickSettingsTileView mWifiDisplayTile;
126    private RefreshCallback mWifiDisplayCallback;
127    private State mWifiDisplayState = new State();
128
129    private QuickSettingsTileView mRSSITile;
130    private RefreshCallback mRSSICallback;
131    private RSSIState mRSSIState = new RSSIState();
132
133    private QuickSettingsTileView mBluetoothTile;
134    private RefreshCallback mBluetoothCallback;
135    private State mBluetoothState = new State();
136
137    private QuickSettingsTileView mBatteryTile;
138    private RefreshCallback mBatteryCallback;
139    private BatteryState mBatteryState = new BatteryState();
140
141    private QuickSettingsTileView mLocationTile;
142    private RefreshCallback mLocationCallback;
143    private State mLocationState = new State();
144
145    private QuickSettingsTileView mImeTile;
146    private RefreshCallback mImeCallback;
147    private State mImeState = new State();
148
149    private QuickSettingsTileView mRotationLockTile;
150    private RefreshCallback mRotationLockCallback;
151    private State mRotationLockState = new State();
152
153    public QuickSettingsModel(Context context) {
154        mContext = context;
155        mHandler = new Handler();
156        mNextAlarmObserver = new NextAlarmObserver(mHandler);
157        mNextAlarmObserver.startObserving();
158
159        IntentFilter alarmIntentFilter = new IntentFilter();
160        alarmIntentFilter.addAction(Intent.ACTION_ALARM_CHANGED);
161        context.registerReceiver(mAlarmIntentReceiver, alarmIntentFilter);
162    }
163
164    // User
165    void addUserTile(QuickSettingsTileView view, RefreshCallback cb) {
166        mUserTile = view;
167        mUserCallback = cb;
168        mUserCallback.refreshView(mUserTile, mUserState);
169    }
170    void setUserTileInfo(String name, Drawable avatar) {
171        mUserState.label = name;
172        mUserState.avatar = avatar;
173        mUserCallback.refreshView(mUserTile, mUserState);
174    }
175
176    // Time
177    void addTimeTile(QuickSettingsTileView view, RefreshCallback cb) {
178        mTimeTile = view;
179        mTimeAlarmCallback = cb;
180        mTimeAlarmCallback.refreshView(view, mTimeAlarmState);
181    }
182    void onAlarmChanged(Intent intent) {
183        mTimeAlarmState.enabled = intent.getBooleanExtra("alarmSet", false);
184        mTimeAlarmCallback.refreshView(mTimeTile, mTimeAlarmState);
185    }
186    void onNextAlarmChanged() {
187        mTimeAlarmState.label = Settings.System.getString(mContext.getContentResolver(),
188                Settings.System.NEXT_ALARM_FORMATTED);
189        mTimeAlarmCallback.refreshView(mTimeTile, mTimeAlarmState);
190    }
191
192    // Airplane Mode
193    void addAirplaneModeTile(QuickSettingsTileView view, RefreshCallback cb) {
194        mAirplaneModeTile = view;
195        mAirplaneModeTile.setOnClickListener(new View.OnClickListener() {
196            @Override
197            public void onClick(View v) {
198                if (mAirplaneModeState.enabled) {
199                    setAirplaneModeState(false);
200                } else {
201                    setAirplaneModeState(true);
202                }
203            }
204        });
205        mAirplaneModeCallback = cb;
206        mAirplaneModeCallback.refreshView(mAirplaneModeTile, mAirplaneModeState);
207    }
208    private void setAirplaneModeState(boolean enabled) {
209        // TODO: Sets the view to be "awaiting" if not already awaiting
210
211        // Change the system setting
212        Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON,
213                                enabled ? 1 : 0);
214
215        // Post the intent
216        Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
217        intent.putExtra("state", enabled);
218        mContext.sendBroadcast(intent);
219    }
220    // NetworkSignalChanged callback
221    @Override
222    public void onAirplaneModeChanged(boolean enabled) {
223        // TODO: If view is in awaiting state, disable
224        Resources r = mContext.getResources();
225        mAirplaneModeState.enabled = enabled;
226        mAirplaneModeState.iconId = (enabled ?
227                R.drawable.ic_qs_airplane_on :
228                R.drawable.ic_qs_airplane_off);
229        mAirplaneModeCallback.refreshView(mAirplaneModeTile, mAirplaneModeState);
230    }
231
232    // Wifi
233    void addWifiTile(QuickSettingsTileView view, RefreshCallback cb) {
234        mWifiTile = view;
235        mWifiCallback = cb;
236        mWifiCallback.refreshView(mWifiTile, mWifiState);
237    }
238    // NetworkSignalChanged callback
239    @Override
240    public void onWifiSignalChanged(boolean enabled, int wifiSignalIconId, String enabledDesc) {
241        // TODO: If view is in awaiting state, disable
242        Resources r = mContext.getResources();
243        mWifiState.iconId = enabled && (wifiSignalIconId > 0)
244                ? wifiSignalIconId
245                : R.drawable.ic_qs_wifi_no_network;
246        mWifiState.label = enabled
247                ? enabledDesc
248                : r.getString(R.string.quick_settings_wifi_no_network);
249        mWifiCallback.refreshView(mWifiTile, mWifiState);
250    }
251
252    // RSSI
253    void addRSSITile(QuickSettingsTileView view, RefreshCallback cb) {
254        mRSSITile = view;
255        mRSSICallback = cb;
256        mRSSICallback.refreshView(mRSSITile, mRSSIState);
257    }
258    boolean deviceSupportsTelephony() {
259        PackageManager pm = mContext.getPackageManager();
260        return pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY);
261    }
262    // NetworkSignalChanged callback
263    @Override
264    public void onMobileDataSignalChanged(boolean enabled, int mobileSignalIconId,
265            int dataTypeIconId, String enabledDesc) {
266        if (deviceSupportsTelephony()) {
267            // TODO: If view is in awaiting state, disable
268            Resources r = mContext.getResources();
269            mRSSIState.signalIconId = enabled && (mobileSignalIconId > 0)
270                    ? mobileSignalIconId
271                    : R.drawable.ic_qs_signal_no_signal;
272            mRSSIState.dataTypeIconId = enabled && (dataTypeIconId > 0)
273                    ? dataTypeIconId
274                    : 0;
275            mRSSIState.label = enabled
276                    ? enabledDesc
277                    : r.getString(R.string.quick_settings_rssi_emergency_only);
278            mRSSICallback.refreshView(mRSSITile, mRSSIState);
279        }
280    }
281
282    // Bluetooth
283    void addBluetoothTile(QuickSettingsTileView view, RefreshCallback cb) {
284        mBluetoothTile = view;
285        mBluetoothCallback = cb;
286
287        final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
288        onBluetoothStateChange(adapter.isEnabled());
289    }
290    boolean deviceSupportsBluetooth() {
291        return (BluetoothAdapter.getDefaultAdapter() != null);
292    }
293    // BluetoothController callback
294    @Override
295    public void onBluetoothStateChange(boolean on) {
296        // TODO: If view is in awaiting state, disable
297        Resources r = mContext.getResources();
298        mBluetoothState.enabled = on;
299        if (on) {
300            mBluetoothState.iconId = R.drawable.ic_qs_bluetooth_on;
301        } else {
302            mBluetoothState.iconId = R.drawable.ic_qs_bluetooth_off;
303        }
304        mBluetoothCallback.refreshView(mBluetoothTile, mBluetoothState);
305    }
306
307    // Battery
308    void addBatteryTile(QuickSettingsTileView view, RefreshCallback cb) {
309        mBatteryTile = view;
310        mBatteryCallback = cb;
311        mBatteryCallback.refreshView(mBatteryTile, mBatteryState);
312    }
313    // BatteryController callback
314    @Override
315    public void onBatteryLevelChanged(int level, boolean pluggedIn) {
316        mBatteryState.batteryLevel = level;
317        mBatteryState.pluggedIn = pluggedIn;
318        mBatteryCallback.refreshView(mBatteryTile, mBatteryState);
319    }
320
321    // Location
322    void addLocationTile(QuickSettingsTileView view, RefreshCallback cb) {
323        mLocationTile = view;
324        mLocationCallback = cb;
325        mLocationCallback.refreshView(mLocationTile, mLocationState);
326    }
327    // LocationController callback
328    @Override
329    public void onLocationGpsStateChanged(boolean inUse, String description) {
330        mLocationState.enabled = inUse;
331        mLocationState.label = description;
332        mLocationCallback.refreshView(mLocationTile, mLocationState);
333    }
334
335    // Wifi Display
336    void addWifiDisplayTile(QuickSettingsTileView view, RefreshCallback cb) {
337        mWifiDisplayTile = view;
338        mWifiDisplayCallback = cb;
339    }
340    public void onWifiDisplayStateChanged(WifiDisplayStatus status) {
341        mWifiDisplayState.enabled = status.isEnabled();
342        if (status.getActiveDisplay() != null) {
343            mWifiDisplayState.label = status.getActiveDisplay().getDeviceName();
344        } else {
345            mWifiDisplayState.label = mContext.getString(
346                    R.string.quick_settings_wifi_display_no_connection_label);
347        }
348        mWifiDisplayCallback.refreshView(mWifiDisplayTile, mWifiDisplayState);
349
350    }
351
352    // IME
353    void addImeTile(QuickSettingsTileView view, RefreshCallback cb) {
354        mImeTile = view;
355        mImeCallback = cb;
356        mImeCallback.refreshView(mImeTile, mImeState);
357    }
358    void onImeWindowStatusChanged(boolean visible) {
359        InputMethodManager imm =
360                (InputMethodManager) mContext.getSystemService(Context.INPUT_METHOD_SERVICE);
361        List<InputMethodInfo> imis = imm.getInputMethodList();
362
363        mImeState.enabled = visible;
364        mImeState.label = getCurrentInputMethodName(mContext, mContext.getContentResolver(),
365                imm, imis, mContext.getPackageManager());
366        mImeCallback.refreshView(mImeTile, mImeState);
367    }
368    private static String getCurrentInputMethodName(Context context, ContentResolver resolver,
369            InputMethodManager imm, List<InputMethodInfo> imis, PackageManager pm) {
370        if (resolver == null || imis == null) return null;
371        final String currentInputMethodId = Settings.Secure.getString(resolver,
372                Settings.Secure.DEFAULT_INPUT_METHOD);
373        if (TextUtils.isEmpty(currentInputMethodId)) return null;
374        for (InputMethodInfo imi : imis) {
375            if (currentInputMethodId.equals(imi.getId())) {
376                final InputMethodSubtype subtype = imm.getCurrentInputMethodSubtype();
377                final CharSequence summary = subtype != null
378                        ? subtype.getDisplayName(context, imi.getPackageName(),
379                                imi.getServiceInfo().applicationInfo)
380                        : context.getString(R.string.quick_settings_ime_label);
381                return summary.toString();
382            }
383        }
384        return null;
385    }
386
387    // Rotation lock
388    void addRotationLockTile(QuickSettingsTileView view, RefreshCallback cb) {
389        mRotationLockTile = view;
390        mRotationLockCallback = cb;
391        onRotationLockChanged();
392    }
393    void onRotationLockChanged() {
394        boolean locked = RotationPolicy.isRotationLocked(mContext);
395        mRotationLockState.enabled = locked;
396        mRotationLockState.iconId = locked
397                ? R.drawable.ic_qs_rotation_locked
398                : R.drawable.ic_qs_auto_rotate;
399        mRotationLockState.label = locked
400                ? mContext.getString(R.string.quick_settings_rotation_locked_label)
401                : mContext.getString(R.string.quick_settings_rotation_unlocked_label);
402        mRotationLockCallback.refreshView(mRotationLockTile, mRotationLockState);
403    }
404
405}