QuickSettings.java revision d63c59786509aadd6a8d0c5cb45ed696339f16b7
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.Context;
22import android.content.CursorLoader;
23import android.content.Intent;
24import android.content.Loader;
25import android.content.res.Resources;
26import android.database.Cursor;
27import android.graphics.drawable.ClipDrawable;
28import android.net.Uri;
29import android.provider.ContactsContract;
30import android.provider.Settings;
31import android.view.LayoutInflater;
32import android.view.View;
33import android.view.ViewGroup;
34import android.widget.TextView;
35
36import java.util.ArrayList;
37
38import com.android.systemui.R;
39import com.android.systemui.statusbar.phone.QuickSettingsModel.State;
40import com.android.systemui.statusbar.policy.BatteryController;
41import com.android.systemui.statusbar.policy.BluetoothController;
42import com.android.systemui.statusbar.policy.LocationController;
43import com.android.systemui.statusbar.policy.NetworkController;
44
45class QuickSettingsModel implements BluetoothStateChangeCallback,
46        NetworkController.NetworkSignalChangedCallback,
47        BatteryController.BatteryStateChangeCallback,
48        LocationController.LocationGpsStateChangeCallback {
49
50    /** Represents the state of a given attribute. */
51    static class State {
52        int iconId;
53        String label;
54        boolean enabled;
55    }
56    static class BatteryState extends State {
57        int batteryLevel;
58        boolean pluggedIn;
59    }
60
61    /** The callback to update a given tile. */
62    interface RefreshCallback {
63        public void refreshView(QuickSettingsTileView view, State state);
64    }
65
66    private Context mContext;
67
68    private QuickSettingsTileView mUserTile;
69    private RefreshCallback mUserCallback;
70    private State mUserState = new State();
71
72    private QuickSettingsTileView mAirplaneModeTile;
73    private RefreshCallback mAirplaneModeCallback;
74    private State mAirplaneModeState = new State();
75
76    private QuickSettingsTileView mWifiTile;
77    private RefreshCallback mWifiCallback;
78    private State mWifiState = new State();
79
80    private QuickSettingsTileView mRSSITile;
81    private RefreshCallback mRSSICallback;
82    private State mRSSIState = new State();
83
84    private QuickSettingsTileView mBluetoothTile;
85    private RefreshCallback mBluetoothCallback;
86    private State mBluetoothState = new State();
87
88    private QuickSettingsTileView mBatteryTile;
89    private RefreshCallback mBatteryCallback;
90    private BatteryState mBatteryState = new BatteryState();
91
92    private QuickSettingsTileView mLocationTile;
93    private RefreshCallback mLocationCallback;
94    private State mLocationState = new State();
95
96    public QuickSettingsModel(Context context) {
97        mContext = context;
98    }
99
100    // User
101    void addUserTile(QuickSettingsTileView view, RefreshCallback cb) {
102        mUserTile = view;
103        mUserCallback = cb;
104        mUserCallback.refreshView(mUserTile, mUserState);
105    }
106    void setUserTileInfo(String name) {
107        mUserState.label = name;
108        mUserCallback.refreshView(mUserTile, mUserState);
109    }
110
111    // Airplane Mode
112    void addAirplaneModeTile(QuickSettingsTileView view, RefreshCallback cb) {
113        mAirplaneModeTile = view;
114        mAirplaneModeTile.setOnClickListener(new View.OnClickListener() {
115            @Override
116            public void onClick(View v) {
117                if (mAirplaneModeState.enabled) {
118                    setAirplaneModeState(false);
119                } else {
120                    setAirplaneModeState(true);
121                }
122            }
123        });
124        mAirplaneModeCallback = cb;
125        mAirplaneModeCallback.refreshView(mAirplaneModeTile, mAirplaneModeState);
126    }
127    private void setAirplaneModeState(boolean enabled) {
128        // TODO: Sets the view to be "awaiting" if not already awaiting
129
130        // Change the system setting
131        Settings.System.putInt(mContext.getContentResolver(), Settings.System.AIRPLANE_MODE_ON,
132                                enabled ? 1 : 0);
133
134        // TODO: Update the UI to reflect system setting
135        // mCheckBoxPref.setChecked(enabled);
136
137        // Post the intent
138        Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
139        intent.putExtra("state", enabled);
140        mContext.sendBroadcast(intent);
141    }
142    // NetworkSignalChanged callback
143    @Override
144    public void onAirplaneModeChanged(boolean enabled) {
145        // TODO: If view is in awaiting state, disable
146        Resources r = mContext.getResources();
147        mAirplaneModeState.enabled = enabled;
148        mAirplaneModeState.iconId = (enabled ?
149                R.drawable.ic_qs_airplane_enabled :
150                R.drawable.ic_qs_airplane_normal);
151        mAirplaneModeCallback.refreshView(mAirplaneModeTile, mAirplaneModeState);
152    }
153
154    // Wifi
155    void addWifiTile(QuickSettingsTileView view, RefreshCallback cb) {
156        mWifiTile = view;
157        mWifiCallback = cb;
158        mWifiCallback.refreshView(mWifiTile, mWifiState);
159    }
160    // NetworkSignalChanged callback
161    @Override
162    public void onWifiSignalChanged(boolean enabled, String description) {
163        // TODO: If view is in awaiting state, disable
164        Resources r = mContext.getResources();
165        // TODO: Check if wifi is enabled
166        mWifiState.enabled = enabled;
167        mWifiState.iconId = (enabled ?
168                R.drawable.ic_qs_wifi_enabled :
169                R.drawable.ic_qs_wifi_normal);
170        mWifiState.label = (enabled ?
171                description :
172                r.getString(R.string.quick_settings_wifi_no_network));
173        mWifiCallback.refreshView(mWifiTile, mWifiState);
174    }
175
176    // RSSI
177    void addRSSITile(QuickSettingsTileView view, RefreshCallback cb) {
178        mRSSITile = view;
179        mRSSICallback = cb;
180        mRSSICallback.refreshView(mRSSITile, mRSSIState);
181    }
182    private void setRSSIState(boolean enabled) {
183        // TODO: Set RSSI enabled
184        // TODO: Sets the view to be "awaiting" if not already awaiting
185    }
186    // NetworkSignalChanged callback
187    @Override
188    public void onMobileDataSignalChanged(boolean enabled, String description) {
189        // TODO: If view is in awaiting state, disable
190        Resources r = mContext.getResources();
191        // TODO: Check if RSSI is enabled
192        mRSSIState.enabled = enabled;
193        mRSSIState.iconId = (enabled ?
194                R.drawable.ic_qs_rssi_enabled :
195                R.drawable.ic_qs_rssi_normal);
196        mRSSIState.label = (enabled ?
197                description :
198                r.getString(R.string.quick_settings_rssi_emergency_only));
199        mRSSICallback.refreshView(mRSSITile, mRSSIState);
200    }
201
202    // Bluetooth
203    void addBluetoothTile(QuickSettingsTileView view, RefreshCallback cb) {
204        mBluetoothTile = view;
205        mBluetoothTile.setOnClickListener(new View.OnClickListener() {
206            @Override
207            public void onClick(View v) {
208                if (mBluetoothState.enabled) {
209                    setBluetoothState(false);
210                } else {
211                    setBluetoothState(true);
212                }
213            }
214        });
215        mBluetoothCallback = cb;
216
217        final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
218        onBluetoothStateChange(adapter.isEnabled());
219    }
220    private void setBluetoothState(boolean enabled) {
221        // TODO: Sets the view to be "awaiting" if not already awaiting
222        final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
223        if (adapter != null) {
224            if (enabled) {
225                adapter.enable();
226            } else {
227                adapter.disable();
228            }
229        }
230    }
231    // BluetoothController callback
232    @Override
233    public void onBluetoothStateChange(boolean on) {
234        // TODO: If view is in awaiting state, disable
235        Resources r = mContext.getResources();
236        mBluetoothState.enabled = on;
237        if (on) {
238            mBluetoothState.iconId = R.drawable.ic_qs_bluetooth_enabled;
239        } else {
240            mBluetoothState.iconId = R.drawable.ic_qs_bluetooth_normal;
241        }
242        mBluetoothCallback.refreshView(mBluetoothTile, mBluetoothState);
243    }
244
245    // Battery
246    void addBatteryTile(QuickSettingsTileView view, RefreshCallback cb) {
247        mBatteryTile = view;
248        mBatteryCallback = cb;
249        mBatteryCallback.refreshView(mBatteryTile, mBatteryState);
250    }
251    // BatteryController callback
252    @Override
253    public void onBatteryLevelChanged(int level, boolean pluggedIn) {
254        mBatteryState.batteryLevel = level;
255        mBatteryState.pluggedIn = pluggedIn;
256        mBatteryCallback.refreshView(mBatteryTile, mBatteryState);
257    }
258
259    // Location
260    void addLocationTile(QuickSettingsTileView view, RefreshCallback cb) {
261        mLocationTile = view;
262        mLocationCallback = cb;
263        mLocationCallback.refreshView(mLocationTile, mLocationState);
264        disableLocationTile();
265    }
266    private void enableLocationTile() {
267        mLocationTile.setVisibility(View.VISIBLE);
268    }
269    private void disableLocationTile() {
270        mLocationTile.setVisibility(View.GONE);
271    }
272    // LocationController callback
273    @Override
274    public void onLocationGpsStateChanged(boolean inUse, String description) {
275        if (inUse) {
276            mLocationState.enabled = inUse;
277            mLocationState.label = description;
278            mLocationCallback.refreshView(mLocationTile, mLocationState);
279            enableLocationTile();
280        } else {
281            disableLocationTile();
282        }
283    }
284
285}
286
287/**
288 *
289 */
290class QuickSettings {
291
292    private Context mContext;
293    private PanelBar mBar;
294    private QuickSettingsModel mModel;
295    private QuickSettingsContainerView mContainerView;
296
297    private CursorLoader mUserInfoLoader;
298
299    // The set of QuickSettingsTiles that have dynamic spans (and need to be updated on
300    // configuration change)
301    private final ArrayList<QuickSettingsTileView> mDynamicSpannedTiles =
302            new ArrayList<QuickSettingsTileView>();
303
304    public QuickSettings(Context context, QuickSettingsContainerView container) {
305        mContext = context;
306        mModel = new QuickSettingsModel(context);
307        mContainerView = container;
308
309        setupQuickSettings();
310        updateResources();
311    }
312
313    void setBar(PanelBar bar) {
314        mBar = bar;
315    }
316
317    void setup(NetworkController networkController, BluetoothController bluetoothController,
318            BatteryController batteryController, LocationController locationController) {
319        networkController.addNetworkSignalChangedCallback(mModel);
320        bluetoothController.addStateChangedCallback(mModel);
321        batteryController.addStateChangedCallback(mModel);
322        locationController.addStateChangedCallback(mModel);
323    }
324
325    private void queryForUserInformation() {
326        Uri userContactUri = Uri.withAppendedPath(
327            ContactsContract.Profile.CONTENT_URI,
328            ContactsContract.Contacts.Data.CONTENT_DIRECTORY);
329
330        String[] selectArgs = {
331            ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME,
332            ContactsContract.CommonDataKinds.Photo.PHOTO
333        };
334        String where = String.format("(%s = ? OR %s = ?) AND %s IS NULL",
335            ContactsContract.Contacts.Data.MIMETYPE,
336            ContactsContract.Contacts.Data.MIMETYPE,
337            ContactsContract.RawContacts.ACCOUNT_TYPE);
338        String[] whereArgs = {
339            ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE,
340            ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE
341        };
342
343        mUserInfoLoader = new CursorLoader(mContext, userContactUri, selectArgs, where, whereArgs,
344                null);
345        mUserInfoLoader.registerListener(0,
346                new Loader.OnLoadCompleteListener<Cursor>() {
347                    @Override
348                    public void onLoadComplete(Loader<Cursor> loader,
349                            Cursor cursor) {
350                        if (cursor.moveToFirst()) {
351                            String name = cursor.getString(0); // DISPLAY_NAME
352                            mModel.setUserTileInfo(name);
353                            /*
354                            byte[] photoData = cursor.getBlob(0);
355                            Bitmap b =
356                                BitmapFactory.decodeByteArray(photoData, 0, photoData.length);
357                             */
358                        }
359                        mUserInfoLoader.stopLoading();
360                    }
361                });
362        mUserInfoLoader.startLoading();
363    }
364
365    private void setupQuickSettings() {
366        // Setup the tiles that we are going to be showing (including the temporary ones)
367        LayoutInflater inflater = LayoutInflater.from(mContext);
368
369        addUserTiles(mContainerView, inflater);
370        addSystemTiles(mContainerView, inflater);
371        addTemporaryTiles(mContainerView, inflater);
372
373        queryForUserInformation();
374    }
375
376    private void addUserTiles(ViewGroup parent, LayoutInflater inflater) {
377        QuickSettingsTileView userTile = (QuickSettingsTileView)
378                inflater.inflate(R.layout.quick_settings_tile, parent, false);
379        userTile.setContent(R.layout.quick_settings_tile_user, inflater);
380        mModel.addUserTile(userTile, new QuickSettingsModel.RefreshCallback() {
381            @Override
382            public void refreshView(QuickSettingsTileView view, State state) {
383                TextView tv = (TextView) view.findViewById(R.id.user_textview);
384                tv.setText(state.label);
385            }
386        });
387        parent.addView(userTile);
388        mDynamicSpannedTiles.add(userTile);
389
390        // Time tile
391        QuickSettingsTileView timeTile = (QuickSettingsTileView)
392                inflater.inflate(R.layout.quick_settings_tile, parent, false);
393        timeTile.setContent(R.layout.quick_settings_tile_time, inflater);
394        parent.addView(timeTile);
395        mDynamicSpannedTiles.add(timeTile);
396
397        // Settings tile
398        QuickSettingsTileView settingsTile = (QuickSettingsTileView)
399                inflater.inflate(R.layout.quick_settings_tile, parent, false);
400        settingsTile.setContent(R.layout.quick_settings_tile_settings, inflater);
401        settingsTile.setOnClickListener(new View.OnClickListener() {
402            @Override
403            public void onClick(View v) {
404                Intent intent = new Intent(android.provider.Settings.ACTION_SETTINGS);
405                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
406                mContext.startActivity(intent);
407                mBar.collapseAllPanels(true);
408            }
409        });
410        parent.addView(settingsTile);
411        mDynamicSpannedTiles.add(settingsTile);
412    }
413
414    private void addSystemTiles(ViewGroup parent, LayoutInflater inflater) {
415        // Wi-fi
416        QuickSettingsTileView wifiTile = (QuickSettingsTileView)
417                inflater.inflate(R.layout.quick_settings_tile, parent, false);
418        wifiTile.setContent(R.layout.quick_settings_tile_wifi, inflater);
419        wifiTile.setOnClickListener(new View.OnClickListener() {
420            @Override
421            public void onClick(View v) {
422                Intent intent = new Intent(android.provider.Settings.ACTION_WIFI_SETTINGS);
423                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
424                mContext.startActivity(intent);
425                mBar.collapseAllPanels(true);
426            }
427        });
428        mModel.addWifiTile(wifiTile, new QuickSettingsModel.RefreshCallback() {
429            @Override
430            public void refreshView(QuickSettingsTileView view, State state) {
431                TextView tv = (TextView) view.findViewById(R.id.wifi_textview);
432                tv.setCompoundDrawablesRelativeWithIntrinsicBounds(0, state.iconId, 0, 0);
433                tv.setText(state.label);
434            }
435        });
436        parent.addView(wifiTile);
437
438        // RSSI
439        QuickSettingsTileView rssiTile = (QuickSettingsTileView)
440                inflater.inflate(R.layout.quick_settings_tile, parent, false);
441        rssiTile.setContent(R.layout.quick_settings_tile_rssi, inflater);
442        rssiTile.setOnClickListener(new View.OnClickListener() {
443            @Override
444            public void onClick(View v) {
445                Intent intent = new Intent(android.provider.Settings.ACTION_WIRELESS_SETTINGS);
446                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
447                mContext.startActivity(intent);
448                mBar.collapseAllPanels(true);
449            }
450        });
451        mModel.addRSSITile(rssiTile, new QuickSettingsModel.RefreshCallback() {
452            @Override
453            public void refreshView(QuickSettingsTileView view, State state) {
454                TextView tv = (TextView) view.findViewById(R.id.rssi_textview);
455                tv.setCompoundDrawablesRelativeWithIntrinsicBounds(0, state.iconId, 0, 0);
456                tv.setText(state.label);
457            }
458        });
459        parent.addView(rssiTile);
460
461        // Battery
462        QuickSettingsTileView batteryTile = (QuickSettingsTileView)
463                inflater.inflate(R.layout.quick_settings_tile, parent, false);
464        batteryTile.setContent(R.layout.quick_settings_tile_battery, inflater);
465        batteryTile.setOnClickListener(new View.OnClickListener() {
466            @Override
467            public void onClick(View v) {
468                Intent intent = new Intent(Intent.ACTION_POWER_USAGE_SUMMARY);
469                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
470                mContext.startActivity(intent);
471                mBar.collapseAllPanels(true);
472            }
473        });
474        mModel.addBatteryTile(batteryTile, new QuickSettingsModel.RefreshCallback() {
475            @Override
476            public void refreshView(QuickSettingsTileView view, State state) {
477                QuickSettingsModel.BatteryState batteryState =
478                        (QuickSettingsModel.BatteryState) state;
479                TextView tv = (TextView) view.findViewById(R.id.battery_textview);
480                ClipDrawable drawable = (ClipDrawable) tv.getCompoundDrawables()[1];
481                drawable.setLevel((int) (10000 * (batteryState.batteryLevel / 100.0f)));
482                // TODO: use format string
483                tv.setText(batteryState.batteryLevel + "%");
484            }
485        });
486        parent.addView(batteryTile);
487
488        // Airplane Mode
489        QuickSettingsTileView airplaneTile = (QuickSettingsTileView)
490                inflater.inflate(R.layout.quick_settings_tile, parent, false);
491        airplaneTile.setContent(R.layout.quick_settings_tile_airplane, inflater);
492        mModel.addAirplaneModeTile(airplaneTile, new QuickSettingsModel.RefreshCallback() {
493            @Override
494            public void refreshView(QuickSettingsTileView view, State state) {
495                TextView tv = (TextView) view.findViewById(R.id.airplane_mode_textview);
496                tv.setCompoundDrawablesRelativeWithIntrinsicBounds(0, state.iconId, 0, 0);
497            }
498        });
499        parent.addView(airplaneTile);
500
501        // Bluetooth
502        QuickSettingsTileView bluetoothTile = (QuickSettingsTileView)
503                inflater.inflate(R.layout.quick_settings_tile, parent, false);
504        bluetoothTile.setContent(R.layout.quick_settings_tile_bluetooth, inflater);
505        mModel.addBluetoothTile(bluetoothTile, new QuickSettingsModel.RefreshCallback() {
506            @Override
507            public void refreshView(QuickSettingsTileView view, State state) {
508                TextView tv = (TextView) view.findViewById(R.id.bluetooth_textview);
509                tv.setCompoundDrawablesRelativeWithIntrinsicBounds(0, state.iconId, 0, 0);
510            }
511        });
512        parent.addView(bluetoothTile);
513
514        // Brightness
515        QuickSettingsTileView brightnessTile = (QuickSettingsTileView)
516                inflater.inflate(R.layout.quick_settings_tile, parent, false);
517        brightnessTile.setContent(R.layout.quick_settings_tile_brightness, inflater);
518        brightnessTile.setOnClickListener(new View.OnClickListener() {
519            @Override
520            public void onClick(View v) {
521                Intent intent = new Intent(android.provider.Settings.ACTION_DISPLAY_SETTINGS);
522                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
523                mContext.startActivity(intent);
524                mBar.collapseAllPanels(true);
525            }
526        });
527        parent.addView(brightnessTile);
528    }
529
530    private void addTemporaryTiles(final ViewGroup parent, final LayoutInflater inflater) {
531        // Location
532        QuickSettingsTileView locationTile = (QuickSettingsTileView)
533                inflater.inflate(R.layout.quick_settings_tile, parent, false);
534        locationTile.setContent(R.layout.quick_settings_tile_location, inflater);
535        locationTile.setOnClickListener(new View.OnClickListener() {
536            @Override
537            public void onClick(View v) {
538                Intent intent =
539                        new Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS);
540                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
541                mContext.startActivity(intent);
542                mBar.collapseAllPanels(true);
543            }
544        });
545        mModel.addLocationTile(locationTile, new QuickSettingsModel.RefreshCallback() {
546            @Override
547            public void refreshView(QuickSettingsTileView view, State state) {
548                TextView tv = (TextView) view.findViewById(R.id.location_textview);
549                tv.setText(state.label);
550            }
551        });
552        parent.addView(locationTile);
553
554        /*
555        QuickSettingsTileView mediaTile = (QuickSettingsTileView)
556                inflater.inflate(R.layout.quick_settings_tile, parent, false);
557        mediaTile.setContent(R.layout.quick_settings_tile_media, inflater);
558        parent.addView(mediaTile);
559        QuickSettingsTileView imeTile = (QuickSettingsTileView)
560                inflater.inflate(R.layout.quick_settings_tile, parent, false);
561        imeTile.setContent(R.layout.quick_settings_tile_ime, inflater);
562        imeTile.setOnClickListener(new View.OnClickListener() {
563            @Override
564            public void onClick(View v) {
565                parent.removeViewAt(0);
566            }
567        });
568        parent.addView(imeTile);
569        */
570    }
571
572    void updateResources() {
573        Resources r = mContext.getResources();
574
575        // Update the User, Time, and Settings tiles spans, and reset everything else
576        int span = r.getInteger(R.integer.quick_settings_user_time_settings_tile_span);
577        for (QuickSettingsTileView v : mDynamicSpannedTiles) {
578            v.setColumnSpan(span);
579        }
580        mContainerView.requestLayout();
581    }
582}