Home | History | Annotate | Download | only in phone
      1 /*
      2  * Copyright (C) 2014 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.systemui.statusbar.phone;
     18 
     19 import android.app.ActivityManager;
     20 import android.app.PendingIntent;
     21 import android.content.ComponentName;
     22 import android.content.Context;
     23 import android.content.Intent;
     24 import android.content.res.Resources;
     25 import android.os.Handler;
     26 import android.os.HandlerThread;
     27 import android.os.Looper;
     28 import android.os.Process;
     29 import android.os.UserHandle;
     30 import android.os.UserManager;
     31 import android.provider.Settings;
     32 import android.provider.Settings.Secure;
     33 import android.service.quicksettings.Tile;
     34 import android.text.TextUtils;
     35 import android.util.Log;
     36 import android.view.View;
     37 
     38 import com.android.systemui.R;
     39 import com.android.systemui.qs.QSTile;
     40 import com.android.systemui.qs.external.CustomTile;
     41 import com.android.systemui.qs.external.TileLifecycleManager;
     42 import com.android.systemui.qs.external.TileServices;
     43 import com.android.systemui.qs.tiles.AirplaneModeTile;
     44 import com.android.systemui.qs.tiles.BatteryTile;
     45 import com.android.systemui.qs.tiles.BluetoothTile;
     46 import com.android.systemui.qs.tiles.CastTile;
     47 import com.android.systemui.qs.tiles.CellularTile;
     48 import com.android.systemui.qs.tiles.ColorInversionTile;
     49 import com.android.systemui.qs.tiles.DataSaverTile;
     50 import com.android.systemui.qs.tiles.DndTile;
     51 import com.android.systemui.qs.tiles.FlashlightTile;
     52 import com.android.systemui.qs.tiles.HotspotTile;
     53 import com.android.systemui.qs.tiles.IntentTile;
     54 import com.android.systemui.qs.tiles.LocationTile;
     55 import com.android.systemui.qs.tiles.NightDisplayTile;
     56 import com.android.systemui.qs.tiles.RotationLockTile;
     57 import com.android.systemui.qs.tiles.UserTile;
     58 import com.android.systemui.qs.tiles.WifiTile;
     59 import com.android.systemui.qs.tiles.WorkModeTile;
     60 import com.android.systemui.statusbar.policy.BatteryController;
     61 import com.android.systemui.statusbar.policy.BluetoothController;
     62 import com.android.systemui.statusbar.policy.CastController;
     63 import com.android.systemui.statusbar.policy.NextAlarmController;
     64 import com.android.systemui.statusbar.policy.FlashlightController;
     65 import com.android.systemui.statusbar.policy.HotspotController;
     66 import com.android.systemui.statusbar.policy.KeyguardMonitor;
     67 import com.android.systemui.statusbar.policy.LocationController;
     68 import com.android.systemui.statusbar.policy.NetworkController;
     69 import com.android.systemui.statusbar.policy.RotationLockController;
     70 import com.android.systemui.statusbar.policy.SecurityController;
     71 import com.android.systemui.statusbar.policy.UserInfoController;
     72 import com.android.systemui.statusbar.policy.UserSwitcherController;
     73 import com.android.systemui.statusbar.policy.ZenModeController;
     74 import com.android.systemui.tuner.TunerService;
     75 import com.android.systemui.tuner.TunerService.Tunable;
     76 
     77 import java.util.ArrayList;
     78 import java.util.Arrays;
     79 import java.util.Collection;
     80 import java.util.LinkedHashMap;
     81 import java.util.List;
     82 import java.util.Map;
     83 
     84 /** Platform implementation of the quick settings tile host **/
     85 public class QSTileHost implements QSTile.Host, Tunable {
     86     private static final String TAG = "QSTileHost";
     87     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
     88 
     89     public static final String TILES_SETTING = Secure.QS_TILES;
     90 
     91     private final Context mContext;
     92     private final PhoneStatusBar mStatusBar;
     93     private final LinkedHashMap<String, QSTile<?>> mTiles = new LinkedHashMap<>();
     94     protected final ArrayList<String> mTileSpecs = new ArrayList<>();
     95     private final BluetoothController mBluetooth;
     96     private final LocationController mLocation;
     97     private final RotationLockController mRotation;
     98     private final NetworkController mNetwork;
     99     private final ZenModeController mZen;
    100     private final HotspotController mHotspot;
    101     private final CastController mCast;
    102     private final Looper mLooper;
    103     private final FlashlightController mFlashlight;
    104     private final UserSwitcherController mUserSwitcherController;
    105     private final UserInfoController mUserInfoController;
    106     private final KeyguardMonitor mKeyguard;
    107     private final SecurityController mSecurity;
    108     private final BatteryController mBattery;
    109     private final StatusBarIconController mIconController;
    110     private final TileServices mServices;
    111 
    112     private final List<Callback> mCallbacks = new ArrayList<>();
    113     private final AutoTileManager mAutoTiles;
    114     private final ManagedProfileController mProfileController;
    115     private final NextAlarmController mNextAlarmController;
    116     private View mHeader;
    117     private int mCurrentUser;
    118 
    119     public QSTileHost(Context context, PhoneStatusBar statusBar,
    120             BluetoothController bluetooth, LocationController location,
    121             RotationLockController rotation, NetworkController network,
    122             ZenModeController zen, HotspotController hotspot,
    123             CastController cast, FlashlightController flashlight,
    124             UserSwitcherController userSwitcher, UserInfoController userInfo,
    125             KeyguardMonitor keyguard, SecurityController security,
    126             BatteryController battery, StatusBarIconController iconController,
    127             NextAlarmController nextAlarmController) {
    128         mContext = context;
    129         mStatusBar = statusBar;
    130         mBluetooth = bluetooth;
    131         mLocation = location;
    132         mRotation = rotation;
    133         mNetwork = network;
    134         mZen = zen;
    135         mHotspot = hotspot;
    136         mCast = cast;
    137         mFlashlight = flashlight;
    138         mUserSwitcherController = userSwitcher;
    139         mUserInfoController = userInfo;
    140         mKeyguard = keyguard;
    141         mSecurity = security;
    142         mBattery = battery;
    143         mIconController = iconController;
    144         mNextAlarmController = nextAlarmController;
    145         mProfileController = new ManagedProfileController(this);
    146 
    147         final HandlerThread ht = new HandlerThread(QSTileHost.class.getSimpleName(),
    148                 Process.THREAD_PRIORITY_BACKGROUND);
    149         ht.start();
    150         mLooper = ht.getLooper();
    151 
    152         mServices = new TileServices(this, mLooper);
    153 
    154         TunerService.get(mContext).addTunable(this, TILES_SETTING);
    155         // AutoTileManager can modify mTiles so make sure mTiles has already been initialized.
    156         mAutoTiles = new AutoTileManager(context, this);
    157     }
    158 
    159     public NextAlarmController getNextAlarmController() {
    160         return mNextAlarmController;
    161     }
    162 
    163     public void setHeaderView(View view) {
    164         mHeader = view;
    165     }
    166 
    167     public PhoneStatusBar getPhoneStatusBar() {
    168         return mStatusBar;
    169     }
    170 
    171     public void destroy() {
    172         mAutoTiles.destroy();
    173         TunerService.get(mContext).removeTunable(this);
    174     }
    175 
    176     @Override
    177     public void addCallback(Callback callback) {
    178         mCallbacks.add(callback);
    179     }
    180 
    181     @Override
    182     public void removeCallback(Callback callback) {
    183         mCallbacks.remove(callback);
    184     }
    185 
    186     @Override
    187     public Collection<QSTile<?>> getTiles() {
    188         return mTiles.values();
    189     }
    190 
    191     @Override
    192     public void startActivityDismissingKeyguard(final Intent intent) {
    193         mStatusBar.postStartActivityDismissingKeyguard(intent, 0);
    194     }
    195 
    196     @Override
    197     public void startActivityDismissingKeyguard(PendingIntent intent) {
    198         mStatusBar.postStartActivityDismissingKeyguard(intent);
    199     }
    200 
    201     @Override
    202     public void startRunnableDismissingKeyguard(Runnable runnable) {
    203         mStatusBar.postQSRunnableDismissingKeyguard(runnable);
    204     }
    205 
    206     @Override
    207     public void warn(String message, Throwable t) {
    208         // already logged
    209     }
    210 
    211     public void animateToggleQSExpansion() {
    212         // TODO: Better path to animated panel expansion.
    213         mHeader.callOnClick();
    214     }
    215 
    216     @Override
    217     public void collapsePanels() {
    218         mStatusBar.postAnimateCollapsePanels();
    219     }
    220 
    221     @Override
    222     public void openPanels() {
    223         mStatusBar.postAnimateOpenPanels();
    224     }
    225 
    226     @Override
    227     public Looper getLooper() {
    228         return mLooper;
    229     }
    230 
    231     @Override
    232     public Context getContext() {
    233         return mContext;
    234     }
    235 
    236     @Override
    237     public BluetoothController getBluetoothController() {
    238         return mBluetooth;
    239     }
    240 
    241     @Override
    242     public LocationController getLocationController() {
    243         return mLocation;
    244     }
    245 
    246     @Override
    247     public RotationLockController getRotationLockController() {
    248         return mRotation;
    249     }
    250 
    251     @Override
    252     public NetworkController getNetworkController() {
    253         return mNetwork;
    254     }
    255 
    256     @Override
    257     public ZenModeController getZenModeController() {
    258         return mZen;
    259     }
    260 
    261     @Override
    262     public HotspotController getHotspotController() {
    263         return mHotspot;
    264     }
    265 
    266     @Override
    267     public CastController getCastController() {
    268         return mCast;
    269     }
    270 
    271     @Override
    272     public FlashlightController getFlashlightController() {
    273         return mFlashlight;
    274     }
    275 
    276     @Override
    277     public KeyguardMonitor getKeyguardMonitor() {
    278         return mKeyguard;
    279     }
    280 
    281     @Override
    282     public UserSwitcherController getUserSwitcherController() {
    283         return mUserSwitcherController;
    284     }
    285 
    286     @Override
    287     public UserInfoController getUserInfoController() {
    288         return mUserInfoController;
    289     }
    290 
    291     @Override
    292     public BatteryController getBatteryController() {
    293         return mBattery;
    294     }
    295 
    296     public SecurityController getSecurityController() {
    297         return mSecurity;
    298     }
    299 
    300     public TileServices getTileServices() {
    301         return mServices;
    302     }
    303 
    304     public StatusBarIconController getIconController() {
    305         return mIconController;
    306     }
    307 
    308     public ManagedProfileController getManagedProfileController() {
    309         return mProfileController;
    310     }
    311 
    312     @Override
    313     public void onTuningChanged(String key, String newValue) {
    314         if (!TILES_SETTING.equals(key)) {
    315             return;
    316         }
    317         if (DEBUG) Log.d(TAG, "Recreating tiles");
    318         if (newValue == null && UserManager.isDeviceInDemoMode(mContext)) {
    319             newValue = mContext.getResources().getString(R.string.quick_settings_tiles_retail_mode);
    320         }
    321         final List<String> tileSpecs = loadTileSpecs(mContext, newValue);
    322         int currentUser = ActivityManager.getCurrentUser();
    323         if (tileSpecs.equals(mTileSpecs) && currentUser == mCurrentUser) return;
    324         for (Map.Entry<String, QSTile<?>> tile : mTiles.entrySet()) {
    325             if (!tileSpecs.contains(tile.getKey())) {
    326                 if (DEBUG) Log.d(TAG, "Destroying tile: " + tile.getKey());
    327                 tile.getValue().destroy();
    328             }
    329         }
    330         final LinkedHashMap<String, QSTile<?>> newTiles = new LinkedHashMap<>();
    331         for (String tileSpec : tileSpecs) {
    332             QSTile<?> tile = mTiles.get(tileSpec);
    333             if (tile != null && (!(tile instanceof CustomTile)
    334                     || ((CustomTile) tile).getUser() == currentUser)) {
    335                 if (DEBUG) Log.d(TAG, "Adding " + tile);
    336                 tile.removeCallbacks();
    337                 if (!(tile instanceof CustomTile) && mCurrentUser != currentUser) {
    338                     tile.userSwitch(currentUser);
    339                 }
    340                 newTiles.put(tileSpec, tile);
    341             } else {
    342                 if (DEBUG) Log.d(TAG, "Creating tile: " + tileSpec);
    343                 try {
    344                     tile = createTile(tileSpec);
    345                     if (tile != null && tile.isAvailable()) {
    346                         tile.setTileSpec(tileSpec);
    347                         newTiles.put(tileSpec, tile);
    348                     }
    349                 } catch (Throwable t) {
    350                     Log.w(TAG, "Error creating tile for spec: " + tileSpec, t);
    351                 }
    352             }
    353         }
    354         mCurrentUser = currentUser;
    355         mTileSpecs.clear();
    356         mTileSpecs.addAll(tileSpecs);
    357         mTiles.clear();
    358         mTiles.putAll(newTiles);
    359         for (int i = 0; i < mCallbacks.size(); i++) {
    360             mCallbacks.get(i).onTilesChanged();
    361         }
    362     }
    363 
    364     @Override
    365     public void removeTile(String tileSpec) {
    366         ArrayList<String> specs = new ArrayList<>(mTileSpecs);
    367         specs.remove(tileSpec);
    368         Settings.Secure.putStringForUser(mContext.getContentResolver(), TILES_SETTING,
    369                 TextUtils.join(",", specs), ActivityManager.getCurrentUser());
    370     }
    371 
    372     public void addTile(String spec) {
    373         final String setting = Settings.Secure.getStringForUser(mContext.getContentResolver(),
    374                 TILES_SETTING, ActivityManager.getCurrentUser());
    375         final List<String> tileSpecs = loadTileSpecs(mContext, setting);
    376         if (tileSpecs.contains(spec)) {
    377             return;
    378         }
    379         tileSpecs.add(spec);
    380         Settings.Secure.putStringForUser(mContext.getContentResolver(), TILES_SETTING,
    381                 TextUtils.join(",", tileSpecs), ActivityManager.getCurrentUser());
    382     }
    383 
    384     public void addTile(ComponentName tile) {
    385         List<String> newSpecs = new ArrayList<>(mTileSpecs);
    386         newSpecs.add(0, CustomTile.toSpec(tile));
    387         changeTiles(mTileSpecs, newSpecs);
    388     }
    389 
    390     public void removeTile(ComponentName tile) {
    391         List<String> newSpecs = new ArrayList<>(mTileSpecs);
    392         newSpecs.remove(CustomTile.toSpec(tile));
    393         changeTiles(mTileSpecs, newSpecs);
    394     }
    395 
    396     public void changeTiles(List<String> previousTiles, List<String> newTiles) {
    397         final int NP = previousTiles.size();
    398         final int NA = newTiles.size();
    399         for (int i = 0; i < NP; i++) {
    400             String tileSpec = previousTiles.get(i);
    401             if (!tileSpec.startsWith(CustomTile.PREFIX)) continue;
    402             if (!newTiles.contains(tileSpec)) {
    403                 ComponentName component = CustomTile.getComponentFromSpec(tileSpec);
    404                 Intent intent = new Intent().setComponent(component);
    405                 TileLifecycleManager lifecycleManager = new TileLifecycleManager(new Handler(),
    406                         mContext, mServices, new Tile(), intent,
    407                         new UserHandle(ActivityManager.getCurrentUser()));
    408                 lifecycleManager.onStopListening();
    409                 lifecycleManager.onTileRemoved();
    410                 TileLifecycleManager.setTileAdded(mContext, component, false);
    411                 lifecycleManager.flushMessagesAndUnbind();
    412             }
    413         }
    414         if (DEBUG) Log.d(TAG, "saveCurrentTiles " + newTiles);
    415         Secure.putStringForUser(getContext().getContentResolver(), QSTileHost.TILES_SETTING,
    416                 TextUtils.join(",", newTiles), ActivityManager.getCurrentUser());
    417     }
    418 
    419     public QSTile<?> createTile(String tileSpec) {
    420         if (tileSpec.equals("wifi")) return new WifiTile(this);
    421         else if (tileSpec.equals("bt")) return new BluetoothTile(this);
    422         else if (tileSpec.equals("cell")) return new CellularTile(this);
    423         else if (tileSpec.equals("dnd")) return new DndTile(this);
    424         else if (tileSpec.equals("inversion")) return new ColorInversionTile(this);
    425         else if (tileSpec.equals("airplane")) return new AirplaneModeTile(this);
    426         else if (tileSpec.equals("work")) return new WorkModeTile(this);
    427         else if (tileSpec.equals("rotation")) return new RotationLockTile(this);
    428         else if (tileSpec.equals("flashlight")) return new FlashlightTile(this);
    429         else if (tileSpec.equals("location")) return new LocationTile(this);
    430         else if (tileSpec.equals("cast")) return new CastTile(this);
    431         else if (tileSpec.equals("hotspot")) return new HotspotTile(this);
    432         else if (tileSpec.equals("user")) return new UserTile(this);
    433         else if (tileSpec.equals("battery")) return new BatteryTile(this);
    434         else if (tileSpec.equals("saver")) return new DataSaverTile(this);
    435         else if (tileSpec.equals("night")) return new NightDisplayTile(this);
    436         // Intent tiles.
    437         else if (tileSpec.startsWith(IntentTile.PREFIX)) return IntentTile.create(this,tileSpec);
    438         else if (tileSpec.startsWith(CustomTile.PREFIX)) return CustomTile.create(this,tileSpec);
    439         else {
    440             Log.w(TAG, "Bad tile spec: " + tileSpec);
    441             return null;
    442         }
    443     }
    444 
    445     protected List<String> loadTileSpecs(Context context, String tileList) {
    446         final Resources res = context.getResources();
    447         final String defaultTileList = res.getString(R.string.quick_settings_tiles_default);
    448         if (tileList == null) {
    449             tileList = res.getString(R.string.quick_settings_tiles);
    450             if (DEBUG) Log.d(TAG, "Loaded tile specs from config: " + tileList);
    451         } else {
    452             if (DEBUG) Log.d(TAG, "Loaded tile specs from setting: " + tileList);
    453         }
    454         final ArrayList<String> tiles = new ArrayList<String>();
    455         boolean addedDefault = false;
    456         for (String tile : tileList.split(",")) {
    457             tile = tile.trim();
    458             if (tile.isEmpty()) continue;
    459             if (tile.equals("default")) {
    460                 if (!addedDefault) {
    461                     tiles.addAll(Arrays.asList(defaultTileList.split(",")));
    462                     addedDefault = true;
    463                 }
    464             } else {
    465                 tiles.add(tile);
    466             }
    467         }
    468         return tiles;
    469     }
    470 }
    471