Showing task selection menus.

These popup menus are shown when a clicked app has > 1 recent tasks.
We still activate the latest task in addition to showing the menu.

Showing task icons in the menu, as well as showing the menu in its UX-polished
form is not yet implemented.

Bug: 20024603
Change-Id: I05e8fe9592501fdd01cbc9032080e4286a658a65
diff --git a/packages/SystemUI/res/layout/shelf_menu_anchor.xml b/packages/SystemUI/res/layout/shelf_menu_anchor.xml
new file mode 100644
index 0000000..984f655
--- /dev/null
+++ b/packages/SystemUI/res/layout/shelf_menu_anchor.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+             android:layout_width="match_parent"
+             android:layout_height="match_parent"
+             android:alpha="0">
+    <ImageView android:id="@+id/shelf_menu_anchor_anchor"
+               android:layout_width="wrap_content"
+               android:layout_height="wrap_content"
+               android:alpha="0"/>
+</FrameLayout>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AppButtonData.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AppButtonData.java
index 2c6987c..f6c1062 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AppButtonData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AppButtonData.java
@@ -34,11 +34,15 @@
         this.pinned = pinned;
     }
 
+    public int getTaskCount() {
+        return tasks == null ? 0 : tasks.size();
+    }
+
     /**
      * Returns true if the button contains no useful information and should be removed.
      */
     public boolean isEmpty() {
-        return !pinned && (tasks == null || tasks.isEmpty());
+        return !pinned && getTaskCount() == 0;
     }
 
     public void addTask(RecentTaskInfo task) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarApps.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarApps.java
index 1c9b04f..57d11aa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarApps.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarApps.java
@@ -34,6 +34,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
+import android.graphics.PixelFormat;
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.os.RemoteException;
@@ -43,10 +44,14 @@
 import android.util.Slog;
 import android.view.DragEvent;
 import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.WindowManager;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
+import android.widget.PopupMenu;
 import android.widget.Toast;
 
 import com.android.internal.content.PackageMonitor;
@@ -78,6 +83,7 @@
     private final UserManager mUserManager;
     private final LayoutInflater mLayoutInflater;
     private final AppPackageMonitor mAppPackageMonitor;
+    private final WindowManager mWindowManager;
 
 
     // This view has two roles:
@@ -103,11 +109,22 @@
         }
     };
 
+    // Layout params for the window that contains the anchor for the popup menus.
+    // We need to create a window for a popup menu because the NavBar window is too narrow and can't
+    // contain the menu.
+    private final WindowManager.LayoutParams mPopupAnchorLayoutParams;
+    // View that contains the anchor for popup menus. The view occupies the whole screen, and
+    // has a child that will be moved to make the menu to appear where we need it.
+    private final ViewGroup mPopupAnchor;
+    private final PopupMenu mPopupMenu;
+    private final int [] mClickedIconLocation = new int[2];
+
     public NavigationBarApps(Context context, AttributeSet attrs) {
         super(context, attrs);
         sAppsModel = new NavigationBarAppsModel(context);
         mPackageManager = context.getPackageManager();
-        mUserManager = (UserManager) getContext().getSystemService(Context.USER_SERVICE);
+        mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
+        mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
         mLayoutInflater = LayoutInflater.from(context);
         mAppPackageMonitor = new AppPackageMonitor();
 
@@ -131,6 +148,23 @@
         } catch (RemoteException e) {
             Slog.e(TAG, "registerTaskStackListener failed", e);
         }
+
+        mPopupAnchorLayoutParams =
+                new WindowManager.LayoutParams(
+                        ViewGroup.LayoutParams.MATCH_PARENT,
+                        ViewGroup.LayoutParams.MATCH_PARENT, 0, 0,
+                        WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY,
+                        WindowManager.LayoutParams.FLAG_FULLSCREEN
+                                | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED
+                                | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
+                        PixelFormat.TRANSLUCENT);
+        mPopupAnchorLayoutParams.setTitle("ShelfMenuAnchor");
+
+        mPopupAnchor = (ViewGroup) mLayoutInflater.inflate(R.layout.shelf_menu_anchor, null);
+
+        ImageView anchorButton =
+                (ImageView) mPopupAnchor.findViewById(R.id.shelf_menu_anchor_anchor);
+        mPopupMenu = new PopupMenu(context, anchorButton);
     }
 
     // Monitor that catches events like "app uninstalled".
@@ -606,13 +640,11 @@
             mContext.startActivityAsUser(launchIntent, optsBundle, appInfo.getUser());
         }
 
-        private void activateLatestTask(List<RecentTaskInfo> tasks) {
-            // 'tasks' is guaranteed to be non-empty.
-            int latestTaskPersistentId = tasks.get(0).persistentId;
+        private void activateTask(int taskPersistentId) {
             // Launch or bring the activity to front.
             IActivityManager manager = ActivityManagerNative.getDefault();
             try {
-                manager.startActivityFromRecents(latestTaskPersistentId, null /* options */);
+                manager.startActivityFromRecents(taskPersistentId, null /* options */);
             } catch (RemoteException e) {
                 Slog.e(TAG, "Exception when activating a recent task", e);
             } catch (IllegalArgumentException e) {
@@ -620,14 +652,83 @@
             }
         }
 
+        /**
+         * Adds to the popup menu items for activating each of tasks in the specified list.
+         */
+        void populateLaunchMenu(List<RecentTaskInfo> tasks) {
+            Menu menu = mPopupMenu.getMenu();
+            int taskCount = tasks.size();
+            for (int i = 0; i < taskCount; ++i) {
+                final RecentTaskInfo taskInfo = tasks.get(i);
+                MenuItem item = menu.add(getActivityForTask(taskInfo).flattenToShortString());
+                item.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
+                    @java.lang.Override
+                    public boolean onMenuItemClick(MenuItem item) {
+                        activateTask(taskInfo.persistentId);
+                        return true;
+                    }
+                });
+            }
+        }
+
+        /**
+         * Shows a task selection menu for clicked apps that have more than 1 running tasks.
+         */
+        void maybeShowLaunchMenu(ImageView appIcon) {
+            final AppButtonData appButtonData = (AppButtonData) appIcon.getTag();
+
+            if (appButtonData.getTaskCount() <= 1) return;
+
+            // Movable view inside the popup anchor view. It serves as the actual anchor for the
+            // menu.
+            final ImageView anchorButton =
+                    (ImageView) mPopupAnchor.findViewById(R.id.shelf_menu_anchor_anchor);
+            // Set same drawable as for the clicked button to have same size.
+            anchorButton.setImageDrawable(appIcon.getDrawable());
+
+            // Move the anchor button to the position of the app button.
+            appIcon.getLocationOnScreen(mClickedIconLocation);
+            anchorButton.setTranslationX(mClickedIconLocation[0]);
+            anchorButton.setTranslationY(mClickedIconLocation[1]);
+
+            final OnAttachStateChangeListener onAttachStateChangeListener =
+                    new OnAttachStateChangeListener() {
+                        @java.lang.Override
+                        public void onViewAttachedToWindow(View v) {
+                            mPopupMenu.show();
+                        }
+
+                        @java.lang.Override
+                        public void onViewDetachedFromWindow(View v) {}
+                    };
+            anchorButton.addOnAttachStateChangeListener(onAttachStateChangeListener);
+
+            populateLaunchMenu(appButtonData.tasks);
+
+            mPopupMenu.setOnDismissListener(new PopupMenu.OnDismissListener() {
+                @java.lang.Override
+                public void onDismiss(PopupMenu menu) {
+                    mWindowManager.removeView(mPopupAnchor);
+                    anchorButton.removeOnAttachStateChangeListener(onAttachStateChangeListener);
+                    mPopupMenu.setOnDismissListener(null);
+                    mPopupMenu.getMenu().clear();
+                }
+            });
+
+            mWindowManager.addView(mPopupAnchor, mPopupAnchorLayoutParams);
+        }
+
         @Override
         public void onClick(View v) {
-            AppButtonData appButtonData = (AppButtonData)v.getTag();
+            AppButtonData appButtonData = (AppButtonData) v.getTag();
 
-            if (appButtonData.tasks == null || appButtonData.tasks.size() == 0) {
+            if (appButtonData.getTaskCount() == 0) {
                 launchApp(appButtonData.appInfo, v);
             } else {
-                activateLatestTask(appButtonData.tasks);
+                // Activate latest task.
+                activateTask(appButtonData.tasks.get(0).persistentId);
+
+                maybeShowLaunchMenu((ImageView) v);
             }
         }
     }