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);
}
}
}