From 8f7f3184d55850b1c4d2036319794151f564e9c0 Mon Sep 17 00:00:00 2001 From: Jason Monk Date: Wed, 18 Nov 2015 16:35:14 -0500 Subject: [PATCH] Allow QS tiles to open dialogs Add a TYPE_QS_DIALOG window type, that is in the same layer as apps and dialogs and such. It is guarded by having a token that is granted by SysUI after a click has occured. If the app shows a dialog before listening finishes (QS closes) then the token will stay granted until the tile is removed by the user, otherwise the token will be removed immediately to avoid later dialogs. Also fix a couple tiny TileService issues: - Stop/Start listening reversed - Fix javadoc referencing wrong action Change-Id: Iedcdd5fd9a2af2b33eb7f6f17bb0e6c997879876 --- api/current.txt | 1 + api/system-current.txt | 1 + api/test-current.txt | 1 + .../android/service/quicksettings/IQSService.aidl | 1 + .../service/quicksettings/IQSTileService.aidl | 2 +- core/java/android/service/quicksettings/Tile.java | 12 ++++++ .../android/service/quicksettings/TileService.java | 28 +++++++++++--- core/java/android/view/WindowManager.java | 8 ++++ .../android/systemui/qs/QSTileServiceWrapper.java | 4 +- .../com/android/systemui/qs/tiles/CustomTile.java | 44 ++++++++++++++++++++-- .../systemui/statusbar/phone/QSTileHost.java | 11 +++++- .../android/server/policy/PhoneWindowManager.java | 8 ++-- .../android/server/wm/AccessibilityController.java | 1 + .../android/server/wm/WindowManagerService.java | 6 +++ 14 files changed, 112 insertions(+), 16 deletions(-) diff --git a/api/current.txt b/api/current.txt index d54b7fd11708..8f933618e72c 100644 --- a/api/current.txt +++ b/api/current.txt @@ -33245,6 +33245,7 @@ package android.service.quicksettings { method public void onStopListening(); method public void onTileAdded(); method public void onTileRemoved(); + method public final void showDialog(android.app.Dialog); field public static final java.lang.String ACTION_QS_TILE = "android.service.quicksettings.action.QS_TILE"; } diff --git a/api/system-current.txt b/api/system-current.txt index 2ea5ebb5a297..7a9728025b43 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -35394,6 +35394,7 @@ package android.service.quicksettings { method public void onStopListening(); method public void onTileAdded(); method public void onTileRemoved(); + method public final void showDialog(android.app.Dialog); field public static final java.lang.String ACTION_QS_TILE = "android.service.quicksettings.action.QS_TILE"; } diff --git a/api/test-current.txt b/api/test-current.txt index 904347de1259..ddb5b0642540 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -33247,6 +33247,7 @@ package android.service.quicksettings { method public void onStopListening(); method public void onTileAdded(); method public void onTileRemoved(); + method public final void showDialog(android.app.Dialog); field public static final java.lang.String ACTION_QS_TILE = "android.service.quicksettings.action.QS_TILE"; } diff --git a/core/java/android/service/quicksettings/IQSService.aidl b/core/java/android/service/quicksettings/IQSService.aidl index 087eb61d906d..7e70501de39b 100644 --- a/core/java/android/service/quicksettings/IQSService.aidl +++ b/core/java/android/service/quicksettings/IQSService.aidl @@ -23,4 +23,5 @@ import android.service.quicksettings.Tile; */ interface IQSService { void updateQsTile(in Tile tile); + void onShowDialog(in Tile tile); } diff --git a/core/java/android/service/quicksettings/IQSTileService.aidl b/core/java/android/service/quicksettings/IQSTileService.aidl index 6b46bee588b9..63a4c5e30df9 100644 --- a/core/java/android/service/quicksettings/IQSTileService.aidl +++ b/core/java/android/service/quicksettings/IQSTileService.aidl @@ -27,5 +27,5 @@ oneway interface IQSTileService { void onTileRemoved(); void onStartListening(); void onStopListening(); - void onClick(); + void onClick(IBinder wtoken); } diff --git a/core/java/android/service/quicksettings/Tile.java b/core/java/android/service/quicksettings/Tile.java index c8ae171c458e..a53fc59d362c 100644 --- a/core/java/android/service/quicksettings/Tile.java +++ b/core/java/android/service/quicksettings/Tile.java @@ -137,6 +137,18 @@ public final class Tile implements Parcelable { } } + /** + * @hide + * Notifies the IQSService that this tile is showing a dialog. + */ + void onShowDialog() { + try { + mService.onShowDialog(this); + } catch (RemoteException e) { + Log.e(TAG, "Couldn't onShowDialog"); + } + } + @Override public void writeToParcel(Parcel dest, int flags) { dest.writeStrongInterface(mService); diff --git a/core/java/android/service/quicksettings/TileService.java b/core/java/android/service/quicksettings/TileService.java index eba4c6f69d59..fd2d5b05b5f0 100644 --- a/core/java/android/service/quicksettings/TileService.java +++ b/core/java/android/service/quicksettings/TileService.java @@ -15,6 +15,7 @@ */ package android.service.quicksettings; +import android.app.Dialog; import android.app.Service; import android.content.Intent; import android.os.Handler; @@ -22,6 +23,7 @@ import android.os.IBinder; import android.os.Looper; import android.os.Message; import android.os.RemoteException; +import android.view.WindowManager; /** * A QSTileService provides the user a tile that can be added to Quick Settings. @@ -55,7 +57,7 @@ import android.os.RemoteException; * android:icon="@drawable/my_default_icon_label" * android:permission="android.permission.BIND_QUICK_SETTINGS_TILE"> * - * + * * * } * @@ -73,6 +75,7 @@ public class TileService extends Service { private boolean mListening = false; private Tile mTile; + private IBinder mToken; /** * Called when the user adds this tile to Quick Settings. @@ -116,6 +119,20 @@ public class TileService extends Service { } /** + * Used to show a dialog. + * + * This will collapse the Quick Settings panel and show the dialog. + * + * @param dialog Dialog to show. + */ + public final void showDialog(Dialog dialog) { + dialog.getWindow().getAttributes().token = mToken; + dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_QS_DIALOG); + dialog.show(); + getQsTile().onShowDialog(); + } + + /** * Gets the {@link Tile} for this service. *

* This tile may be used to get or set the current state for this @@ -155,8 +172,8 @@ public class TileService extends Service { } @Override - public void onClick() throws RemoteException { - mHandler.sendEmptyMessage(H.MSG_TILE_CLICKED); + public void onClick(IBinder wtoken) throws RemoteException { + mHandler.obtainMessage(H.MSG_TILE_CLICKED, wtoken).sendToTarget(); } }; } @@ -185,19 +202,20 @@ public class TileService extends Service { case MSG_TILE_REMOVED: TileService.this.onTileAdded(); break; - case MSG_START_LISTENING: + case MSG_STOP_LISTENING: if (mListening) { mListening = false; TileService.this.onStopListening(); } break; - case MSG_STOP_LISTENING: + case MSG_START_LISTENING: if (!mListening) { mListening = true; TileService.this.onStartListening(); } break; case MSG_TILE_CLICKED: + mToken = (IBinder) msg.obj; TileService.this.onClick(); break; } diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 1521f2ed07fe..d6bc27c4996a 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -238,6 +238,7 @@ public interface WindowManager extends ViewManager { @ViewDebug.IntToString(from = TYPE_VOICE_INTERACTION, to = "TYPE_VOICE_INTERACTION"), @ViewDebug.IntToString(from = TYPE_VOICE_INTERACTION_STARTING, to = "TYPE_VOICE_INTERACTION_STARTING"), @ViewDebug.IntToString(from = TYPE_DOCK_DIVIDER, to = "TYPE_DOCK_DIVIDER"), + @ViewDebug.IntToString(from = TYPE_QS_DIALOG, to = "TYPE_QS_DIALOG"), }) public int type; @@ -584,6 +585,13 @@ public interface WindowManager extends ViewManager { public static final int TYPE_DOCK_DIVIDER = FIRST_SYSTEM_WINDOW+34; /** + * Window type: like {@link #TYPE_APPLICATION_ATTACHED_DIALOG}, but used + * by Quick Settings Tiles. + * @hide + */ + public static final int TYPE_QS_DIALOG = FIRST_SYSTEM_WINDOW+35; + + /** * End of types of system windows. */ public static final int LAST_SYSTEM_WINDOW = 2999; diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileServiceWrapper.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileServiceWrapper.java index 55f4736dd74a..a5e1fd55c8f0 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSTileServiceWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileServiceWrapper.java @@ -66,9 +66,9 @@ public class QSTileServiceWrapper implements IQSTileService { } @Override - public void onClick() { + public void onClick(IBinder token) { try { - mService.onClick(); + mService.onClick(token); } catch (Exception e) { Log.d(TAG, "Caught exception from QSTileService", e); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CustomTile.java index d26e8d64dc82..04006ebae41e 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CustomTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CustomTile.java @@ -24,12 +24,16 @@ import android.content.ServiceConnection; import android.content.pm.PackageManager; import android.content.pm.ServiceInfo; import android.graphics.drawable.Drawable; +import android.os.Binder; import android.os.IBinder; +import android.os.RemoteException; import android.os.UserHandle; import android.service.quicksettings.IQSTileService; import android.service.quicksettings.Tile; import android.util.Log; - +import android.view.IWindowManager; +import android.view.WindowManager; +import android.view.WindowManagerGlobal; import com.android.internal.logging.MetricsLogger; import com.android.systemui.qs.QSTile; import com.android.systemui.qs.QSTileServiceWrapper; @@ -38,19 +42,26 @@ import com.android.systemui.statusbar.phone.QSTileHost; public class CustomTile extends QSTile { public static final String PREFIX = "custom("; + private static final boolean DEBUG = false; + // We don't want to thrash binding and unbinding if the user opens and closes the panel a lot. // So instead we have a period of waiting. private static final long UNBIND_DELAY = 30000; private final ComponentName mComponent; private final Tile mTile; + private final IWindowManager mWindowManager; + private final IBinder mToken = new Binder(); private QSTileServiceWrapper mService; private boolean mListening; private boolean mBound; + private boolean mIsTokenGranted; + private boolean mIsShowingDialog; private CustomTile(QSTileHost host, String action) { super(host); + mWindowManager = WindowManagerGlobal.getWindowManagerService(); mComponent = ComponentName.unflattenFromString(action); mTile = new Tile(mComponent, host); try { @@ -72,12 +83,15 @@ public class CustomTile extends QSTile { } public void updateState(Tile tile) { - Log.d("TileService", "Setting state " + tile.getLabel()); mTile.setIcon(tile.getIcon()); mTile.setLabel(tile.getLabel()); mTile.setContentDescription(tile.getContentDescription()); } + public void onDialogShown() { + mIsShowingDialog = true; + } + @Override public void setListening(boolean listening) { if (mListening == listening) return; @@ -95,14 +109,30 @@ public class CustomTile extends QSTile { if (mService!= null) { mService.onStopListening(); } + if (mIsTokenGranted && !mIsShowingDialog) { + try { + if (DEBUG) Log.d(TAG, "Removing token"); + mWindowManager.removeWindowToken(mToken); + } catch (RemoteException e) { + } + mIsTokenGranted = false; + } + mIsShowingDialog = false; mHandler.postDelayed(mUnbind, UNBIND_DELAY); } } - + @Override protected void handleDestroy() { super.handleDestroy(); mHandler.removeCallbacks(mUnbind); + if (mIsTokenGranted) { + try { + if (DEBUG) Log.d(TAG, "Removing token"); + mWindowManager.removeWindowToken(mToken); + } catch (RemoteException e) { + } + } mUnbind.run(); } @@ -119,7 +149,13 @@ public class CustomTile extends QSTile { @Override protected void handleClick() { if (mService != null) { - mService.onClick(); + try { + if (DEBUG) Log.d(TAG, "Adding token"); + mWindowManager.addWindowToken(mToken, WindowManager.LayoutParams.TYPE_QS_DIALOG); + mIsTokenGranted = true; + } catch (RemoteException e) { + } + mService.onClick(mToken); } else { Log.e(TAG, "Click with no service " + getTileSpec()); } 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 57c26485ce10..f7ff8aa13909 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java @@ -293,12 +293,21 @@ public final class QSTileHost extends IQSService.Stub implements QSTile.Host, Tu verifyCaller(tile.getComponentName().getPackageName()); CustomTile customTile = getTileForComponent(tile.getComponentName()); if (customTile != null) { - Log.d("TileService", "Got tile update for " + tile.getComponentName()); customTile.updateState(tile); customTile.refreshState(); } } + @Override + public void onShowDialog(Tile tile) throws RemoteException { + verifyCaller(tile.getComponentName().getPackageName()); + CustomTile customTile = getTileForComponent(tile.getComponentName()); + if (customTile != null) { + customTile.onDialogShown(); + collapsePanels(); + } + } + private void verifyCaller(String packageName) { try { int uid = mContext.getPackageManager().getPackageUid(packageName, diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 639753a13b16..9a7d1531cee2 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -108,9 +108,6 @@ import android.view.KeyCharacterMap; import android.view.KeyCharacterMap.FallbackAction; import android.view.KeyEvent; import android.view.MotionEvent; - -import com.android.internal.logging.MetricsLogger; -import com.android.internal.policy.PhoneWindow; import android.view.Surface; import android.view.View; import android.view.ViewConfiguration; @@ -124,6 +121,8 @@ import android.view.animation.Animation; import android.view.animation.AnimationSet; import android.view.animation.AnimationUtils; import com.android.internal.R; +import com.android.internal.logging.MetricsLogger; +import com.android.internal.policy.PhoneWindow; import com.android.internal.statusbar.IStatusBarService; import com.android.internal.util.ScreenShapeHelper; import com.android.internal.widget.PointerLocationView; @@ -1915,6 +1914,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { case TYPE_PRIVATE_PRESENTATION: case TYPE_VOICE_INTERACTION: case TYPE_ACCESSIBILITY_OVERLAY: + case TYPE_QS_DIALOG: // The window manager will check these. break; case TYPE_PHONE: @@ -2110,6 +2110,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { return 2; case TYPE_DOCK_DIVIDER: return 2; + case TYPE_QS_DIALOG: + return 2; case TYPE_PHONE: return 3; case TYPE_SEARCH_BAR: diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java index c2466091bb91..a4b42763b1ce 100644 --- a/services/core/java/com/android/server/wm/AccessibilityController.java +++ b/services/core/java/com/android/server/wm/AccessibilityController.java @@ -351,6 +351,7 @@ final class AccessibilityController { case WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG: case WindowManager.LayoutParams.TYPE_SYSTEM_ERROR: case WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY: + case WindowManager.LayoutParams.TYPE_QS_DIALOG: case WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL: { Rect magnifiedRegionBounds = mTempRect2; mMagnifedViewport.getMagnifiedFrameInContentCoordsLocked( diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 707450cfdca7..f153873186c5 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -43,6 +43,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_DREAM; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG; import static android.view.WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION; +import static android.view.WindowManager.LayoutParams.TYPE_QS_DIALOG; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION; import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; @@ -1865,6 +1866,11 @@ public class WindowManagerService extends IWindowManager.Stub + attrs.token + ". Aborting."); return WindowManagerGlobal.ADD_BAD_APP_TOKEN; } + if (type == TYPE_QS_DIALOG) { + Slog.w(TAG, "Attempted to add QS dialog window with unknown token " + + attrs.token + ". Aborting."); + return WindowManagerGlobal.ADD_BAD_APP_TOKEN; + } if (type == TYPE_ACCESSIBILITY_OVERLAY) { Slog.w(TAG, "Attempted to add Accessibility overlay window with unknown token " + attrs.token + ". Aborting."); -- 2.11.0