From bbadff8603ca6922a0ef89338bee5b59d6dcf641 Mon Sep 17 00:00:00 2001 From: Jason Monk Date: Fri, 6 Nov 2015 15:47:26 -0500 Subject: [PATCH] Add Quick Settings API It is a little bit limited right now, but it contains the lifecycle of a tile getting added/removed, and listening/not listening and clicks. SysUI side will need some cleanup later on. Change-Id: I4db803c8a271f8bf44f2ef710517969a84a95cf0 --- Android.mk | 3 + api/current.txt | 30 + api/system-current.txt | 30 + .../service/quicksettings/IQSService.aidl | 26 + .../service/quicksettings/IQSTileService.aidl | 31 + .../android/service/quicksettings/Tile.aidl | 19 + .../android/service/quicksettings/Tile.java | 186 ++++++ .../service/quicksettings/TileService.java | 206 +++++++ core/res/AndroidManifest.xml | 6 + packages/SystemUI/res/xml/tuner_prefs.xml | 4 - .../src/com/android/systemui/qs/QSPanel.java | 4 +- .../src/com/android/systemui/qs/QSTile.java | 12 +- .../systemui/qs/QSTileServiceWrapper.java | 76 +++ .../com/android/systemui/qs/QSTileView.java | 8 +- .../qs/customize/BlankCustomTile.java | 88 +++ .../systemui/qs/customize/CustomQSPanel.java | 162 +++++- .../qs/customize/CustomQSTileHost.java | 194 ------- .../qs/customize/NonPagedTileLayout.java | 6 +- .../systemui/qs/customize/QSCustomizer.java | 25 +- .../systemui/qs/customize/TileAdapter.java | 15 +- .../systemui/qs/tiles/BluetoothTile.java | 2 +- .../android/systemui/qs/tiles/CustomTile.java | 143 ++++- .../android/systemui/qs/tiles/WifiTile.java | 2 +- .../statusbar/phone/PhoneStatusBar.java | 2 +- .../systemui/statusbar/phone/QSTileHost.java | 105 +++- .../statusbar/phone/QuickStatusBarHeader.java | 2 +- .../com/android/systemui/tuner/QsTuner.java | 547 ------------------ .../android/systemui/tuner/TunerFragment.java | 28 - 28 files changed, 1105 insertions(+), 857 deletions(-) create mode 100644 core/java/android/service/quicksettings/IQSService.aidl create mode 100644 core/java/android/service/quicksettings/IQSTileService.aidl create mode 100644 core/java/android/service/quicksettings/Tile.aidl create mode 100644 core/java/android/service/quicksettings/Tile.java create mode 100644 core/java/android/service/quicksettings/TileService.java create mode 100644 packages/SystemUI/src/com/android/systemui/qs/QSTileServiceWrapper.java create mode 100644 packages/SystemUI/src/com/android/systemui/qs/customize/BlankCustomTile.java delete mode 100644 packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSTileHost.java delete mode 100644 packages/SystemUI/src/com/android/systemui/tuner/QsTuner.java diff --git a/Android.mk b/Android.mk index 71bba0f16ea9a..7d942ebbeaa7e 100644 --- a/Android.mk +++ b/Android.mk @@ -417,6 +417,8 @@ LOCAL_SRC_FILES += \ packages/services/PacProcessor/com/android/net/IProxyService.aidl \ packages/services/Proxy/com/android/net/IProxyCallback.aidl \ packages/services/Proxy/com/android/net/IProxyPortListener.aidl \ + core/java/android/service/quicksettings/IQSService.aidl \ + core/java/android/service/quicksettings/IQSTileService.aidl \ # FRAMEWORKS_BASE_JAVA_SRC_DIRS comes from build/core/pathmap.mk LOCAL_AIDL_INCLUDES += $(FRAMEWORKS_BASE_JAVA_SRC_DIRS) @@ -625,6 +627,7 @@ aidl_files := \ frameworks/base/core/java/android/bluetooth/le/ScanResult.aidl \ frameworks/base/core/java/android/bluetooth/BluetoothDevice.aidl \ frameworks/base/core/java/android/database/CursorWindow.aidl \ + frameworks/base/core/java/android/service/quicksettings/Tile.aidl \ gen := $(TARGET_OUT_COMMON_INTERMEDIATES)/framework.aidl $(gen): PRIVATE_SRC_FILES := $(aidl_files) diff --git a/api/current.txt b/api/current.txt index 1a7139c08cd89..7355fa6a0aca4 100644 --- a/api/current.txt +++ b/api/current.txt @@ -30,6 +30,7 @@ package android { field public static final java.lang.String BIND_NFC_SERVICE = "android.permission.BIND_NFC_SERVICE"; field public static final java.lang.String BIND_NOTIFICATION_LISTENER_SERVICE = "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE"; field public static final java.lang.String BIND_PRINT_SERVICE = "android.permission.BIND_PRINT_SERVICE"; + field public static final java.lang.String BIND_QUICK_SETTINGS_TILE = "android.permission.BIND_QUICK_SETTINGS_TILE"; field public static final java.lang.String BIND_REMOTEVIEWS = "android.permission.BIND_REMOTEVIEWS"; field public static final java.lang.String BIND_TELECOM_CONNECTION_SERVICE = "android.permission.BIND_TELECOM_CONNECTION_SERVICE"; field public static final java.lang.String BIND_TEXT_SERVICE = "android.permission.BIND_TEXT_SERVICE"; @@ -29043,6 +29044,35 @@ package android.service.notification { } +package android.service.quicksettings { + + public final class Tile implements android.os.Parcelable { + method public int describeContents(); + method public java.lang.CharSequence getContentDescription(); + method public android.graphics.drawable.Icon getIcon(); + method public java.lang.CharSequence getLabel(); + method public void setContentDescription(java.lang.CharSequence); + method public void setIcon(android.graphics.drawable.Icon); + method public void setLabel(java.lang.CharSequence); + method public void updateTile(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator CREATOR; + } + + public class TileService extends android.app.Service { + ctor public TileService(); + method public final android.service.quicksettings.Tile getQsTile(); + method public android.os.IBinder onBind(android.content.Intent); + method public void onClick(); + method public void onStartListening(); + method public void onStopListening(); + method public void onTileAdded(); + method public void onTileRemoved(); + field public static final java.lang.String ACTION_QS_TILE = "android.service.quicksettings.action.QS_TILE"; + } + +} + package android.service.restrictions { public abstract class RestrictionsReceiver extends android.content.BroadcastReceiver { diff --git a/api/system-current.txt b/api/system-current.txt index f278441014733..b7204114f49ce 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -43,6 +43,7 @@ package android { field public static final java.lang.String BIND_NFC_SERVICE = "android.permission.BIND_NFC_SERVICE"; field public static final java.lang.String BIND_NOTIFICATION_LISTENER_SERVICE = "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE"; field public static final java.lang.String BIND_PRINT_SERVICE = "android.permission.BIND_PRINT_SERVICE"; + field public static final java.lang.String BIND_QUICK_SETTINGS_TILE = "android.permission.BIND_QUICK_SETTINGS_TILE"; field public static final java.lang.String BIND_REMOTEVIEWS = "android.permission.BIND_REMOTEVIEWS"; field public static final java.lang.String BIND_TELECOM_CONNECTION_SERVICE = "android.permission.BIND_TELECOM_CONNECTION_SERVICE"; field public static final java.lang.String BIND_TEXT_SERVICE = "android.permission.BIND_TEXT_SERVICE"; @@ -31189,6 +31190,35 @@ package android.service.persistentdata { } +package android.service.quicksettings { + + public final class Tile implements android.os.Parcelable { + method public int describeContents(); + method public java.lang.CharSequence getContentDescription(); + method public android.graphics.drawable.Icon getIcon(); + method public java.lang.CharSequence getLabel(); + method public void setContentDescription(java.lang.CharSequence); + method public void setIcon(android.graphics.drawable.Icon); + method public void setLabel(java.lang.CharSequence); + method public void updateTile(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator CREATOR; + } + + public class TileService extends android.app.Service { + ctor public TileService(); + method public final android.service.quicksettings.Tile getQsTile(); + method public android.os.IBinder onBind(android.content.Intent); + method public void onClick(); + method public void onStartListening(); + method public void onStopListening(); + method public void onTileAdded(); + method public void onTileRemoved(); + field public static final java.lang.String ACTION_QS_TILE = "android.service.quicksettings.action.QS_TILE"; + } + +} + package android.service.restrictions { public abstract class RestrictionsReceiver extends android.content.BroadcastReceiver { diff --git a/core/java/android/service/quicksettings/IQSService.aidl b/core/java/android/service/quicksettings/IQSService.aidl new file mode 100644 index 0000000000000..087eb61d906d6 --- /dev/null +++ b/core/java/android/service/quicksettings/IQSService.aidl @@ -0,0 +1,26 @@ +/* + * Copyright 2015, 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 android.service.quicksettings; + +import android.content.ComponentName; +import android.service.quicksettings.Tile; + +/** + * @hide + */ +interface IQSService { + void updateQsTile(in Tile tile); +} diff --git a/core/java/android/service/quicksettings/IQSTileService.aidl b/core/java/android/service/quicksettings/IQSTileService.aidl new file mode 100644 index 0000000000000..6b46bee588b9e --- /dev/null +++ b/core/java/android/service/quicksettings/IQSTileService.aidl @@ -0,0 +1,31 @@ +/* + * Copyright 2015, 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 android.service.quicksettings; + +import android.service.quicksettings.Tile; +import android.service.quicksettings.IQSService; + +/** + * @hide + */ +oneway interface IQSTileService { + void setQSTile(in Tile tile); + void onTileAdded(); + void onTileRemoved(); + void onStartListening(); + void onStopListening(); + void onClick(); +} diff --git a/core/java/android/service/quicksettings/Tile.aidl b/core/java/android/service/quicksettings/Tile.aidl new file mode 100644 index 0000000000000..0373326d42a70 --- /dev/null +++ b/core/java/android/service/quicksettings/Tile.aidl @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2015, 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 android.service.quicksettings; + +parcelable Tile; diff --git a/core/java/android/service/quicksettings/Tile.java b/core/java/android/service/quicksettings/Tile.java new file mode 100644 index 0000000000000..c8ae171c458e2 --- /dev/null +++ b/core/java/android/service/quicksettings/Tile.java @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2015 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 android.service.quicksettings; + +import android.content.ComponentName; +import android.graphics.drawable.Icon; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.RemoteException; +import android.text.TextUtils; +import android.util.Log; + +/** + * A Tile holds the state of a tile that will be displayed + * in Quick Settings. + * + * A tile in Quick Settings exists as an icon with an accompanied label. + * It also may have content description for accessibility usability. + * The style and layout of the tile may change to match a given + * device. + */ +public final class Tile implements Parcelable { + + private static final String TAG = "Tile"; + + private ComponentName mComponentName; + private IQSService mService; + private Icon mIcon; + private CharSequence mLabel; + private CharSequence mContentDescription; + + /** + * @hide + */ + public Tile(Parcel source) { + readFromParcel(source); + } + + /** + * @hide + */ + public Tile(ComponentName componentName, IQSService service) { + mComponentName = componentName; + mService = service; + } + + /** + * @hide + */ + public ComponentName getComponentName() { + return mComponentName; + } + + /** + * Gets the current icon for the tile. + */ + public Icon getIcon() { + return mIcon; + } + + /** + * Sets the current icon for the tile. + * + * This icon is expected to be white on alpha, and may be + * tinted by the system to match it's theme. + * + * Does not take effect until {@link #updateTile()} is called. + * + * @param icon New icon to show. + */ + public void setIcon(Icon icon) { + this.mIcon = icon; + } + + /** + * Gets the current label for the tile. + */ + public CharSequence getLabel() { + return mLabel; + } + + /** + * Sets the current label for the tile. + * + * Does not take effect until {@link #updateTile()} is called. + * + * @param label New label to show. + */ + public void setLabel(CharSequence label) { + this.mLabel = label; + } + + /** + * Gets the current content description for the tile. + */ + public CharSequence getContentDescription() { + return mContentDescription; + } + + /** + * Sets the current content description for the tile. + * + * Does not take effect until {@link #updateTile()} is called. + * + * @param contentDescription New content description to use. + */ + public void setContentDescription(CharSequence contentDescription) { + this.mContentDescription = contentDescription; + } + + @Override + public int describeContents() { + return 0; + } + + /** + * Pushes the state of the Tile to Quick Settings to be displayed. + */ + public void updateTile() { + try { + mService.updateQsTile(this); + } catch (RemoteException e) { + Log.e(TAG, "Couldn't update tile"); + } + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeStrongInterface(mService); + if (mComponentName != null) { + dest.writeByte((byte) 1); + mComponentName.writeToParcel(dest, flags); + } else { + dest.writeByte((byte) 0); + } + if (mIcon != null) { + dest.writeByte((byte) 1); + mIcon.writeToParcel(dest, flags); + } else { + dest.writeByte((byte) 0); + } + TextUtils.writeToParcel(mLabel, dest, flags); + TextUtils.writeToParcel(mContentDescription, dest, flags); + } + + private void readFromParcel(Parcel source) { + mService = IQSService.Stub.asInterface(source.readStrongBinder()); + if (source.readByte() != 0) { + mComponentName = ComponentName.CREATOR.createFromParcel(source); + } else { + mComponentName = null; + } + if (source.readByte() != 0) { + mIcon = Icon.CREATOR.createFromParcel(source); + } else { + mIcon = null; + } + mLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source); + mContentDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source); + } + + public static final Creator CREATOR = new Creator() { + @Override + public Tile createFromParcel(Parcel source) { + return new Tile(source); + } + + @Override + public Tile[] newArray(int size) { + return new Tile[size]; + } + }; +} \ No newline at end of file diff --git a/core/java/android/service/quicksettings/TileService.java b/core/java/android/service/quicksettings/TileService.java new file mode 100644 index 0000000000000..eba4c6f69d596 --- /dev/null +++ b/core/java/android/service/quicksettings/TileService.java @@ -0,0 +1,206 @@ +/* + * Copyright (C) 2015 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 android.service.quicksettings; + +import android.app.Service; +import android.content.Intent; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.RemoteException; + +/** + * A QSTileService provides the user a tile that can be added to Quick Settings. + * Quick Settings is a space provided that allows the user to change settings and + * take quick actions without leaving the context of their current app. + * + *

The lifecycle of a QSTileService is different from some other services in + * that it may be unbound during parts of its lifecycle. Any of the following + * lifecycle events can happen indepently in a separate binding/creation of the + * service.

+ * + *
    + *
  • When a tile is added by the user its QSTileService will be bound to and + * {@link #onTileAdded()} will be called.
  • + * + *
  • When a tile should be up to date and listing will be indicated by + * {@link #onStartListening()} and {@link #onStopListening()}.
  • + * + *
  • When the user removes a tile from Quick Settings {@link #onStopListening()} + * will be called.
  • + *
+ *

QSTileService will be detected by tiles that match the {@value #ACTION_QS_TILE} + * and require the permission "android.permission.BIND_QUICK_SETTINGS_TILE". + * The label and icon for the service will be used as the default label and + * icon for the tile. Here is an example QSTileService declaration.

+ *
+ * {@literal
+ * 
+ *     
+ *         
+ *     
+ * }
+ * 
+ * + * @see Tile Tile for details about the UI of a Quick Settings Tile. + */ +public class TileService extends Service { + + /** + * Action that identifies a Service as being a QSTileService. + */ + public static final String ACTION_QS_TILE = "android.service.quicksettings.action.QS_TILE"; + + private final H mHandler = new H(Looper.getMainLooper()); + + private boolean mListening = false; + private Tile mTile; + + /** + * Called when the user adds this tile to Quick Settings. + *

+ * Note that this is not guaranteed to be called between {@link #onCreate()} + * and {@link #onStartListening()}, it will only be called when the tile is added + * and not on subsequent binds. + */ + public void onTileAdded() { + } + + /** + * Called when the user removes this tile from Quick Settings. + */ + public void onTileRemoved() { + } + + /** + * Called when this tile moves into a listening state. + *

+ * When this tile is in a listening state it is expected to keep the + * UI up to date. Any listeners or callbacks needed to keep this tile + * up to date should be registered here and unregistered in {@link #onStopListening()}. + * + * @see #getQsTile() + * @see Tile#updateTile() + */ + public void onStartListening() { + } + + /** + * Called when this tile moves out of the listening state. + */ + public void onStopListening() { + } + + /** + * Called when the user clicks on this tile. + */ + public void onClick() { + } + + /** + * Gets the {@link Tile} for this service. + *

+ * This tile may be used to get or set the current state for this + * tile. This tile is only valid for updates between {@link #onStartListening()} + * and {@link #onStopListening()}. + */ + public final Tile getQsTile() { + return mTile; + } + + @Override + public IBinder onBind(Intent intent) { + return new IQSTileService.Stub() { + @Override + public void setQSTile(Tile tile) throws RemoteException { + mHandler.obtainMessage(H.MSG_SET_TILE, tile).sendToTarget(); + } + + @Override + public void onTileRemoved() throws RemoteException { + mHandler.sendEmptyMessage(H.MSG_TILE_REMOVED); + } + + @Override + public void onTileAdded() throws RemoteException { + mHandler.sendEmptyMessage(H.MSG_TILE_ADDED); + } + + @Override + public void onStopListening() throws RemoteException { + mHandler.sendEmptyMessage(H.MSG_STOP_LISTENING); + } + + @Override + public void onStartListening() throws RemoteException { + mHandler.sendEmptyMessage(H.MSG_START_LISTENING); + } + + @Override + public void onClick() throws RemoteException { + mHandler.sendEmptyMessage(H.MSG_TILE_CLICKED); + } + }; + } + + private class H extends Handler { + private static final int MSG_SET_TILE = 1; + private static final int MSG_START_LISTENING = 2; + private static final int MSG_STOP_LISTENING = 3; + private static final int MSG_TILE_ADDED = 4; + private static final int MSG_TILE_REMOVED = 5; + private static final int MSG_TILE_CLICKED = 6; + + public H(Looper looper) { + super(looper); + } + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_SET_TILE: + mTile = (Tile) msg.obj; + break; + case MSG_TILE_ADDED: + TileService.this.onTileRemoved(); + break; + case MSG_TILE_REMOVED: + TileService.this.onTileAdded(); + break; + case MSG_START_LISTENING: + if (mListening) { + mListening = false; + TileService.this.onStopListening(); + } + break; + case MSG_STOP_LISTENING: + if (!mListening) { + mListening = true; + TileService.this.onStartListening(); + } + break; + case MSG_TILE_CLICKED: + TileService.this.onClick(); + break; + } + } + } +} diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 6bdf71bdba6f7..42ede31527d0c 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -1831,6 +1831,12 @@ + + +