diff options
4 files changed, 110 insertions, 130 deletions
diff --git a/packages/SystemUI/res/layout/navigation_bar_with_apps.xml b/packages/SystemUI/res/layout/navigation_bar_with_apps.xml index 4c06123968d4..d29b90025ef0 100644 --- a/packages/SystemUI/res/layout/navigation_bar_with_apps.xml +++ b/packages/SystemUI/res/layout/navigation_bar_with_apps.xml @@ -91,42 +91,10 @@ android:src="@drawable/nav_app_divider" /> - <!-- TODO: Build the list of icons dynamically. --> <com.android.systemui.statusbar.phone.NavigationBarRecents android:layout_width="wrap_content" android:layout_height="match_parent" - > - <ImageView android:id="@+id/recent0" - android:layout_width="48dp" - android:layout_height="48dp" - android:layout_marginLeft="8dp" - android:layout_marginRight="8dp" - android:padding="6dp" - android:layout_gravity="center" - android:scaleType="centerInside" - android:visibility="invisible" - /> - <ImageView android:id="@+id/recent1" - android:layout_width="48dp" - android:layout_height="48dp" - android:layout_marginLeft="8dp" - android:layout_marginRight="8dp" - android:padding="6dp" - android:layout_gravity="center" - android:scaleType="centerInside" - android:visibility="invisible" - /> - <ImageView android:id="@+id/recent2" - android:layout_width="48dp" - android:layout_height="48dp" - android:layout_marginLeft="8dp" - android:layout_marginRight="8dp" - android:padding="6dp" - android:layout_gravity="center" - android:scaleType="centerInside" - android:visibility="invisible" - /> - </com.android.systemui.statusbar.phone.NavigationBarRecents> + /> </LinearLayout> <FrameLayout @@ -287,42 +255,10 @@ android:src="@drawable/nav_app_divider" /> - <!-- TODO: Build the list of icons dynamically. --> - <com.android.systemui.statusbar.phone.NavigationBarRecents - android:layout_width="wrap_content" - android:layout_height="match_parent" - > - <ImageView android:id="@+id/recent0" - android:layout_width="48dp" - android:layout_height="48dp" - android:layout_marginLeft="8dp" - android:layout_marginRight="8dp" - android:padding="6dp" - android:layout_gravity="center" - android:scaleType="centerInside" - android:visibility="invisible" - /> - <ImageView android:id="@+id/recent1" - android:layout_width="48dp" - android:layout_height="48dp" - android:layout_marginLeft="8dp" - android:layout_marginRight="8dp" - android:padding="6dp" - android:layout_gravity="center" - android:scaleType="centerInside" - android:visibility="invisible" - /> - <ImageView android:id="@+id/recent2" - android:layout_width="48dp" - android:layout_height="48dp" - android:layout_marginLeft="8dp" - android:layout_marginRight="8dp" - android:padding="6dp" - android:layout_gravity="center" - android:scaleType="centerInside" - android:visibility="invisible" - /> - </com.android.systemui.statusbar.phone.NavigationBarRecents> + <com.android.systemui.statusbar.phone.NavigationBarRecents + android:layout_width="wrap_content" + android:layout_height="match_parent" + /> </LinearLayout> <FrameLayout 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 ddbcd331da69..ce2dd458be80 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarApps.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarApps.java @@ -29,7 +29,7 @@ import android.graphics.Rect; import android.os.Bundle; import android.os.UserHandle; import android.util.AttributeSet; -import android.util.Log; +import android.util.Slog; import android.view.DragEvent; import android.view.LayoutInflater; import android.view.View; @@ -111,21 +111,29 @@ class NavigationBarApps extends LinearLayout { return button; } + // Not shared with NavigationBarRecents because the data model is specific to pinned apps. private class AppLongClickListener implements View.OnLongClickListener { @Override public boolean onLongClick(View v) { mDragView = v; + ImageView icon = (ImageView) v; ComponentName activityName = mAppsModel.getApp(indexOfChild(v)); - // The drag data is an Intent to launch the activity. - Intent mainIntent = Intent.makeMainActivity(activityName); - ClipData dragData = ClipData.newIntent("", mainIntent); - // Use the ImageView to create the shadow. - View.DragShadowBuilder shadow = new AppIconDragShadowBuilder((ImageView) v); - v.startDrag(dragData, shadow, null /* myLocalState */, 0 /* flags */); + startAppDrag(icon, activityName); return true; } } + /** Helper function to start dragging an app icon (either pinned or recent). */ + static void startAppDrag(ImageView icon, ComponentName activityName) { + // The drag data is an Intent to launch the activity. + Intent mainIntent = Intent.makeMainActivity(activityName); + ClipData dragData = ClipData.newIntent("", mainIntent); + // Use the ImageView to create the shadow. + View.DragShadowBuilder shadow = new AppIconDragShadowBuilder(icon); + // Use a global drag because the icon might be dragged into the launcher. + icon.startDrag(dragData, shadow, null /* myLocalState */, View.DRAG_FLAG_GLOBAL); + } + @Override public boolean dispatchDragEvent(DragEvent event) { // ACTION_DRAG_ENTERED is handled by each individual app icon drag listener. @@ -161,7 +169,7 @@ class NavigationBarApps extends LinearLayout { * an app shortcut and will be accepted for a drop. */ private boolean onDragStarted(DragEvent event) { - if (DEBUG) Log.d(TAG, "onDragStarted"); + if (DEBUG) Slog.d(TAG, "onDragStarted"); // Ensure that an app shortcut is being dragged. if (!canAcceptDrag(event)) { @@ -194,7 +202,7 @@ class NavigationBarApps extends LinearLayout { * needs to use LinearLayout/ViewGroup methods. */ private void onDragEnteredIcon(View target) { - if (DEBUG) Log.d(TAG, "onDragEntered " + indexOfChild(target)); + if (DEBUG) Slog.d(TAG, "onDragEntered " + indexOfChild(target)); // If the drag didn't start from an existing icon, add an invisible placeholder to create // empty space for the user to drag into. @@ -227,7 +235,7 @@ class NavigationBarApps extends LinearLayout { } private boolean onDrop(DragEvent event) { - if (DEBUG) Log.d(TAG, "onDrop"); + if (DEBUG) Slog.d(TAG, "onDrop"); int dragViewIndex = indexOfChild(mDragView); if (mAppsModel.getApp(dragViewIndex) == null) { @@ -285,7 +293,7 @@ class NavigationBarApps extends LinearLayout { /** Cleans up at the end of the drag. */ private boolean onDragEnded() { - if (DEBUG) Log.d(TAG, "onDragEnded"); + if (DEBUG) Slog.d(TAG, "onDragEnded"); if (mDragView != null) { // The icon wasn't dropped into the app list. Remove the placeholder. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarRecents.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarRecents.java index 64bca34b2160..a645e2267968 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarRecents.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarRecents.java @@ -20,7 +20,6 @@ import android.app.ActivityManager; import android.app.ActivityManagerNative; import android.app.IActivityManager; import android.app.ITaskStackListener; -import android.content.ClipData; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -28,7 +27,10 @@ import android.content.pm.PackageManager; import android.os.Handler; import android.os.RemoteException; import android.os.UserHandle; +import android.util.ArraySet; import android.util.AttributeSet; +import android.util.Slog; +import android.view.LayoutInflater; import android.view.View; import android.widget.ImageView; import android.widget.LinearLayout; @@ -36,6 +38,7 @@ import android.widget.LinearLayout; import com.android.systemui.R; import java.util.List; +import java.util.Set; /** * Recent task icons appearing in the navigation bar. Touching an icon brings the activity to the @@ -43,21 +46,31 @@ import java.util.List; * icons. */ class NavigationBarRecents extends LinearLayout { + private final static boolean DEBUG = false; private final static String TAG = "NavigationBarRecents"; - private final static int[] RECENT_APP_BUTTON_IDS = { R.id.recent0, R.id.recent1, R.id.recent2 }; + // Maximum number of icons to show. + // TODO: Implement an overflow UI so the shelf can display an unlimited number of recents. + private final static int MAX_RECENTS = 10; private final ActivityManager mActivityManager; private final PackageManager mPackageManager; + private final LayoutInflater mLayoutInflater; private final TaskStackListenerImpl mTaskStackListener; + // Recent tasks being displayed in the shelf. + private final Set<ComponentName> mCurrentTasks = new ArraySet<ComponentName>(MAX_RECENTS); public NavigationBarRecents(Context context, AttributeSet attrs) { super(context, attrs); mActivityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); mPackageManager = getContext().getPackageManager(); + mLayoutInflater = LayoutInflater.from(context); // Listen for task stack changes and refresh when they happen. Update notifications happen // on an IPC thread, so use Handler to handle the message on the main thread. + // TODO: This has too much latency. It only adds the icon when app launch is completed + // and the launch animation is done playing. This class should add the icon immediately + // when the launch starts. Handler handler = new Handler(); mTaskStackListener = new TaskStackListenerImpl(handler); IActivityManager iam = ActivityManagerNative.getDefault(); @@ -68,54 +81,86 @@ class NavigationBarRecents extends LinearLayout { } } - @Override - protected void onFinishInflate() { - super.onFinishInflate(); - - // Set up the buttons. - for (int i = 0; i < RECENT_APP_BUTTON_IDS.length; i++) { - ImageView button = getRecentAppButton(i); - button.setOnLongClickListener(AppLongClickListener.getInstance()); - } - - // TODO: When is the right time to do the initial update? - updateRecentApps(); - } - - private ImageView getRecentAppButton(int index) { - return (ImageView) findViewById(RECENT_APP_BUTTON_IDS[index]); - } - private void updateRecentApps() { // TODO: Should this be getRunningTasks? - List<ActivityManager.RecentTaskInfo> tasks = mActivityManager.getRecentTasksForUser( - RECENT_APP_BUTTON_IDS.length, + // TODO: Query other UserHandles? + List<ActivityManager.RecentTaskInfo> recentTasks = mActivityManager.getRecentTasksForUser( + MAX_RECENTS, ActivityManager.RECENT_IGNORE_HOME_STACK_TASKS | ActivityManager.RECENT_IGNORE_UNAVAILABLE | ActivityManager.RECENT_INCLUDE_PROFILES | ActivityManager.RECENT_WITH_EXCLUDED, UserHandle.USER_CURRENT); - // Show the recent icons with the oldest on the left. - int buttonIndex = 0; - int taskIndex = tasks.size() - 1; - while (taskIndex >= 0 && buttonIndex < RECENT_APP_BUTTON_IDS.length) { - updateRecentAppButton(getRecentAppButton(buttonIndex), tasks.get(taskIndex)); - taskIndex--; - buttonIndex++; + if (DEBUG) Slog.d(TAG, "Got recents " + recentTasks.size()); + removeMissingRecents(recentTasks); + addNewRecents(recentTasks); + } + + // Remove any icons that disappeared from recents. + private void removeMissingRecents(List<ActivityManager.RecentTaskInfo> recentTasks) { + // Extract the component names. + Set<ComponentName> recentComponents = new ArraySet<ComponentName>(recentTasks.size()); + for (ActivityManager.RecentTaskInfo task : recentTasks) { + ComponentName component = task.baseIntent.getComponent(); + if (component == null) { // It's unclear if this can happen in practice. + continue; + } + recentComponents.add(component); } - // Hide the unused buttons. - while (buttonIndex < RECENT_APP_BUTTON_IDS.length) { - hideButton(getRecentAppButton(buttonIndex)); - buttonIndex++; + + // Start with a copy of the currently displayed tasks. + Set<ComponentName> removed = new ArraySet<ComponentName>(mCurrentTasks); + // Remove all the entries that still exist in recents. + removed.removeAll(recentComponents); + // The remaining entries no longer exist in recents, so remove their icons. + for (ComponentName task : removed) { + removeIcon(task); } } - private void updateRecentAppButton(ImageView button, ActivityManager.RecentTaskInfo task) { - ComponentName component = task.baseIntent.getComponent(); - if (component == null) { - hideButton(button); - return; + // Removes the icon for a task. + private void removeIcon(ComponentName task) { + for (int i = 0; i < getChildCount(); i++) { + ComponentName childTask = (ComponentName) getChildAt(i).getTag(); + if (childTask.equals(task)) { + if (DEBUG) Slog.d(TAG, "removing missing " + task); + removeViewAt(i); + mCurrentTasks.remove(task); + return; + } } + } + + // Adds new tasks at the end of the icon list. + private void addNewRecents(List<ActivityManager.RecentTaskInfo> recentTasks) { + for (ActivityManager.RecentTaskInfo task : recentTasks) { + // Don't overflow the list. + if (getChildCount() >= MAX_RECENTS) { + return; + } + ComponentName component = task.baseIntent.getComponent(); + if (component == null) { // It's unclear if this can happen in practice. + continue; + } + // Don't add tasks that are already being shown. + if (mCurrentTasks.contains(component)) { + continue; + } + addRecentAppButton(task); + } + } + + // Adds an icon at the end of the list to represent an activity for a given component. + private void addRecentAppButton(ActivityManager.RecentTaskInfo task) { + // Add this task to the currently-shown set. + ComponentName component = task.baseIntent.getComponent(); + mCurrentTasks.add(component); + if (DEBUG) Slog.d(TAG, "adding " + component); + + ImageView button = (ImageView) mLayoutInflater.inflate( + R.layout.navigation_bar_app_item, this, false /* attachToRoot */); + button.setOnLongClickListener(AppLongClickListener.getInstance()); + addView(button); // Use the View's tag to store metadata for drag and drop. button.setTag(component); @@ -134,11 +179,6 @@ class NavigationBarRecents extends LinearLayout { }); } - private void hideButton(ImageView button) { - button.setImageDrawable(null); - button.setVisibility(View.GONE); - } - /** * A listener that updates the app buttons whenever the recents task stack changes. * NOTE: This is not the right way to do this. @@ -173,13 +213,9 @@ class NavigationBarRecents extends LinearLayout { @Override public boolean onLongClick(View v) { + ImageView icon = (ImageView) v; ComponentName activityName = (ComponentName) v.getTag(); - // The drag data is an Intent to launch the activity. - Intent mainIntent = Intent.makeMainActivity(activityName); - ClipData dragData = ClipData.newIntent("", mainIntent); - // Use the ImageView to create the shadow. - View.DragShadowBuilder shadow = new AppIconDragShadowBuilder((ImageView) v); - v.startDrag(dragData, shadow, null /* myLocalState */, 0 /* flags */); + NavigationBarApps.startAppDrag(icon, activityName); return true; } } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 8431da215be6..3a83b6c79970 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -391,7 +391,7 @@ public final class ActivityManagerService extends ActivityManagerNative static final int LAST_PREBOOT_DELIVERED_FILE_VERSION = 10000; // Delay in notifying task stack change listeners (in millis) - static final int NOTIFY_TASK_STACK_CHANGE_LISTENERS_DELAY = 1000; + static final int NOTIFY_TASK_STACK_CHANGE_LISTENERS_DELAY = 100; // Necessary ApplicationInfo flags to mark an app as persistent private static final int PERSISTENT_MASK = |