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.provider.Settings;
     31 import android.provider.Settings.Secure;
     32 import android.service.quicksettings.Tile;
     33 import android.text.TextUtils;
     34 import android.util.Log;
     35 import android.view.View;
     36 
     37 import com.android.systemui.R;
     38 import com.android.systemui.qs.QSTile;
     39 import com.android.systemui.qs.external.CustomTile;
     40 import com.android.systemui.qs.external.TileLifecycleManager;
     41 import com.android.systemui.qs.external.TileServices;
     42 import com.android.systemui.qs.tiles.AirplaneModeTile;
     43 import com.android.systemui.qs.tiles.BatteryTile;
     44 import com.android.systemui.qs.tiles.BluetoothTile;
     45 import com.android.systemui.qs.tiles.CastTile;
     46 import com.android.systemui.qs.tiles.CellularTile;
     47 import com.android.systemui.qs.tiles.ColorInversionTile;
     48 import com.android.systemui.qs.tiles.DataSaverTile;
     49 import com.android.systemui.qs.tiles.DndTile;
     50 import com.android.systemui.qs.tiles.FlashlightTile;
     51 import com.android.systemui.qs.tiles.HotspotTile;
     52 import com.android.systemui.qs.tiles.IntentTile;
     53 import com.android.systemui.qs.tiles.LocationTile;
     54 import com.android.systemui.qs.tiles.RotationLockTile;
     55 import com.android.systemui.qs.tiles.UserTile;
     56 import com.android.systemui.qs.tiles.WifiTile;
     57 import com.android.systemui.qs.tiles.WorkModeTile;
     58 import com.android.systemui.statusbar.policy.BatteryController;
     59 import com.android.systemui.statusbar.policy.BluetoothController;
     60 import com.android.systemui.statusbar.policy.CastController;
     61 import com.android.systemui.statusbar.policy.NextAlarmController;
     62 import com.android.systemui.statusbar.policy.NightModeController;
     63 import com.android.systemui.statusbar.policy.FlashlightController;
     64 import com.android.systemui.statusbar.policy.HotspotController;
     65 import com.android.systemui.statusbar.policy.KeyguardMonitor;
     66 import com.android.systemui.statusbar.policy.LocationController;
     67 import com.android.systemui.statusbar.policy.NetworkController;
     68 import com.android.systemui.statusbar.policy.RotationLockController;
     69 import com.android.systemui.statusbar.policy.SecurityController;
     70 import com.android.systemui.statusbar.policy.UserInfoController;
     71 import com.android.systemui.statusbar.policy.UserSwitcherController;
     72 import com.android.systemui.statusbar.policy.ZenModeController;
     73 import com.android.systemui.tuner.NightModeTile;
     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 = "sysui_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 NightModeController mNightModeController;
    114     private final AutoTileManager mAutoTiles;
    115     private final ManagedProfileController mProfileController;
    116     private final NextAlarmController mNextAlarmController;
    117     private View mHeader;
    118     private int mCurrentUser;
    119 
    120     public QSTileHost(Context context, PhoneStatusBar statusBar,
    121             BluetoothController bluetooth, LocationController location,
    122             RotationLockController rotation, NetworkController network,
    123             ZenModeController zen, HotspotController hotspot,
    124             CastController cast, FlashlightController flashlight,
    125             UserSwitcherController userSwitcher, UserInfoController userInfo,
    126             KeyguardMonitor keyguard, SecurityController security,
    127             BatteryController battery, StatusBarIconController iconController,
    128             NextAlarmController nextAlarmController) {
    129         mContext = context;
    130         mStatusBar = statusBar;
    131         mBluetooth = bluetooth;
    132         mLocation = location;
    133         mRotation = rotation;
    134         mNetwork = network;
    135         mZen = zen;
    136         mHotspot = hotspot;
    137         mCast = cast;
    138         mFlashlight = flashlight;
    139         mUserSwitcherController = userSwitcher;
    140         mUserInfoController = userInfo;
    141         mKeyguard = keyguard;
    142         mSecurity = security;
    143         mBattery = battery;
    144         mIconController = iconController;
    145         mNextAlarmController = nextAlarmController;
    146         mNightModeController = new NightModeController(mContext, true);
    147         mProfileController = new ManagedProfileController(this);
    148 
    149         final HandlerThread ht = new HandlerThread(QSTileHost.class.getSimpleName(),
    150                 Process.THREAD_PRIORITY_BACKGROUND);
    151         ht.start();
    152         mLooper = ht.getLooper();
    153 
    154         mServices = new TileServices(this, mLooper);
    155 
    156         TunerService.get(mContext).addTunable(this, TILES_SETTING);
    157         // AutoTileManager can modify mTiles so make sure mTiles has already been initialized.
    158         mAutoTiles = new AutoTileManager(context, this);
    159     }
    160 
    161     public NextAlarmController getNextAlarmController() {
    162         return mNextAlarmController;
    163     }
    164 
    165     public void setHeaderView(View view) {
    166         mHeader = view;
    167     }
    168 
    169     public PhoneStatusBar getPhoneStatusBar() {
    170         return mStatusBar;
    171     }
    172 
    173     public void destroy() {
    174         mAutoTiles.destroy();
    175         TunerService.get(mContext).removeTunable(this);
    176     }
    177 
    178     @Override
    179     public void addCallback(Callback callback) {
    180         mCallbacks.add(callback);
    181     }
    182 
    183     @Override
    184     public void removeCallback(Callback callback) {
    185         mCallbacks.remove(callback);
    186     }
    187 
    188     @Override
    189     public Collection<QSTile<?>> getTiles() {
    190         return mTiles.values();
    191     }
    192 
    193     @Override
    194     public void startActivityDismissingKeyguard(final Intent intent) {
    195         mStatusBar.postStartActivityDismissingKeyguard(intent, 0);
    196     }
    197 
    198     @Override
    199     public void startActivityDismissingKeyguard(PendingIntent intent) {
    200         mStatusBar.postStartActivityDismissingKeyguard(intent);
    201     }
    202 
    203     @Override
    204     public void startRunnableDismissingKeyguard(Runnable runnable) {
    205         mStatusBar.postQSRunnableDismissingKeyguard(runnable);
    206     }
    207 
    208     @Override
    209     public void warn(String message, Throwable t) {
    210         // already logged
    211     }
    212 
    213     public void animateToggleQSExpansion() {
    214         // TODO: Better path to animated panel expansion.
    215         mHeader.callOnClick();
    216     }
    217 
    218     @Override
    219     public void collapsePanels() {
    220         mStatusBar.postAnimateCollapsePanels();
    221     }
    222 
    223     @Override
    224     public void openPanels() {
    225         mStatusBar.postAnimateOpenPanels();
    226     }
    227 
    228     @Override
    229     public Looper getLooper() {
    230         return mLooper;
    231     }
    232 
    233     @Override
    234     public Context getContext() {
    235         return mContext;
    236     }
    237 
    238     @Override
    239     public BluetoothController getBluetoothController() {
    240         return mBluetooth;
    241     }
    242 
    243     @Override
    244     public LocationController getLocationController() {
    245         return mLocation;
    246     }
    247 
    248     @Override
    249     public RotationLockController getRotationLockController() {
    250         return mRotation;
    251     }
    252 
    253     @Override
    254     public NetworkController getNetworkController() {
    255         return mNetwork;
    256     }
    257 
    258     @Override
    259     public ZenModeController getZenModeController() {
    260         return mZen;
    261     }
    262 
    263     @Override
    264     public HotspotController getHotspotController() {
    265         return mHotspot;
    266     }
    267 
    268     @Override
    269     public CastController getCastController() {
    270         return mCast;
    271     }
    272 
    273     @Override
    274     public FlashlightController getFlashlightController() {
    275         return mFlashlight;
    276     }
    277 
    278     @Override
    279     public KeyguardMonitor getKeyguardMonitor() {
    280         return mKeyguard;
    281     }
    282 
    283     @Override
    284     public UserSwitcherController getUserSwitcherController() {
    285         return mUserSwitcherController;
    286     }
    287 
    288     @Override
    289     public UserInfoController getUserInfoController() {
    290         return mUserInfoController;
    291     }
    292 
    293     @Override
    294     public BatteryController getBatteryController() {
    295         return mBattery;
    296     }
    297 
    298     public SecurityController getSecurityController() {
    299         return mSecurity;
    300     }
    301 
    302     public TileServices getTileServices() {
    303         return mServices;
    304     }
    305 
    306     public StatusBarIconController getIconController() {
    307         return mIconController;
    308     }
    309 
    310     public NightModeController getNightModeController() {
    311         return mNightModeController;
    312     }
    313 
    314     public ManagedProfileController getManagedProfileController() {
    315         return mProfileController;
    316     }
    317 
    318     @Override
    319     public void onTuningChanged(String key, String newValue) {
    320         if (!TILES_SETTING.equals(key)) {
    321             return;
    322         }
    323         if (DEBUG) Log.d(TAG, "Recreating tiles");
    324         final List<String> tileSpecs = loadTileSpecs(mContext, newValue);
    325         int currentUser = ActivityManager.getCurrentUser();
    326         if (tileSpecs.equals(mTileSpecs) && currentUser == mCurrentUser) return;
    327         for (Map.Entry<String, QSTile<?>> tile : mTiles.entrySet()) {
    328             if (!tileSpecs.contains(tile.getKey())) {
    329                 if (DEBUG) Log.d(TAG, "Destroying tile: " + tile.getKey());
    330                 tile.getValue().destroy();
    331             }
    332         }
    333         final LinkedHashMap<String, QSTile<?>> newTiles = new LinkedHashMap<>();
    334         for (String tileSpec : tileSpecs) {
    335             QSTile<?> tile = mTiles.get(tileSpec);
    336             if (tile != null && (!(tile instanceof CustomTile)
    337                     || ((CustomTile) tile).getUser() == currentUser)) {
    338                 if (DEBUG) Log.d(TAG, "Adding " + tile);
    339                 tile.removeCallbacks();
    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(component), intent,
    407                         new UserHandle(ActivityManager.getCurrentUser()));
    408                 lifecycleManager.onStopListening();
    409                 lifecycleManager.onTileRemoved();
    410                 lifecycleManager.flushMessagesAndUnbind();
    411             }
    412         }
    413         for (int i = 0; i < NA; i++) {
    414             String tileSpec = newTiles.get(i);
    415             if (!tileSpec.startsWith(CustomTile.PREFIX)) continue;
    416             if (!previousTiles.contains(tileSpec)) {
    417                 ComponentName component = CustomTile.getComponentFromSpec(tileSpec);
    418                 Intent intent = new Intent().setComponent(component);
    419                 TileLifecycleManager lifecycleManager = new TileLifecycleManager(new Handler(),
    420                         mContext, mServices, new Tile(component), intent,
    421                         new UserHandle(ActivityManager.getCurrentUser()));
    422                 lifecycleManager.onTileAdded();
    423                 lifecycleManager.flushMessagesAndUnbind();
    424             }
    425         }
    426         if (DEBUG) Log.d(TAG, "saveCurrentTiles " + newTiles);
    427         Secure.putStringForUser(getContext().getContentResolver(), QSTileHost.TILES_SETTING,
    428                 TextUtils.join(",", newTiles), ActivityManager.getCurrentUser());
    429     }
    430 
    431     public QSTile<?> createTile(String tileSpec) {
    432         if (tileSpec.equals("wifi")) return new WifiTile(this);
    433         else if (tileSpec.equals("bt")) return new BluetoothTile(this);
    434         else if (tileSpec.equals("cell")) return new CellularTile(this);
    435         else if (tileSpec.equals("dnd")) return new DndTile(this);
    436         else if (tileSpec.equals("inversion")) return new ColorInversionTile(this);
    437         else if (tileSpec.equals("airplane")) return new AirplaneModeTile(this);
    438         else if (tileSpec.equals("work")) return new WorkModeTile(this);
    439         else if (tileSpec.equals("rotation")) return new RotationLockTile(this);
    440         else if (tileSpec.equals("flashlight")) return new FlashlightTile(this);
    441         else if (tileSpec.equals("location")) return new LocationTile(this);
    442         else if (tileSpec.equals("cast")) return new CastTile(this);
    443         else if (tileSpec.equals("hotspot")) return new HotspotTile(this);
    444         else if (tileSpec.equals("user")) return new UserTile(this);
    445         else if (tileSpec.equals("battery")) return new BatteryTile(this);
    446         else if (tileSpec.equals("saver")) return new DataSaverTile(this);
    447         else if (tileSpec.equals(NightModeTile.NIGHT_MODE_SPEC))
    448             return new NightModeTile(this);
    449         // Intent tiles.
    450         else if (tileSpec.startsWith(IntentTile.PREFIX)) return IntentTile.create(this,tileSpec);
    451         else if (tileSpec.startsWith(CustomTile.PREFIX)) return CustomTile.create(this,tileSpec);
    452         else {
    453             Log.w(TAG, "Bad tile spec: " + tileSpec);
    454             return null;
    455         }
    456     }
    457 
    458     protected List<String> loadTileSpecs(Context context, String tileList) {
    459         final Resources res = context.getResources();
    460         final String defaultTileList = res.getString(R.string.quick_settings_tiles_default);
    461         if (tileList == null) {
    462             tileList = res.getString(R.string.quick_settings_tiles);
    463             if (DEBUG) Log.d(TAG, "Loaded tile specs from config: " + tileList);
    464         } else {
    465             if (DEBUG) Log.d(TAG, "Loaded tile specs from setting: " + tileList);
    466         }
    467         final ArrayList<String> tiles = new ArrayList<String>();
    468         boolean addedDefault = false;
    469         for (String tile : tileList.split(",")) {
    470             tile = tile.trim();
    471             if (tile.isEmpty()) continue;
    472             if (tile.equals("default")) {
    473                 if (!addedDefault) {
    474                     tiles.addAll(Arrays.asList(defaultTileList.split(",")));
    475                     addedDefault = true;
    476                 }
    477             } else {
    478                 tiles.add(tile);
    479             }
    480         }
    481         return tiles;
    482     }
    483 }
    484