diff options
author | John Spurlock <jspurlock@google.com> | 2014-08-10 18:04:16 -0400 |
---|---|---|
committer | John Spurlock <jspurlock@google.com> | 2014-08-11 10:19:21 -0400 |
commit | bceed060f0090a4f86418c4515128d5ec8ebdd4a (patch) | |
tree | e772b34120b40d046a370aa9fb9c3e253011c131 /packages/SystemUI/src | |
parent | 88992bc90c874eb0eb159b3ea37659a8b83bbee0 (diff) | |
download | frameworks_base-bceed060f0090a4f86418c4515128d5ec8ebdd4a.zip frameworks_base-bceed060f0090a4f86418c4515128d5ec8ebdd4a.tar.gz frameworks_base-bceed060f0090a4f86418c4515128d5ec8ebdd4a.tar.bz2 |
QS: Fix some QS layout issues.
- Make the tile list configurable for testing.
- Support an external tile backed by a sticky broadcast intent.
- Ensure tiles clean up properly when no longer needed.
Bug:16818269
Bug:16822505
Change-Id: Ie24f878aae0d19c7f1feca4c519d10667023bef3
Diffstat (limited to 'packages/SystemUI/src')
11 files changed, 381 insertions, 47 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java index 3679b4c..cbf6e29 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java @@ -37,6 +37,7 @@ import com.android.systemui.settings.ToggleSlider; import com.android.systemui.statusbar.phone.QSTileHost; import java.util.ArrayList; +import java.util.Collection; /** View that represents the quick settings tile panel. **/ public class QSPanel extends ViewGroup { @@ -186,12 +187,25 @@ public class QSPanel extends ViewGroup { v.setVisibility(visible ? VISIBLE : GONE); } - public void addTile(final QSTile<?> tile) { + public void setTiles(Collection<QSTile<?>> tiles) { + for (TileRecord record : mRecords) { + removeView(record.tileView); + } + mRecords.clear(); + for (QSTile<?> tile : tiles) { + addTile(tile); + } + if (isShowingDetail()) { + mDetail.bringToFront(); + } + } + + private void addTile(final QSTile<?> tile) { final TileRecord r = new TileRecord(); r.tile = tile; r.tileView = tile.createTileView(mContext); r.tileView.setVisibility(View.GONE); - r.tile.setCallback(new QSTile.Callback() { + final QSTile.Callback callback = new QSTile.Callback() { @Override public void onStateChanged(QSTile.State state) { setTileVisibility(r.tileView, state.visible); @@ -213,7 +227,8 @@ public class QSPanel extends ViewGroup { fireScanStateChanged(state); } } - }); + }; + r.tile.setCallback(callback); final View.OnClickListener click = new View.OnClickListener() { @Override public void onClick(View v) { @@ -227,6 +242,8 @@ public class QSPanel extends ViewGroup { } }; r.tileView.init(click, clickSecondary); + r.tile.setListening(mListening); + callback.onStateChanged(r.tile.getState()); r.tile.refreshState(); mRecords.add(r); diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java index 93766af..6975541 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java @@ -37,9 +37,8 @@ import com.android.systemui.statusbar.policy.NetworkController; import com.android.systemui.statusbar.policy.RotationLockController; import com.android.systemui.statusbar.policy.HotspotController; import com.android.systemui.statusbar.policy.ZenModeController; -import com.android.systemui.volume.VolumeComponent; -import java.util.List; +import java.util.Collection; import java.util.Objects; /** @@ -134,6 +133,14 @@ public abstract class QSTile<TState extends State> implements Listenable { mHandler.obtainMessage(H.SCAN_STATE_CHANGED, state ? 1 : 0, 0).sendToTarget(); } + public void destroy() { + mHandler.sendEmptyMessage(H.DESTROY); + } + + public TState getState() { + return mState; + } + // call only on tile worker looper private void handleSetCallback(Callback callback) { @@ -181,6 +188,11 @@ public abstract class QSTile<TState extends State> implements Listenable { handleRefreshState(null); } + protected void handleDestroy() { + setListening(false); + mCallback = null; + } + protected final class H extends Handler { private static final int SET_CALLBACK = 1; private static final int CLICK = 2; @@ -190,6 +202,7 @@ public abstract class QSTile<TState extends State> implements Listenable { private static final int USER_SWITCH = 6; private static final int TOGGLE_STATE_CHANGED = 7; private static final int SCAN_STATE_CHANGED = 8; + private static final int DESTROY = 9; private H(Looper looper) { super(looper); @@ -223,6 +236,11 @@ public abstract class QSTile<TState extends State> implements Listenable { } else if (msg.what == SCAN_STATE_CHANGED) { name = "handleScanStateChanged"; handleScanStateChanged(msg.arg1 != 0); + } else if (msg.what == DESTROY) { + name = "handleDestroy"; + handleDestroy(); + } else { + throw new IllegalArgumentException("Unknown msg: " + msg.what); } } catch (Throwable t) { final String error = "Error in " + name; @@ -245,7 +263,8 @@ public abstract class QSTile<TState extends State> implements Listenable { void collapsePanels(); Looper getLooper(); Context getContext(); - List<QSTile<?>> getTiles(); + Collection<QSTile<?>> getTiles(); + void setCallback(Callback callback); BluetoothController getBluetoothController(); LocationController getLocationController(); RotationLockController getRotationLockController(); @@ -255,6 +274,10 @@ public abstract class QSTile<TState extends State> implements Listenable { CastController getCastController(); FlashlightController getFlashlightController(); KeyguardMonitor getKeyguardMonitor(); + + public interface Callback { + void onTilesChanged(); + } } public static class State { diff --git a/packages/SystemUI/src/com/android/systemui/qs/UsageTracker.java b/packages/SystemUI/src/com/android/systemui/qs/UsageTracker.java index e72d3a9..ad79aba 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/UsageTracker.java +++ b/packages/SystemUI/src/com/android/systemui/qs/UsageTracker.java @@ -23,8 +23,9 @@ import android.content.IntentFilter; import android.content.SharedPreferences; import com.android.systemui.R; +import com.android.systemui.statusbar.policy.Listenable; -public class UsageTracker { +public class UsageTracker implements Listenable { private static final long MILLIS_PER_DAY = 1000 * 60 * 60 * 24; private final Context mContext; @@ -32,7 +33,7 @@ public class UsageTracker { private final String mPrefKey; private final String mResetAction; - private BroadcastReceiver mReceiver; + private boolean mRegistered; public UsageTracker(Context context, Class<?> tile) { mContext = context; @@ -42,17 +43,14 @@ public class UsageTracker { mResetAction = "com.android.systemui.qs." + tile.getSimpleName() + ".usage_reset"; } - public void listenForReset() { - if (mReceiver != null) { - mReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - if (mResetAction.equals(intent.getAction())) { - reset(); - } - } - }; - mContext.registerReceiver(mReceiver, new IntentFilter(mResetAction)); + @Override + public void setListening(boolean listen) { + if (listen && !mRegistered) { + mContext.registerReceiver(mReceiver, new IntentFilter(mResetAction)); + mRegistered = true; + } else if (!listen && mRegistered) { + mContext.unregisterReceiver(mReceiver); + mRegistered = false; } } @@ -72,4 +70,13 @@ public class UsageTracker { private SharedPreferences getSharedPrefs() { return mContext.getSharedPreferences(mContext.getPackageName(), 0); } + + private BroadcastReceiver mReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (mResetAction.equals(intent.getAction())) { + reset(); + } + } + }; } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java index 191bac9..5d1fa80 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java @@ -31,6 +31,8 @@ import com.android.systemui.qs.QSTile; public class AirplaneModeTile extends QSTile<QSTile.BooleanState> { private final GlobalSetting mSetting; + private boolean mListening; + public AirplaneModeTile(Host host) { super(host); @@ -79,6 +81,8 @@ public class AirplaneModeTile extends QSTile<QSTile.BooleanState> { } public void setListening(boolean listening) { + if (mListening == listening) return; + mListening = listening; if (listening) { final IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java index 9c88466..21254d4 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java @@ -41,7 +41,13 @@ public class ColorInversionTile extends QSTile<QSTile.BooleanState> { } }; mUsageTracker = new UsageTracker(host.getContext(), ColorInversionTile.class); - mUsageTracker.listenForReset(); + mUsageTracker.setListening(true); + } + + @Override + protected void handleDestroy() { + super.handleDestroy(); + mUsageTracker.setListening(false); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java index f4ddd84..3ddf5e3 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java @@ -16,18 +16,13 @@ package com.android.systemui.qs.tiles; +import android.app.ActivityManager; +import android.os.SystemClock; + import com.android.systemui.R; import com.android.systemui.qs.QSTile; -import com.android.systemui.qs.SecureSetting; import com.android.systemui.statusbar.policy.FlashlightController; -import android.app.ActivityManager; -import android.os.Handler; -import android.os.Looper; -import android.os.SystemClock; -import android.provider.Settings.Secure; -import android.util.Log; - /** Quick settings tile: Control flashlight **/ public class FlashlightTile extends QSTile<QSTile.BooleanState> implements FlashlightController.FlashlightListener { @@ -46,6 +41,12 @@ public class FlashlightTile extends QSTile<QSTile.BooleanState> implements } @Override + protected void handleDestroy() { + super.handleDestroy(); + mFlashlightController.removeListener(this); + } + + @Override protected BooleanState newTileState() { return new BooleanState(); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java index ff26b54..96333a3 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java @@ -35,7 +35,13 @@ public class HotspotTile extends QSTile<QSTile.BooleanState> { super(host); mController = host.getHotspotController(); mUsageTracker = new UsageTracker(host.getContext(), HotspotTile.class); - mUsageTracker.listenForReset(); + mUsageTracker.setListening(true); + } + + @Override + protected void handleDestroy() { + super.handleDestroy(); + mUsageTracker.setListening(false); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/IntentTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/IntentTile.java new file mode 100644 index 0000000..58587e6 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/IntentTile.java @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles; + +import android.app.PendingIntent; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.os.UserHandle; +import android.text.TextUtils; +import android.util.Log; + +import com.android.systemui.qs.QSTile; + +public class IntentTile extends QSTile<QSTile.State> { + public static final String PREFIX = "intent("; + + private PendingIntent mOnClick; + private String mOnClickUri; + private int mCurrentUserId; + + private IntentTile(Host host, String action) { + super(host); + mContext.registerReceiver(mReceiver, new IntentFilter(action)); + } + + @Override + protected void handleDestroy() { + super.handleDestroy(); + mContext.unregisterReceiver(mReceiver); + } + + public static QSTile<?> create(Host host, String spec) { + if (spec == null || !spec.startsWith(PREFIX) || !spec.endsWith(")")) { + throw new IllegalArgumentException("Bad intent tile spec: " + spec); + } + final String action = spec.substring(PREFIX.length(), spec.length() - 1); + if (action.isEmpty()) { + throw new IllegalArgumentException("Empty intent tile spec action"); + } + return new IntentTile(host, action); + } + + @Override + public void setListening(boolean listening) { + } + + @Override + protected State newTileState() { + return new State(); + } + + @Override + protected void handleUserSwitch(int newUserId) { + super.handleUserSwitch(newUserId); + mCurrentUserId = newUserId; + } + + @Override + protected void handleClick() { + try { + if (mOnClick != null) { + mOnClick.send(); + } else if (mOnClickUri != null) { + final Intent intent = Intent.parseUri(mOnClickUri, Intent.URI_INTENT_SCHEME); + mContext.sendBroadcastAsUser(intent, new UserHandle(mCurrentUserId)); + } + } catch (Throwable t) { + Log.w(TAG, "Error sending click intent", t); + } + } + + @Override + protected void handleUpdateState(State state, Object arg) { + if (!(arg instanceof Intent)) return; + final Intent intent = (Intent) arg; + state.visible = intent.getBooleanExtra("visible", true); + state.contentDescription = intent.getStringExtra("contentDescription"); + state.label = intent.getStringExtra("label"); + state.iconId = 0; + state.icon = null; + final byte[] iconBitmap = intent.getByteArrayExtra("iconBitmap"); + if (iconBitmap != null) { + try { + final Bitmap b = BitmapFactory.decodeByteArray(iconBitmap, 0, iconBitmap.length); + state.icon = new BitmapDrawable(mContext.getResources(), b); + } catch (Throwable t) { + Log.w(TAG, "Error loading icon bitmap, length " + iconBitmap.length, t); + } + } else { + final int iconId = intent.getIntExtra("iconId", 0); + if (iconId != 0) { + final String iconPackage = intent.getStringExtra("iconPackage"); + if (!TextUtils.isEmpty(iconPackage)) { + state.icon = getPackageDrawable(iconPackage, iconId); + } else { + state.iconId = iconId; + } + } + } + mOnClick = intent.getParcelableExtra("onClick"); + mOnClickUri = intent.getStringExtra("onClickUri"); + } + + private Drawable getPackageDrawable(String pkg, int id) { + try { + return mContext.createPackageContext(pkg, 0).getDrawable(id); + } catch (Throwable t) { + Log.w(TAG, "Error loading package drawable pkg=" + pkg + " id=" + id, t); + return null; + } + } + + private final BroadcastReceiver mReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + refreshState(intent); + } + }; +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java index 74ae4a4..a07bc5c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java @@ -197,6 +197,20 @@ public class NotificationPanelView extends PanelView implements mAfforanceHelper = new KeyguardAffordanceHelper(this, getContext()); mSecureCameraLaunchManager = new SecureCameraLaunchManager(getContext(), mKeyguardBottomArea); + + // recompute internal state when qspanel height changes + mQsContainer.addOnLayoutChangeListener(new OnLayoutChangeListener() { + @Override + public void onLayoutChange(View v, int left, int top, int right, + int bottom, int oldLeft, int oldTop, int oldRight, + int oldBottom) { + final int height = bottom - top; + final int oldHeight = oldBottom - oldTop; + if (height != oldHeight) { + onScrollChanged(); + } + } + }); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java index 5eb45df..a2f8931 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -818,10 +818,14 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, mUserSwitcherController, mKeyguardMonitor, mSecurityController); mQSPanel.setHost(qsh); - for (QSTile<?> tile : qsh.getTiles()) { - mQSPanel.addTile(tile); - } + mQSPanel.setTiles(qsh.getTiles()); mHeader.setQSPanel(mQSPanel); + qsh.setCallback(new QSTileHost.Callback() { + @Override + public void onTilesChanged() { + mQSPanel.setTiles(qsh.getTiles()); + } + }); } mBackdrop = (FrameLayout) mStatusBarWindow.findViewById(R.id.backdrop); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java index 8f25fb9..729d459 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java @@ -18,9 +18,16 @@ package com.android.systemui.statusbar.phone; import android.content.Context; import android.content.Intent; +import android.content.res.Resources; +import android.database.ContentObserver; +import android.net.Uri; +import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; +import android.provider.Settings.Secure; +import android.util.Log; +import com.android.systemui.R; import com.android.systemui.qs.QSTile; import com.android.systemui.qs.tiles.AirplaneModeTile; import com.android.systemui.qs.tiles.BluetoothTile; @@ -29,6 +36,7 @@ import com.android.systemui.qs.tiles.CellularTile; import com.android.systemui.qs.tiles.ColorInversionTile; import com.android.systemui.qs.tiles.FlashlightTile; import com.android.systemui.qs.tiles.HotspotTile; +import com.android.systemui.qs.tiles.IntentTile; import com.android.systemui.qs.tiles.LocationTile; import com.android.systemui.qs.tiles.RotationLockTile; import com.android.systemui.qs.tiles.WifiTile; @@ -46,13 +54,23 @@ import com.android.systemui.statusbar.policy.UserSwitcherController; import com.android.systemui.statusbar.policy.ZenModeController; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; /** Platform implementation of the quick settings tile host **/ public class QSTileHost implements QSTile.Host { + private static final String TAG = "QSTileHost"; + private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + + private static final String TILES_SETTING = "sysui_qs_tiles"; private final Context mContext; private final PhoneStatusBar mStatusBar; + private final LinkedHashMap<String, QSTile<?>> mTiles = new LinkedHashMap<>(); + private final Observer mObserver = new Observer(); private final BluetoothController mBluetooth; private final LocationController mLocation; private final RotationLockController mRotation; @@ -62,12 +80,13 @@ public class QSTileHost implements QSTile.Host { private final CastController mCast; private final Looper mLooper; private final CurrentUserTracker mUserTracker; - private final ArrayList<QSTile<?>> mTiles = new ArrayList<QSTile<?>>(); private final FlashlightController mFlashlight; private final UserSwitcherController mUserSwitcherController; private final KeyguardMonitor mKeyguard; private final SecurityController mSecurity; + private Callback mCallback; + public QSTileHost(Context context, PhoneStatusBar statusBar, BluetoothController bluetooth, LocationController location, RotationLockController rotation, NetworkController network, @@ -93,31 +112,30 @@ public class QSTileHost implements QSTile.Host { ht.start(); mLooper = ht.getLooper(); - mTiles.add(new WifiTile(this)); - mTiles.add(new BluetoothTile(this)); - mTiles.add(new ColorInversionTile(this)); - mTiles.add(new CellularTile(this)); - mTiles.add(new AirplaneModeTile(this)); - mTiles.add(new RotationLockTile(this)); - mTiles.add(new FlashlightTile(this)); - mTiles.add(new LocationTile(this)); - mTiles.add(new CastTile(this)); - mTiles.add(new HotspotTile(this)); - mUserTracker = new CurrentUserTracker(mContext) { @Override public void onUserSwitched(int newUserId) { - for (QSTile<?> tile : mTiles) { + recreateTiles(); + for (QSTile<?> tile : mTiles.values()) { tile.userSwitch(newUserId); } + mObserver.register(); } }; + recreateTiles(); + mUserTracker.startTracking(); + mObserver.register(); + } + + @Override + public void setCallback(Callback callback) { + mCallback = callback; } @Override - public List<QSTile<?>> getTiles() { - return mTiles; + public Collection<QSTile<?>> getTiles() { + return mTiles.values(); } @Override @@ -197,4 +215,99 @@ public class QSTileHost implements QSTile.Host { public SecurityController getSecurityController() { return mSecurity; } + + private void recreateTiles() { + if (DEBUG) Log.d(TAG, "Recreating tiles"); + final List<String> tileSpecs = loadTileSpecs(); + for (Map.Entry<String, QSTile<?>> tile : mTiles.entrySet()) { + if (!tileSpecs.contains(tile.getKey())) { + if (DEBUG) Log.d(TAG, "Destroying tile: " + tile.getKey()); + tile.getValue().destroy(); + } + } + final LinkedHashMap<String, QSTile<?>> newTiles = new LinkedHashMap<>(); + for (String tileSpec : tileSpecs) { + if (mTiles.containsKey(tileSpec)) { + newTiles.put(tileSpec, mTiles.get(tileSpec)); + } else { + if (DEBUG) Log.d(TAG, "Creating tile: " + tileSpec); + try { + newTiles.put(tileSpec, createTile(tileSpec)); + } catch (Throwable t) { + Log.w(TAG, "Error creating tile for spec: " + tileSpec, t); + } + } + } + if (mTiles.equals(newTiles)) return; + mTiles.clear(); + mTiles.putAll(newTiles); + if (mCallback != null) { + mCallback.onTilesChanged(); + } + } + + private QSTile<?> createTile(String tileSpec) { + if (tileSpec.equals("wifi")) return new WifiTile(this); + else if (tileSpec.equals("bt")) return new BluetoothTile(this); + else if (tileSpec.equals("inversion")) return new ColorInversionTile(this); + else if (tileSpec.equals("cell")) return new CellularTile(this); + else if (tileSpec.equals("airplane")) return new AirplaneModeTile(this); + else if (tileSpec.equals("rotation")) return new RotationLockTile(this); + else if (tileSpec.equals("flashlight")) return new FlashlightTile(this); + else if (tileSpec.equals("location")) return new LocationTile(this); + else if (tileSpec.equals("cast")) return new CastTile(this); + else if (tileSpec.equals("hotspot")) return new HotspotTile(this); + else if (tileSpec.startsWith(IntentTile.PREFIX)) return IntentTile.create(this,tileSpec); + else throw new IllegalArgumentException("Bad tile spec: " + tileSpec); + } + + private List<String> loadTileSpecs() { + final Resources res = mContext.getResources(); + final String defaultTileList = res.getString(R.string.quick_settings_tiles_default); + String tileList = Secure.getStringForUser(mContext.getContentResolver(), TILES_SETTING, + mUserTracker.getCurrentUserId()); + if (tileList == null) { + tileList = res.getString(R.string.quick_settings_tiles); + if (DEBUG) Log.d(TAG, "Loaded tile specs from config: " + tileList); + } else { + if (DEBUG) Log.d(TAG, "Loaded tile specs from setting: " + tileList); + } + final ArrayList<String> tiles = new ArrayList<String>(); + boolean addedDefault = false; + for (String tile : tileList.split(",")) { + tile = tile.trim(); + if (tile.isEmpty()) continue; + if (tile.equals("default")) { + if (!addedDefault) { + tiles.addAll(Arrays.asList(defaultTileList.split(","))); + addedDefault = true; + } + } else { + tiles.add(tile); + } + } + return tiles; + } + + private class Observer extends ContentObserver { + private boolean mRegistered; + + public Observer() { + super(new Handler(Looper.getMainLooper())); + } + + public void register() { + if (mRegistered) { + mContext.getContentResolver().unregisterContentObserver(this); + } + mContext.getContentResolver().registerContentObserver(Secure.getUriFor(TILES_SETTING), + false, this, mUserTracker.getCurrentUserId()); + mRegistered = true; + } + + @Override + public void onChange(boolean selfChange, Uri uri) { + recreateTiles(); + } + } } |