OSDN Git Service

Allow QS tiles to open dialogs
authorJason Monk <jmonk@google.com>
Wed, 18 Nov 2015 21:35:14 +0000 (16:35 -0500)
committerJason Monk <jmonk@google.com>
Thu, 3 Dec 2015 14:22:15 +0000 (09:22 -0500)
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

14 files changed:
api/current.txt
api/system-current.txt
api/test-current.txt
core/java/android/service/quicksettings/IQSService.aidl
core/java/android/service/quicksettings/IQSTileService.aidl
core/java/android/service/quicksettings/Tile.java
core/java/android/service/quicksettings/TileService.java
core/java/android/view/WindowManager.java
packages/SystemUI/src/com/android/systemui/qs/QSTileServiceWrapper.java
packages/SystemUI/src/com/android/systemui/qs/tiles/CustomTile.java
packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
services/core/java/com/android/server/policy/PhoneWindowManager.java
services/core/java/com/android/server/wm/AccessibilityController.java
services/core/java/com/android/server/wm/WindowManagerService.java

index d54b7fd..8f93361 100644 (file)
@@ -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";
   }
 
index 2ea5ebb..7a97280 100644 (file)
@@ -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";
   }
 
index 904347d..ddb5b06 100644 (file)
@@ -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";
   }
 
index 087eb61..7e70501 100644 (file)
@@ -23,4 +23,5 @@ import android.service.quicksettings.Tile;
  */
 interface IQSService {
     void updateQsTile(in Tile tile);
+    void onShowDialog(in Tile tile);
 }
index 6b46bee..63a4c5e 100644 (file)
@@ -27,5 +27,5 @@ oneway interface IQSTileService {
     void onTileRemoved();
     void onStartListening();
     void onStopListening();
-    void onClick();
+    void onClick(IBinder wtoken);
 }
index c8ae171..a53fc59 100644 (file)
@@ -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);
index eba4c6f..fd2d5b0 100644 (file)
@@ -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">
  *     <intent-filter>
- *         <action android:name="android.intent.action.QS_TILE" />
+ *         <action android:name="android.service.quicksettings.action.QS_TILE" />
  *     </intent-filter>
  * </service>}
  * </pre>
@@ -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.
      * <p/>
      * 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;
             }
index 1521f2e..d6bc27c 100644 (file)
@@ -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;
index 55f4736..a5e1fd5 100644 (file)
@@ -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);
         }
index d26e8d6..04006eb 100644 (file)
@@ -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<QSTile.State> {
     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<QSTile.State> {
     }
 
     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<QSTile.State> {
             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<QSTile.State> {
     @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());
         }
index 57c2648..f7ff8aa 100644 (file)
@@ -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,
index 639753a..9a7d153 100644 (file)
@@ -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:
index c246609..a4b4276 100644 (file)
@@ -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(
index 707450c..f153873 100644 (file)
@@ -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.");