Initial changes to support swiping on the nav bar to switch affiliated tasks.
- Actual sideways animations to come once they've been finalized
Bug: 16846966
Change-Id: If6d40495498197a86a98f9b03f54ced3d2baf64a
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 3d712b7..26e5ce3 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -145,6 +145,8 @@
<integer name="recents_animate_task_view_remove_duration">250</integer>
<!-- The minimum alpha for the dim applied to cards that go deeper into the stack. -->
<integer name="recents_max_task_stack_view_dim">96</integer>
+ <!-- The number of tasks that RecentsTaskLoader should load. -->
+ <integer name="recents_max_num_tasks_to_load">50</integer>
<!-- Transposes the recents layout in landscape. -->
<bool name="recents_transpose_layout_with_orientation">true</bool>
diff --git a/packages/SystemUI/src/com/android/systemui/RecentsComponent.java b/packages/SystemUI/src/com/android/systemui/RecentsComponent.java
index 41d5984..dd93389 100644
--- a/packages/SystemUI/src/com/android/systemui/RecentsComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/RecentsComponent.java
@@ -29,5 +29,7 @@
void toggleRecents(Display display, int layoutDirection, View statusBarView);
void preloadRecents();
void cancelPreloadingRecents();
+ void showNextAffiliatedTask();
+ void showPrevAffiliatedTask();
void setCallback(Callbacks cb);
}
diff --git a/packages/SystemUI/src/com/android/systemui/recent/Recents.java b/packages/SystemUI/src/com/android/systemui/recent/Recents.java
index 4cf5fe1..9a55590 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/Recents.java
@@ -274,6 +274,20 @@
}
@Override
+ public void showNextAffiliatedTask() {
+ if (mUseAlternateRecents) {
+ mAlternateRecents.onShowNextAffiliatedTask();
+ }
+ }
+
+ @Override
+ public void showPrevAffiliatedTask() {
+ if (mUseAlternateRecents) {
+ mAlternateRecents.onShowPrevAffiliatedTask();
+ }
+ }
+
+ @Override
public void setCallback(Callbacks cb) {
if (mUseAlternateRecents) {
mAlternateRecents.setRecentsComponentCallback(cb);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
index bb5fe54..efb7a2c 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
@@ -34,8 +34,10 @@
import com.android.systemui.RecentsComponent;
import com.android.systemui.recents.misc.Console;
import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.recents.misc.Utilities;
import com.android.systemui.recents.model.RecentsTaskLoader;
import com.android.systemui.recents.model.Task;
+import com.android.systemui.recents.model.TaskGrouping;
import com.android.systemui.recents.model.TaskStack;
import com.android.systemui.recents.views.TaskStackView;
import com.android.systemui.recents.views.TaskStackViewLayoutAlgorithm;
@@ -165,6 +167,77 @@
// Do nothing
}
+ void showRelativeAffiliatedTask(boolean showNextTask) {
+ TaskStack stack = RecentsTaskLoader.getShallowTaskStack(mSystemServicesProxy,
+ Integer.MAX_VALUE);
+ // Return early if there are no tasks
+ if (stack.getTaskCount() == 0) return;
+
+ ActivityManager.RunningTaskInfo runningTask = getTopMostTask();
+ // Return early if the running task is in the home stack (optimization)
+ if (mSystemServicesProxy.isInHomeStack(runningTask.id)) return;
+
+ // Find the task in the recents list
+ ArrayList<Task> tasks = stack.getTasks();
+ Task toTask = null;
+ ActivityOptions launchOpts = null;
+ int taskCount = tasks.size();
+ for (int i = 0; i < taskCount; i++) {
+ Task task = tasks.get(i);
+ if (task.key.id == runningTask.id) {
+ TaskGrouping group = task.group;
+ Task.TaskKey toTaskKey;
+ if (showNextTask) {
+ toTaskKey = group.getNextTaskInGroup(task);
+ // XXX: We will actually set the appropriate launch animations here
+ } else {
+ toTaskKey = group.getPrevTaskInGroup(task);
+ // XXX: We will actually set the appropriate launch animations here
+ }
+ if (toTaskKey != null) {
+ toTask = stack.findTaskWithId(toTaskKey.id);
+ }
+ break;
+ }
+ }
+
+ // Return early if there is no next task
+ if (toTask == null) {
+ // XXX: We will actually show a bounce animation here
+ return;
+ }
+
+ // Launch the task
+ if (toTask.isActive) {
+ // Bring an active task to the foreground
+ mSystemServicesProxy.moveTaskToFront(toTask.key.id, launchOpts);
+ } else {
+ // Launch the activity anew with the desired animation
+ boolean isDocument = Utilities.isDocument(toTask.key.baseIntent);
+ Intent intent = new Intent(toTask.key.baseIntent);
+ intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
+ | Intent.FLAG_ACTIVITY_TASK_ON_HOME);
+ if (!isDocument) {
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ }
+ try {
+ mSystemServicesProxy.startActivityFromRecents(toTask.key.id, launchOpts);
+ } catch (ActivityNotFoundException anfe) {}
+
+ // Remove the old task from activity manager
+ RecentsTaskLoader.getInstance().getSystemServicesProxy().removeTask(toTask.key.id,
+ isDocument);
+ }
+ }
+
+ public void onShowNextAffiliatedTask() {
+ showRelativeAffiliatedTask(true);
+ }
+
+ public void onShowPrevAffiliatedTask() {
+ showRelativeAffiliatedTask(false);
+ }
+
public void onConfigurationChanged(Configuration newConfig) {
mConfig = RecentsConfiguration.reinitialize(mContext, mSystemServicesProxy);
mConfig.updateOnConfigurationChange();
@@ -318,7 +391,7 @@
/** Returns the transition rect for the given task id. */
Rect getThumbnailTransitionRect(int runningTaskId) {
// Get the stack of tasks that we are animating into
- TaskStack stack = RecentsTaskLoader.getShallowTaskStack(mSystemServicesProxy);
+ TaskStack stack = RecentsTaskLoader.getShallowTaskStack(mSystemServicesProxy, -1);
if (stack.getTaskCount() == 0) {
return new Rect();
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
index dabeadf..65e7076 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
@@ -58,6 +58,9 @@
boolean isLandscape;
boolean transposeRecentsLayoutWithOrientation;
+ /** Loading */
+ public int maxNumTasksToLoad;
+
/** Search bar */
int searchBarAppWidgetId = -1;
public int searchBarSpaceHeightPx;
@@ -162,8 +165,7 @@
}
// Layout
- isLandscape = res.getConfiguration().orientation ==
- Configuration.ORIENTATION_LANDSCAPE;
+ isLandscape = res.getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE;
transposeRecentsLayoutWithOrientation =
res.getBoolean(R.bool.recents_transpose_layout_with_orientation);
@@ -180,6 +182,9 @@
filteringNewViewsAnimDuration =
res.getInteger(R.integer.recents_filter_animate_new_views_duration);
+ // Loading
+ maxNumTasksToLoad = res.getInteger(R.integer.recents_max_num_tasks_to_load);
+
// Search Bar
searchBarSpaceHeightPx = res.getDimensionPixelSize(R.dimen.recents_search_bar_space_height);
searchBarAppWidgetId = settings.getInt(Constants.Values.App.Key_SearchAppWidgetId, -1);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
index 8716184..0e2f370 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
@@ -295,9 +295,15 @@
}
/** Gets the list of recent tasks, ordered from back to front. */
- private static List<ActivityManager.RecentTaskInfo> getRecentTasks(SystemServicesProxy ssp) {
+ private static List<ActivityManager.RecentTaskInfo> getRecentTasks(SystemServicesProxy ssp,
+ int numTasks) {
+ // Set a default number of tasks to query if none is provided
+ if (numTasks < 0) {
+ RecentsConfiguration config = RecentsConfiguration.getInstance();
+ numTasks = config.maxNumTasksToLoad;
+ }
List<ActivityManager.RecentTaskInfo> tasks =
- ssp.getRecentTasks(50, UserHandle.CURRENT.getIdentifier());
+ ssp.getRecentTasks(numTasks, UserHandle.CURRENT.getIdentifier());
Collections.reverse(tasks);
return tasks;
}
@@ -313,7 +319,7 @@
// Get the recent tasks
SystemServicesProxy ssp = mSystemServicesProxy;
- List<ActivityManager.RecentTaskInfo> tasks = getRecentTasks(ssp);
+ List<ActivityManager.RecentTaskInfo> tasks = getRecentTasks(ssp, -1);
// From back to front, add each task to the task stack
int taskCount = tasks.size();
@@ -395,9 +401,9 @@
}
/** Creates a lightweight stack of the current recent tasks, without thumbnails and icons. */
- public static TaskStack getShallowTaskStack(SystemServicesProxy ssp) {
+ public static TaskStack getShallowTaskStack(SystemServicesProxy ssp, int numTasks) {
RecentsConfiguration config = RecentsConfiguration.getInstance();
- List<ActivityManager.RecentTaskInfo> tasks = getRecentTasks(ssp);
+ List<ActivityManager.RecentTaskInfo> tasks = getRecentTasks(ssp, numTasks);
TaskStack stack = new TaskStack();
int taskCount = tasks.size();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/TaskGrouping.java b/packages/SystemUI/src/com/android/systemui/recents/model/TaskGrouping.java
index 326485e..288f07c 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskGrouping.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskGrouping.java
@@ -43,6 +43,24 @@
updateTaskIndices();
}
+ /** Returns the key of the next task in the group. */
+ public Task.TaskKey getNextTaskInGroup(Task t) {
+ int i = indexOf(t);
+ if ((i + 1) < getTaskCount()) {
+ return mTaskKeys.get(i + 1);
+ }
+ return null;
+ }
+
+ /** Returns the key of the previous task in the group. */
+ public Task.TaskKey getPrevTaskInGroup(Task t) {
+ int i = indexOf(t);
+ if ((i - 1) >= 0) {
+ return mTaskKeys.get(i - 1);
+ }
+ return null;
+ }
+
/** Gets the front task */
public boolean isFrontMostTask(Task t) {
return (t.key == mFrontMostTaskKey);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
index 435eb42..0269141 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
@@ -266,6 +266,19 @@
return mTaskList.indexOf(t);
}
+ /** Finds the task with the specified task id. */
+ public Task findTaskWithId(int taskId) {
+ ArrayList<Task> tasks = mTaskList.getTasks();
+ int taskCount = tasks.size();
+ for (int i = 0; i < taskCount; i++) {
+ Task task = tasks.get(i);
+ if (task.key.id == taskId) {
+ return task;
+ }
+ }
+ return null;
+ }
+
/******** Filtering ********/
/** Filters the stack into tasks similar to the one specified */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 8319f41..2df173e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -109,12 +109,14 @@
protected static final int MSG_TOGGLE_RECENTS_APPS = 1021;
protected static final int MSG_PRELOAD_RECENT_APPS = 1022;
protected static final int MSG_CANCEL_PRELOAD_RECENT_APPS = 1023;
- protected static final int MSG_OPEN_SEARCH_PANEL = 1024;
- protected static final int MSG_CLOSE_SEARCH_PANEL = 1025;
- protected static final int MSG_SHOW_HEADS_UP = 1026;
- protected static final int MSG_HIDE_HEADS_UP = 1027;
- protected static final int MSG_ESCALATE_HEADS_UP = 1028;
- protected static final int MSG_DECAY_HEADS_UP = 1029;
+ protected static final int MSG_SHOW_NEXT_AFFILIATED_TASK = 1024;
+ protected static final int MSG_SHOW_PREV_AFFILIATED_TASK = 1025;
+ protected static final int MSG_OPEN_SEARCH_PANEL = 1026;
+ protected static final int MSG_CLOSE_SEARCH_PANEL = 1027;
+ protected static final int MSG_SHOW_HEADS_UP = 1028;
+ protected static final int MSG_HIDE_HEADS_UP = 1029;
+ protected static final int MSG_ESCALATE_HEADS_UP = 1030;
+ protected static final int MSG_DECAY_HEADS_UP = 1031;
protected static final boolean ENABLE_HEADS_UP = true;
// scores above this threshold should be displayed in heads up mode.
@@ -689,6 +691,20 @@
mHandler.sendEmptyMessage(msg);
}
+ /** Jumps to the next affiliated task in the group. */
+ public void showNextAffiliatedTask() {
+ int msg = MSG_SHOW_NEXT_AFFILIATED_TASK;
+ mHandler.removeMessages(msg);
+ mHandler.sendEmptyMessage(msg);
+ }
+
+ /** Jumps to the previous affiliated task in the group. */
+ public void showPreviousAffiliatedTask() {
+ int msg = MSG_SHOW_PREV_AFFILIATED_TASK;
+ mHandler.removeMessages(msg);
+ mHandler.sendEmptyMessage(msg);
+ }
+
@Override
public void showSearchPanel() {
int msg = MSG_OPEN_SEARCH_PANEL;
@@ -800,6 +816,18 @@
}
}
+ protected void showRecentsNextAffiliatedTask() {
+ if (mRecents != null) {
+ mRecents.showNextAffiliatedTask();
+ }
+ }
+
+ protected void showRecentsPreviousAffiliatedTask() {
+ if (mRecents != null) {
+ mRecents.showPrevAffiliatedTask();
+ }
+ }
+
@Override
public void onVisibilityChanged(boolean visible) {
// Do nothing
@@ -884,6 +912,12 @@
case MSG_CANCEL_PRELOAD_RECENT_APPS:
cancelPreloadingRecents();
break;
+ case MSG_SHOW_NEXT_AFFILIATED_TASK:
+ showRecentsNextAffiliatedTask();
+ break;
+ case MSG_SHOW_PREV_AFFILIATED_TASK:
+ showRecentsPreviousAffiliatedTask();
+ break;
case MSG_OPEN_SEARCH_PANEL:
if (DEBUG) Log.d(TAG, "opening search panel");
if (mSearchPanelView != null && mSearchPanelView.isAssistantAvailable()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 95cb9a1..3592971 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -37,12 +37,9 @@
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
-import android.view.accessibility.AccessibilityManager;
-import android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener;
import android.view.inputmethod.InputMethodManager;
import android.widget.ImageView;
import android.widget.LinearLayout;
-
import com.android.systemui.R;
import com.android.systemui.statusbar.BaseStatusBar;
import com.android.systemui.statusbar.DelegateViewHelper;
@@ -77,6 +74,7 @@
private Drawable mRecentIcon;
private Drawable mRecentLandIcon;
+ private NavigationBarViewTaskSwitchHelper mTaskSwitchHelper;
private DelegateViewHelper mDelegateHelper;
private DeadZone mDeadZone;
private final NavigationBarTransitions mBarTransitions;
@@ -177,6 +175,7 @@
mVertical = false;
mShowMenu = false;
mDelegateHelper = new DelegateViewHelper(this);
+ mTaskSwitchHelper = new NavigationBarViewTaskSwitchHelper(context);
getIcons(res);
@@ -192,6 +191,7 @@
}
public void setBar(BaseStatusBar phoneStatusBar) {
+ mTaskSwitchHelper.setBar(phoneStatusBar);
mDelegateHelper.setBar(phoneStatusBar);
}
@@ -201,6 +201,9 @@
@Override
public boolean onTouchEvent(MotionEvent event) {
+ if (mTaskSwitchHelper.onTouchEvent(event)) {
+ return true;
+ }
if (mDeadZone != null && event.getAction() == MotionEvent.ACTION_OUTSIDE) {
mDeadZone.poke(event);
}
@@ -213,7 +216,8 @@
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
- return mDelegateHelper.onInterceptTouchEvent(event);
+ return mTaskSwitchHelper.onInterceptTouchEvent(event) ||
+ mDelegateHelper.onInterceptTouchEvent(event);
}
private H mHandler = new H();
@@ -421,6 +425,8 @@
if (mDelegateHelper != null) {
mDelegateHelper.setSwapXY(mVertical);
}
+ boolean isRTL = (getLayoutDirection() == View.LAYOUT_DIRECTION_RTL);
+ mTaskSwitchHelper.setBarState(mVertical, isRTL);
setNavigationIconHints(mNavigationIconHints, true);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarViewTaskSwitchHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarViewTaskSwitchHelper.java
new file mode 100644
index 0000000..3c20d1f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarViewTaskSwitchHelper.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import android.content.Context;
+import android.view.GestureDetector;
+import android.view.MotionEvent;
+import android.view.ViewConfiguration;
+import com.android.systemui.statusbar.BaseStatusBar;
+
+public class NavigationBarViewTaskSwitchHelper extends GestureDetector.SimpleOnGestureListener {
+
+ private BaseStatusBar mBar;
+ private boolean mIsVertical;
+ private boolean mIsRTL;
+
+ private final GestureDetector mTaskSwitcherDetector;
+ private final int mScrollTouchSlop;
+ private final int mMinFlingVelocity;
+ private boolean mInterceptTouches;
+ private int mTouchDownX;
+
+ public NavigationBarViewTaskSwitchHelper(Context context) {
+ ViewConfiguration configuration = ViewConfiguration.get(context);
+ mScrollTouchSlop = configuration.getScaledTouchSlop();
+ mMinFlingVelocity = configuration.getScaledMinimumFlingVelocity();
+ mTaskSwitcherDetector = new GestureDetector(context, this);
+ }
+
+ public void setBar(BaseStatusBar phoneStatusBar) {
+ mBar = phoneStatusBar;
+ }
+
+ public void setBarState(boolean isVertical, boolean isRTL) {
+ mIsVertical = isVertical;
+ mIsRTL = isRTL;
+ }
+
+ public boolean onInterceptTouchEvent(MotionEvent event) {
+ // If we move more than a fixed amount, then start capturing for the
+ // task switcher detector
+ mTaskSwitcherDetector.onTouchEvent(event);
+ int action = event.getAction();
+ switch (action & MotionEvent.ACTION_MASK) {
+ case MotionEvent.ACTION_DOWN: {
+ mTouchDownX = (int) event.getX();
+ mInterceptTouches = false;
+ break;
+ }
+ case MotionEvent.ACTION_MOVE: {
+ int x = (int) event.getX();
+ if (Math.abs(x - mTouchDownX) > mScrollTouchSlop) {
+ mInterceptTouches = true;
+ return true;
+ }
+ break;
+ }
+ case MotionEvent.ACTION_CANCEL:
+ case MotionEvent.ACTION_UP:
+ mInterceptTouches = false;
+ break;
+ }
+ return mInterceptTouches;
+ }
+
+ public boolean onTouchEvent(MotionEvent event) {
+ if (!mInterceptTouches) return false;
+ return mTaskSwitcherDetector.onTouchEvent(event);
+ }
+
+ @Override
+ public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
+ float absVelX = Math.abs(velocityX);
+ float absVelY = Math.abs(velocityY);
+ boolean isValidFling = absVelX > mMinFlingVelocity &&
+ mIsVertical ? (absVelY > absVelX) : (absVelX > absVelY);
+ if (isValidFling) {
+ boolean showNext;
+ if (!mIsRTL) {
+ showNext = mIsVertical ? (velocityY < 0) : (velocityX < 0);
+ } else {
+ // In RTL, vertical is still the same, but horizontal is flipped
+ showNext = mIsVertical ? (velocityY < 0) : (velocityX > 0);
+ }
+ if (showNext) {
+ mBar.showNextAffiliatedTask();
+ } else {
+ mBar.showPreviousAffiliatedTask();
+ }
+ }
+ return true;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index a2f8931..6fff6c3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -183,7 +183,7 @@
private static final int MSG_OPEN_NOTIFICATION_PANEL = 1000;
private static final int MSG_CLOSE_PANELS = 1001;
private static final int MSG_OPEN_SETTINGS_PANEL = 1002;
- // 1020-1030 reserved for BaseStatusBar
+ // 1020-1040 reserved for BaseStatusBar
private static final boolean CLOSE_PANEL_WHEN_EMPTIED = true;