Refactoring to update stack in onResume.

This CL fixes a long-standing issue in which Recents is not updated
correctly if it is not completely hidden and shown.  In particular, it
would cause animation issues when launching into a non-fullscreen
activity or if the user quickly toggles between recent tasks.  It
contains several fixes:

- The visual state in Recents is no longer reset until the activity is 
  fully hidden (onStop() is called), and the task stack state is saved 
  allowing us to return to the same initial state.  When restarting the 
  activity, we propagate whether the activity was hidden down the view 
  hierarchy, so that each task can decide whether to reset itself.
- When the recents activity is started, we now merge the new stack with
  the current stack instead of replacing it completely.  This unifies
  the logic when dismissing multi-window while Recents is open, and this
  CL fixes an issue with the merging where onStackTaskAdded() was called
  before the stack was updated with the new task.  As a result of this
  change, we can just rebind the task views without having to return and
  pick them up from the view pool.
- This CL also fixes an issue with flashing when the screen turns off.  
  The activity onStop() can be called before the activity is fully 
  hidden, which would trigger a reset(), which would return all views to
  the pool.

Bug: 23815609
Bug: 25411120
Bug: 27186407
Change-Id: I83d74c947f9b47766d6778b7f5c421bb6df833e9
This commit is contained in:
Winson
2016-02-17 13:27:33 -08:00
parent 8b030cce34
commit 8873754f66
15 changed files with 348 additions and 336 deletions

View File

@@ -513,8 +513,9 @@ public class Recents extends SystemUI
* Handle Recents activity visibility changed.
*/
public final void onBusEvent(final RecentsVisibilityChangedEvent event) {
int processUser = event.systemServicesProxy.getProcessUser();
if (event.systemServicesProxy.isSystemUser(processUser)) {
SystemServicesProxy ssp = Recents.getSystemServices();
int processUser = ssp.getProcessUser();
if (ssp.isSystemUser(processUser)) {
mImpl.onVisibilityChanged(event.applicationContext, event.visible);
} else {
postToSystemUser(new Runnable() {

View File

@@ -55,7 +55,7 @@ import com.android.systemui.recents.events.activity.IterateRecentsEvent;
import com.android.systemui.recents.events.activity.LaunchTaskFailedEvent;
import com.android.systemui.recents.events.activity.LaunchTaskSucceededEvent;
import com.android.systemui.recents.events.activity.ShowHistoryEvent;
import com.android.systemui.recents.events.activity.TaskStackUpdatedEvent;
import com.android.systemui.recents.events.activity.MultiWindowStateChangedEvent;
import com.android.systemui.recents.events.activity.ToggleRecentsEvent;
import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
import com.android.systemui.recents.events.component.ScreenPinningRequestEvent;
@@ -97,6 +97,7 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD
private long mLastTabKeyEventTime;
private boolean mFinishedOnStartup;
private boolean mIgnoreAltTabRelease;
private boolean mIsVisible;
// Top level views
private RecentsView mRecentsView;
@@ -174,59 +175,6 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD
}
};
/** Updates the set of recent tasks */
void updateRecentsTasks() {
// If AlternateRecentsComponent has preloaded a load plan, then use that to prevent
// reconstructing the task stack
RecentsTaskLoader loader = Recents.getTaskLoader();
RecentsTaskLoadPlan plan = RecentsImpl.consumeInstanceLoadPlan();
if (plan == null) {
plan = loader.createLoadPlan(this);
}
// Start loading tasks according to the load plan
RecentsConfiguration config = Recents.getConfiguration();
RecentsActivityLaunchState launchState = config.getLaunchState();
if (!plan.hasTasks()) {
loader.preloadTasks(plan, -1, launchState.launchedFromHome);
}
RecentsTaskLoadPlan.Options loadOpts = new RecentsTaskLoadPlan.Options();
loadOpts.runningTaskId = launchState.launchedToTaskId;
loadOpts.numVisibleTasks = launchState.launchedNumVisibleTasks;
loadOpts.numVisibleTaskThumbnails = launchState.launchedNumVisibleThumbnails;
loader.loadTasks(this, plan, loadOpts);
TaskStack stack = plan.getTaskStack();
mRecentsView.setTaskStack(stack);
// Animate the SystemUI scrims into view
Task launchTarget = stack.getLaunchTarget();
int taskCount = stack.getTaskCount();
int launchTaskIndexInStack = launchTarget != null
? stack.indexOfStackTask(launchTarget)
: 0;
boolean hasNavBarScrim = (taskCount > 0) && !config.hasTransposedNavBar;
boolean animateNavBarScrim = !launchState.launchedWhileDocking;
mScrimViews.prepareEnterRecentsAnimation(hasNavBarScrim, animateNavBarScrim);
// Keep track of whether we launched from the nav bar button or via alt-tab
if (launchState.launchedWithAltTab) {
MetricsLogger.count(this, "overview_trigger_alttab", 1);
} else {
MetricsLogger.count(this, "overview_trigger_nav_btn", 1);
}
// Keep track of whether we launched from an app or from home
if (launchState.launchedFromApp) {
MetricsLogger.count(this, "overview_source_app", 1);
// If from an app, track the stack index of the app in the stack (for affiliated tasks)
MetricsLogger.histogram(this, "overview_source_app_index", launchTaskIndexInStack);
} else {
MetricsLogger.count(this, "overview_source_home", 1);
}
// Keep track of the total stack task count
MetricsLogger.histogram(this, "overview_task_count", taskCount);
}
/**
* Dismisses the history view back into the stack view.
*/
@@ -345,6 +293,7 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD
// Set the Recents layout
setContentView(R.layout.recents);
takeKeyEvents(true);
mRecentsView = (RecentsView) findViewById(R.id.recents_view);
mRecentsView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
@@ -381,46 +330,13 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD
registerReceiver(mSystemBroadcastReceiver, filter);
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);
}
@Override
protected void onStart() {
super.onStart();
// Update the recent tasks
updateRecentsTasks();
// If this is a new instance from a configuration change, then we have to manually trigger
// the enter animation state, or if recents was relaunched by AM, without going through
// the normal mechanisms
RecentsConfiguration config = Recents.getConfiguration();
RecentsActivityLaunchState launchState = config.getLaunchState();
boolean wasLaunchedByAm = !launchState.launchedFromHome &&
!launchState.launchedFromApp;
if (launchState.launchedHasConfigurationChanged || wasLaunchedByAm) {
EventBus.getDefault().send(new EnterRecentsWindowAnimationCompletedEvent());
}
// Notify that recents is now visible
SystemServicesProxy ssp = Recents.getSystemServices();
EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, ssp, true));
EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, true));
MetricsLogger.visible(this, MetricsEvent.OVERVIEW_ACTIVITY);
mRecentsView.getViewTreeObserver().addOnPreDrawListener(
new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
mRecentsView.getViewTreeObserver().removeOnPreDrawListener(this);
EventBus.getDefault().post(new RecentsDrawnEvent());
return true;
}
});
}
@Override
@@ -430,26 +346,91 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD
}
@Override
protected void onPause() {
super.onPause();
protected void onResume() {
super.onResume();
// Stop the fast-toggle dozer
mIterateTrigger.stopDozing();
// If the Recents component has preloaded a load plan, then use that to prevent
// reconstructing the task stack
RecentsTaskLoader loader = Recents.getTaskLoader();
RecentsTaskLoadPlan loadPlan = RecentsImpl.consumeInstanceLoadPlan();
if (loadPlan == null) {
loadPlan = loader.createLoadPlan(this);
}
// Start loading tasks according to the load plan
RecentsConfiguration config = Recents.getConfiguration();
RecentsActivityLaunchState launchState = config.getLaunchState();
if (!loadPlan.hasTasks()) {
loader.preloadTasks(loadPlan, -1, launchState.launchedFromHome);
}
RecentsTaskLoadPlan.Options loadOpts = new RecentsTaskLoadPlan.Options();
loadOpts.runningTaskId = launchState.launchedToTaskId;
loadOpts.numVisibleTasks = launchState.launchedNumVisibleTasks;
loadOpts.numVisibleTaskThumbnails = launchState.launchedNumVisibleThumbnails;
loader.loadTasks(this, loadPlan, loadOpts);
TaskStack stack = loadPlan.getTaskStack();
mRecentsView.onResume(mIsVisible, stack);
// Animate the SystemUI scrims into view
Task launchTarget = stack.getLaunchTarget();
int taskCount = stack.getTaskCount();
int launchTaskIndexInStack = launchTarget != null
? stack.indexOfStackTask(launchTarget)
: 0;
boolean hasNavBarScrim = (taskCount > 0) && !config.hasTransposedNavBar;
boolean animateNavBarScrim = !launchState.launchedWhileDocking;
mScrimViews.prepareEnterRecentsAnimation(hasNavBarScrim, animateNavBarScrim);
// If this is a new instance from a configuration change, then we have to manually trigger
// the enter animation state, or if recents was relaunched by AM, without going through
// the normal mechanisms
boolean wasLaunchedByAm = !launchState.launchedFromHome &&
!launchState.launchedFromApp;
if (launchState.launchedHasConfigurationChanged || wasLaunchedByAm) {
EventBus.getDefault().send(new EnterRecentsWindowAnimationCompletedEvent());
}
mRecentsView.getViewTreeObserver().addOnPreDrawListener(
new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
mRecentsView.getViewTreeObserver().removeOnPreDrawListener(this);
EventBus.getDefault().post(new RecentsDrawnEvent());
return true;
}
});
// Keep track of whether we launched from the nav bar button or via alt-tab
if (launchState.launchedWithAltTab) {
MetricsLogger.count(this, "overview_trigger_alttab", 1);
} else {
MetricsLogger.count(this, "overview_trigger_nav_btn", 1);
}
// Keep track of whether we launched from an app or from home
if (launchState.launchedFromApp) {
MetricsLogger.count(this, "overview_source_app", 1);
// If from an app, track the stack index of the app in the stack (for affiliated tasks)
MetricsLogger.histogram(this, "overview_source_app_index", launchTaskIndexInStack);
} else {
MetricsLogger.count(this, "overview_source_home", 1);
}
// Keep track of the total stack task count
MetricsLogger.histogram(this, "overview_task_count", taskCount);
// After we have resumed, set the visible state until the next onStop() call
mIsVisible = true;
}
@Override
protected void onStop() {
super.onStop();
protected void onPause() {
super.onPause();
// Reset some states
mIgnoreAltTabRelease = false;
if (RecentsDebugFlags.Static.EnableHistory && mRecentsView.isHistoryVisible()) {
EventBus.getDefault().send(new HideHistoryEvent(false /* animate */));
}
// Notify that recents is now hidden
SystemServicesProxy ssp = Recents.getSystemServices();
EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, ssp, false));
mIterateTrigger.stopDozing();
// Workaround for b/22542869, if the RecentsActivity is started again, but without going
// through SystemUI, we need to reset the config launch flags to ensure that we do not
@@ -457,7 +438,20 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD
RecentsConfiguration config = Recents.getConfiguration();
RecentsActivityLaunchState launchState = config.getLaunchState();
launchState.reset();
}
@Override
protected void onStop() {
super.onStop();
// Only hide the history if Recents is completely hidden
if (RecentsDebugFlags.Static.EnableHistory && mRecentsView.isHistoryVisible()) {
EventBus.getDefault().send(new HideHistoryEvent(false /* animate */));
}
// Notify that recents is now hidden
mIsVisible = false;
EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, false));
MetricsLogger.hidden(this, MetricsEvent.OVERVIEW_ACTIVITY);
}
@@ -525,16 +519,22 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD
public void onMultiWindowChanged(boolean inMultiWindow) {
super.onMultiWindowChanged(inMultiWindow);
EventBus.getDefault().send(new ConfigurationChangedEvent());
// Reload the task stack completely
RecentsConfiguration config = Recents.getConfiguration();
RecentsActivityLaunchState launchState = config.getLaunchState();
RecentsTaskLoader loader = Recents.getTaskLoader();
RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options();
launchOpts.loadIcons = false;
launchOpts.loadThumbnails = false;
launchOpts.onlyLoadForCache = true;
RecentsTaskLoadPlan loadPlan = loader.createLoadPlan(this);
loader.preloadTasks(loadPlan, -1, false);
loader.loadTasks(this, loadPlan, launchOpts);
EventBus.getDefault().send(new TaskStackUpdatedEvent(loadPlan.getTaskStack(),
inMultiWindow));
loader.preloadTasks(loadPlan, -1 /* topTaskId */, false /* isTopTaskHome */);
RecentsTaskLoadPlan.Options loadOpts = new RecentsTaskLoadPlan.Options();
loadOpts.numVisibleTasks = launchState.launchedNumVisibleTasks;
loadOpts.numVisibleTaskThumbnails = launchState.launchedNumVisibleThumbnails;
loader.loadTasks(this, loadPlan, loadOpts);
mRecentsView.onResume(mIsVisible, loadPlan.getTaskStack());
EventBus.getDefault().send(new MultiWindowStateChangedEvent(inMultiWindow));
}
@Override

View File

@@ -602,7 +602,7 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener
com.android.internal.R.dimen.navigation_bar_width);
mTaskBarHeight = res.getDimensionPixelSize(
R.dimen.recents_task_bar_height);
mDummyStackView = new TaskStackView(mContext, new TaskStack());
mDummyStackView = new TaskStackView(mContext);
mHeaderBar = (TaskViewHeader) inflater.inflate(R.layout.recents_task_view_header,
null, false);
}
@@ -615,8 +615,7 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener
* is not already bound (can be expensive)
* @param stack the stack to initialize the stack layout with
*/
private void updateHeaderBarLayout(boolean tryAndBindSearchWidget,
TaskStack stack) {
private void updateHeaderBarLayout(boolean tryAndBindSearchWidget, TaskStack stack) {
RecentsConfiguration config = Recents.getConfiguration();
SystemServicesProxy ssp = Recents.getSystemServices();
Rect systemInsets = new Rect();
@@ -640,14 +639,15 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener
mSearchBarBounds, mTaskStackBounds);
// Rebind the header bar and draw it for the transition
TaskStackLayoutAlgorithm algo = mDummyStackView.getStackAlgorithm();
TaskStackLayoutAlgorithm stackLayout = mDummyStackView.getStackAlgorithm();
Rect taskStackBounds = new Rect(mTaskStackBounds);
algo.setSystemInsets(systemInsets);
stackLayout.setSystemInsets(systemInsets);
if (stack != null) {
algo.initialize(taskStackBounds,
stackLayout.initialize(taskStackBounds,
TaskStackLayoutAlgorithm.StackState.getStackStateForStack(stack));
mDummyStackView.setTasks(stack, false /* notifyStackChanges */);
}
Rect taskViewBounds = algo.getUntransformedTaskViewBounds();
Rect taskViewBounds = stackLayout.getUntransformedTaskViewBounds();
if (!taskViewBounds.equals(mLastTaskViewBounds)) {
mLastTaskViewBounds.set(taskViewBounds);
@@ -705,7 +705,6 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener
updateHeaderBarLayout(false /* tryAndBindSearchWidget */, stack);
// Update the destination rect
mDummyStackView.updateLayoutForStack(stack);
final Task toTask = new Task();
final TaskViewTransform toTransform = getThumbnailTransitionTransform(stackView, toTask);
ForegroundThread.getHandler().postAtFrontOfQueue(new Runnable() {
@@ -887,7 +886,6 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener
updateHeaderBarLayout(false /* tryAndBindSearchWidget */, stack);
// Prepare the dummy stack for the transition
mDummyStackView.updateLayoutForStack(stack);
TaskStackLayoutAlgorithm.VisibilityReport stackVr =
mDummyStackView.computeStackVisibilityReport();

View File

@@ -0,0 +1,31 @@
/*
* 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.
*/
package com.android.systemui.recents.events.activity;
import com.android.systemui.recents.events.EventBus;
/**
* This is sent by the activity whenever the multi-window state has changed.
*/
public class MultiWindowStateChangedEvent extends EventBus.Event {
public final boolean inMultiWindow;
public MultiWindowStateChangedEvent(boolean inMultiWindow) {
this.inMultiWindow = inMultiWindow;
}
}

View File

@@ -19,21 +19,18 @@ package com.android.systemui.recents.events.component;
import android.content.Context;
import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.misc.SystemServicesProxy;
/**
* This is sent when the visibility of the RecentsActivity for the current user changes.
* This is sent when the visibility of the RecentsActivity for the current user changes. Handlers
* of this event should not alter the UI, as the activity may still be visible.
*/
public class RecentsVisibilityChangedEvent extends EventBus.Event {
public final Context applicationContext;
public final SystemServicesProxy systemServicesProxy;
public final boolean visible;
public RecentsVisibilityChangedEvent(Context context, SystemServicesProxy systemServicesProxy,
boolean visible) {
public RecentsVisibilityChangedEvent(Context context, boolean visible) {
this.applicationContext = context.getApplicationContext();
this.systemServicesProxy = systemServicesProxy;
this.visible = visible;
}
}

View File

@@ -212,8 +212,7 @@ public class RecentsTaskLoadPlan {
// Initialize the stacks
mStack = new TaskStack();
mStack.setTasks(allTasks, false /* notifyStackChanges */);
mStack.createAffiliatedGroupings(mContext);
mStack.setTasks(mContext, allTasks, false /* notifyStackChanges */);
}
/**

View File

@@ -100,11 +100,7 @@ public class Task {
@Override
public String toString() {
return "Task.Key: " + id + ", "
+ "s: " + stackId + ", "
+ "u: " + userId + ", "
+ "lat: " + lastActiveTime + ", "
+ getComponent().getPackageName();
return "t" + id + ", s" + stackId + ", u" + userId;
}
private void updateHashCode() {
@@ -204,7 +200,9 @@ public class Task {
this.isDockable = isDockable;
}
/** Copies the other task. */
/**
* Copies the metadata from another task, but retains the current callbacks.
*/
public void copyFrom(Task o) {
this.key = o.key;
this.group = o.group;
@@ -300,11 +298,6 @@ public class Task {
@Override
public String toString() {
String groupAffiliation = "no group";
if (group != null) {
groupAffiliation = Integer.toString(group.affiliation);
}
return "Task (" + groupAffiliation + "): " + key +
" [" + super.toString() + "]";
return "[" + key.toString() + "] " + title;
}
}

View File

@@ -92,15 +92,6 @@ class FilteredTaskList {
}
}
/**
* Resets the task list, but does not remove the filter.
*/
void reset() {
mTasks.clear();
mFilteredTasks.clear();
mTaskIndices.clear();
}
/** Removes the task filter and returns the previous touch state */
void removeFilter() {
mFilter = null;
@@ -481,15 +472,6 @@ public class TaskStack {
mCb = cb;
}
/** Resets this TaskStack. */
public void reset() {
mCb = null;
mStackTaskList.reset();
mHistoryTaskList.reset();
mGroups.clear();
mAffinitiesGroups.clear();
}
/**
* Moves the given task to either the front of the freeform workspace or the stack.
*/
@@ -556,12 +538,12 @@ public class TaskStack {
* @param tasks the new set of tasks to replace the current set.
* @param notifyStackChanges whether or not to callback on specific changes to the list of tasks.
*/
public void setTasks(List<Task> tasks, boolean notifyStackChanges) {
public void setTasks(Context context, List<Task> tasks, boolean notifyStackChanges) {
// Compute a has set for each of the tasks
ArrayMap<Task.TaskKey, Task> currentTasksMap = createTaskKeyMapFromList(mRawTaskList);
ArrayMap<Task.TaskKey, Task> newTasksMap = createTaskKeyMapFromList(tasks);
ArrayList<Task> newTasks = new ArrayList<>();
ArrayList<Task> addedTasks = new ArrayList<>();
ArrayList<Task> allTasks = new ArrayList<>();
// Disable notifications if there are no callbacks
if (mCb == null) {
@@ -570,10 +552,13 @@ public class TaskStack {
// Remove any tasks that no longer exist
int taskCount = mRawTaskList.size();
for (int i = 0; i < taskCount; i++) {
for (int i = taskCount - 1; i >= 0; i--) {
Task task = mRawTaskList.get(i);
if (!newTasksMap.containsKey(task.key)) {
if (notifyStackChanges) {
// If we are notifying, then remove the task now, otherwise the raw task list
// will be reset at the end of this method
removeTask(task, AnimationProps.IMMEDIATE);
mCb.onStackTaskRemoved(this, task, i == (taskCount - 1), null,
AnimationProps.IMMEDIATE);
}
@@ -584,26 +569,28 @@ public class TaskStack {
// Add any new tasks
taskCount = tasks.size();
for (int i = 0; i < taskCount; i++) {
Task task = tasks.get(i);
if (!currentTasksMap.containsKey(task.key)) {
if (notifyStackChanges) {
mCb.onStackTaskAdded(this, task);
}
newTasks.add(task);
} else {
newTasks.add(currentTasksMap.get(task.key));
Task newTask = tasks.get(i);
Task currentTask = currentTasksMap.get(newTask.key);
if (currentTask == null && notifyStackChanges) {
addedTasks.add(newTask);
} else if (currentTask != null) {
// The current task has bound callbacks, so just copy the data from the new task
// state and add it back into the list
currentTask.copyFrom(newTask);
newTask = currentTask;
}
allTasks.add(newTask);
}
// Sort all the tasks to ensure they are ordered correctly
Collections.sort(newTasks, FREEFORM_LAST_ACTIVE_TIME_COMPARATOR);
Collections.sort(allTasks, FREEFORM_LAST_ACTIVE_TIME_COMPARATOR);
// Filter out the historical tasks from this new list
ArrayList<Task> stackTasks = new ArrayList<>();
ArrayList<Task> historyTasks = new ArrayList<>();
int newTaskCount = newTasks.size();
int newTaskCount = allTasks.size();
for (int i = 0; i < newTaskCount; i++) {
Task task = newTasks.get(i);
Task task = allTasks.get(i);
if (task.isHistorical) {
historyTasks.add(task);
} else {
@@ -613,10 +600,16 @@ public class TaskStack {
mStackTaskList.set(stackTasks);
mHistoryTaskList.set(historyTasks);
mRawTaskList.clear();
mRawTaskList.addAll(newTasks);
mGroups.clear();
mAffinitiesGroups.clear();
mRawTaskList = allTasks;
// Only callback for the newly added tasks after this stack has been updated
int addedTaskCount = addedTasks.size();
for (int i = 0; i < addedTaskCount; i++) {
mCb.onStackTaskAdded(this, addedTasks.get(i));
}
// Update the affiliated groupings
createAffiliatedGroupings(context);
}
/**
@@ -779,9 +772,12 @@ public class TaskStack {
}
/**
* Temporary: This method will simulate affiliation groups by
* Temporary: This method will simulate affiliation groups
*/
public void createAffiliatedGroupings(Context context) {
void createAffiliatedGroupings(Context context) {
mGroups.clear();
mAffinitiesGroups.clear();
if (RecentsDebugFlags.Static.EnableMockTaskGroups) {
ArrayMap<Task.TaskKey, Task> taskMap = new ArrayMap<>();
// Sort all tasks by increasing firstActiveTime of the task
@@ -926,13 +922,13 @@ public class TaskStack {
@Override
public String toString() {
String str = "Stack Tasks:\n";
String str = "Stack Tasks (" + mStackTaskList.size() + "):\n";
for (Task t : mStackTaskList.getTasks()) {
str += " " + t.toString() + "\n";
str += " " + t.toString() + "\n";
}
str += "Historical Tasks:\n";
str += "Historical Tasks(" + mHistoryTaskList.size() + "):\n";
for (Task t : mHistoryTaskList.getTasks()) {
str += " " + t.toString() + "\n";
str += " " + t.toString() + "\n";
}
return str;
}

View File

@@ -294,7 +294,7 @@ public class RecentsTvActivity extends Activity implements OnPreDrawListener {
// Notify that recents is now visible
SystemServicesProxy ssp = Recents.getSystemServices();
EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, ssp, true));
EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, true));
if (mPipManager.isPipShown()) {
// Place mPipView at the PIP bounds for fine tuned focus handling.
@@ -343,8 +343,7 @@ public class RecentsTvActivity extends Activity implements OnPreDrawListener {
mPipManager.removeListener(mPipListener);
mIgnoreAltTabRelease = false;
// Notify that recents is now hidden
SystemServicesProxy ssp = Recents.getSystemServices();
EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, ssp, false));
EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, false));
// Workaround for b/22542869, if the RecentsActivity is started again, but without going
// through SystemUI, we need to reset the config launch flags to ensure that we do not

View File

@@ -177,6 +177,7 @@ public class RecentsTransitionHelper {
// Keep track of failed launches
EventBus.getDefault().send(new LaunchTaskFailedEvent());
}
if (transitionFuture != null) {
IRemoteCallback.Stub callback = null;
if (animStartedListener != null) {

View File

@@ -60,9 +60,7 @@ import com.android.systemui.recents.events.activity.HideHistoryEvent;
import com.android.systemui.recents.events.activity.LaunchTaskEvent;
import com.android.systemui.recents.events.activity.ShowHistoryButtonEvent;
import com.android.systemui.recents.events.activity.ShowHistoryEvent;
import com.android.systemui.recents.events.activity.TaskStackUpdatedEvent;
import com.android.systemui.recents.events.activity.ToggleHistoryEvent;
import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
import com.android.systemui.recents.events.ui.DraggingInRecentsEndedEvent;
import com.android.systemui.recents.events.ui.DraggingInRecentsEvent;
import com.android.systemui.recents.events.ui.ResetBackgroundScrimEvent;
@@ -168,35 +166,39 @@ public class RecentsView extends FrameLayout {
}
/** Set/get the bsp root node */
public void setTaskStack(TaskStack stack) {
public void onResume(boolean isResumingFromVisible, TaskStack stack) {
RecentsConfiguration config = Recents.getConfiguration();
RecentsActivityLaunchState launchState = config.getLaunchState();
mStack = stack;
if (launchState.launchedReuseTaskStackViews) {
if (mTaskStackView != null) {
// If onRecentsHidden is not triggered, we need to the stack view again here
mTaskStackView.reset();
mTaskStackView.setStack(stack);
} else {
mTaskStackView = new TaskStackView(getContext(), stack);
addView(mTaskStackView);
}
} else {
if (mTaskStackView != null) {
removeView(mTaskStackView);
}
mTaskStackView = new TaskStackView(getContext(), stack);
if (mTaskStackView == null || !launchState.launchedReuseTaskStackViews) {
isResumingFromVisible = false;
removeView(mTaskStackView);
mTaskStackView = new TaskStackView(getContext());
mStack = mTaskStackView.getStack();
addView(mTaskStackView);
}
// If we are already occluded by the app, then just set the default background scrim now.
// Otherwise, defer until the enter animation completes to animate the scrim with the
// tasks for the home animation.
if (launchState.launchedWhileDocking || launchState.launchedFromApp
|| mStack.getTaskCount() == 0) {
mBackgroundScrim.setAlpha((int) (DEFAULT_SCRIM_ALPHA * 255));
// Reset the state
mAwaitingFirstLayout = !isResumingFromVisible;
mLastTaskLaunchedWasFreeform = false;
// Update the stack
mTaskStackView.onResume(isResumingFromVisible);
mTaskStackView.setTasks(stack, isResumingFromVisible /* notifyStackChanges */);
if (isResumingFromVisible) {
// If we are already visible, then restore the background scrim
animateBackgroundScrim(DEFAULT_SCRIM_ALPHA, DEFAULT_UPDATE_SCRIM_DURATION);
} else {
mBackgroundScrim.setAlpha(0);
// If we are already occluded by the app, then set the final background scrim alpha now.
// Otherwise, defer until the enter animation completes to animate the scrim alpha with
// the tasks for the home animation.
if (launchState.launchedWhileDocking || launchState.launchedFromApp
|| mStack.getTaskCount() == 0) {
mBackgroundScrim.setAlpha((int) (DEFAULT_SCRIM_ALPHA * 255));
} else {
mBackgroundScrim.setAlpha(0);
}
}
// Update the top level view's visibilities
@@ -205,9 +207,6 @@ public class RecentsView extends FrameLayout {
} else {
showEmptyView();
}
// Trigger a new layout
requestLayout();
}
/**
@@ -662,13 +661,6 @@ public class RecentsView extends FrameLayout {
animator.start();
}
public final void onBusEvent(TaskStackUpdatedEvent event) {
if (!event.inMultiWindow) {
mStack.setTasks(event.stack.computeAllTasksList(), true /* notifyStackChanges */);
mStack.createAffiliatedGroupings(getContext());
}
}
public final void onBusEvent(EnterRecentsWindowAnimationCompletedEvent event) {
RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
if (!launchState.launchedWhileDocking && !launchState.launchedFromApp
@@ -686,17 +678,6 @@ public class RecentsView extends FrameLayout {
animateBackgroundScrim(DEFAULT_SCRIM_ALPHA, DEFAULT_UPDATE_SCRIM_DURATION);
}
public final void onBusEvent(RecentsVisibilityChangedEvent event) {
if (!event.visible) {
// Reset the view state
mAwaitingFirstLayout = true;
mLastTaskLaunchedWasFreeform = false;
if (RecentsDebugFlags.Static.EnableHistory) {
hideHistoryButton(0, false /* translate */);
}
}
}
public final void onBusEvent(ToggleHistoryEvent event) {
if (!RecentsDebugFlags.Static.EnableHistory) {
return;

View File

@@ -16,9 +16,11 @@
package com.android.systemui.recents.views;
import android.animation.AnimatorListenerAdapter;
import android.app.Activity;
import android.content.Context;
import android.view.View;
import android.view.ViewPropertyAnimator;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
@@ -56,25 +58,41 @@ public class SystemBarScrimViews {
View.VISIBLE : View.INVISIBLE);
}
/**
* Animates the nav bar scrim visibility.
*/
public void animateNavBarScrimVisibility(boolean visible, AnimationProps animation) {
int toY = 0;
if (visible) {
mNavBarScrimView.setVisibility(View.VISIBLE);
mNavBarScrimView.setTranslationY(mNavBarScrimView.getMeasuredHeight());
} else {
toY = mNavBarScrimView.getMeasuredHeight();
}
if (animation != AnimationProps.IMMEDIATE) {
mNavBarScrimView.animate()
.translationY(toY)
.setDuration(animation.getDuration(AnimationProps.BOUNDS))
.setInterpolator(animation.getInterpolator(AnimationProps.BOUNDS))
.start();
} else {
mNavBarScrimView.setTranslationY(toY);
}
}
/**** EventBus events ****/
/**
* Starts animating the scrim views when entering Recents.
*/
public final void onBusEvent(EnterRecentsWindowAnimationCompletedEvent event) {
if (mHasNavBarScrim && mShouldAnimateNavBarScrim) {
mNavBarScrimView.setTranslationY(mNavBarScrimView.getMeasuredHeight());
mNavBarScrimView.animate()
.translationY(0)
.setDuration(mNavBarScrimEnterDuration)
.setInterpolator(Interpolators.DECELERATE_QUINT)
.withStartAction(new Runnable() {
@Override
public void run() {
mNavBarScrimView.setVisibility(View.VISIBLE);
}
})
.start();
if (mHasNavBarScrim) {
AnimationProps animation = mShouldAnimateNavBarScrim
? new AnimationProps()
.setDuration(AnimationProps.BOUNDS, mNavBarScrimEnterDuration)
.setInterpolator(AnimationProps.BOUNDS, Interpolators.DECELERATE_QUINT)
: AnimationProps.IMMEDIATE;
animateNavBarScrimVisibility(true, animation);
}
}
@@ -83,13 +101,12 @@ public class SystemBarScrimViews {
* going home).
*/
public final void onBusEvent(DismissRecentsToHomeAnimationStarted event) {
if (mHasNavBarScrim && mShouldAnimateNavBarScrim) {
mNavBarScrimView.animate()
.translationY(mNavBarScrimView.getMeasuredHeight())
.setStartDelay(0)
.setDuration(TaskStackAnimationHelper.EXIT_TO_HOME_TRANSLATION_DURATION)
.setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
.start();
if (mHasNavBarScrim) {
AnimationProps animation = new AnimationProps()
.setDuration(AnimationProps.BOUNDS,
TaskStackAnimationHelper.EXIT_TO_HOME_TRANSLATION_DURATION)
.setInterpolator(AnimationProps.BOUNDS, Interpolators.FAST_OUT_SLOW_IN);
animateNavBarScrimVisibility(false, animation);
}
}
}

View File

@@ -531,7 +531,6 @@ public class TaskStackLayoutAlgorithm {
return;
}
RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
mUnfocusedRange.offset(0f);
int taskCount = tasks.size();
for (int i = taskCount - 1; i >= 0; i--) {
@@ -571,6 +570,10 @@ public class TaskStackLayoutAlgorithm {
* Updates this stack when a scroll happens.
*/
public void updateFocusStateOnScroll(float stackScroll, float deltaScroll) {
if (deltaScroll == 0f) {
return;
}
for (int i = mTaskIndexOverrideMap.size() - 1; i >= 0; i--) {
int taskId = mTaskIndexOverrideMap.keyAt(i);
float x = mTaskIndexMap.get(taskId);
@@ -630,6 +633,13 @@ public class TaskStackLayoutAlgorithm {
return mState;
}
/**
* Returns whether this stack layout has been initialized.
*/
public boolean isInitialized() {
return !mStackRect.isEmpty();
}
/**
* Computes the maximum number of visible tasks and thumbnails when the scroll is at the initial
* stack scroll. Requires that update() is called first.

View File

@@ -65,11 +65,10 @@ import com.android.systemui.recents.events.activity.IterateRecentsEvent;
import com.android.systemui.recents.events.activity.LaunchNextTaskRequestEvent;
import com.android.systemui.recents.events.activity.LaunchTaskEvent;
import com.android.systemui.recents.events.activity.LaunchTaskStartedEvent;
import com.android.systemui.recents.events.activity.MultiWindowStateChangedEvent;
import com.android.systemui.recents.events.activity.PackagesChangedEvent;
import com.android.systemui.recents.events.activity.ShowHistoryButtonEvent;
import com.android.systemui.recents.events.activity.ShowHistoryEvent;
import com.android.systemui.recents.events.activity.TaskStackUpdatedEvent;
import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
import com.android.systemui.recents.events.ui.AllTaskViewsDismissedEvent;
import com.android.systemui.recents.events.ui.DeleteTaskDataEvent;
import com.android.systemui.recents.events.ui.DismissTaskViewEvent;
@@ -117,7 +116,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
private static final ArraySet<Task.TaskKey> EMPTY_TASK_SET = new ArraySet<>();
LayoutInflater mInflater;
TaskStack mStack;
TaskStack mStack = new TaskStack();
@ViewDebug.ExportedProperty(deepExport=true, prefix="layout_")
TaskStackLayoutAlgorithm mLayoutAlgorithm;
@ViewDebug.ExportedProperty(deepExport=true, prefix="scroller_")
@@ -206,13 +205,13 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
}
};
public TaskStackView(Context context, TaskStack stack) {
public TaskStackView(Context context) {
super(context);
SystemServicesProxy ssp = Recents.getSystemServices();
Resources res = context.getResources();
// Set the stack first
setStack(stack);
mStack.setCallbacks(this);
mViewPool = new ViewPool<>(context, this);
mInflater = LayoutInflater.from(context);
mLayoutAlgorithm = new TaskStackLayoutAlgorithm(context, this);
@@ -248,6 +247,41 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
}
}
/**
* Called only if we are resuming Recents.
*/
void onResume(boolean isResumingFromVisible) {
if (!isResumingFromVisible) {
// Reset the focused task
resetFocusedTask(getFocusedTask());
}
// Reset the state of each of the task views
List<TaskView> taskViews = new ArrayList<>();
taskViews.addAll(getTaskViews());
taskViews.addAll(mViewPool.getViews());
for (int i = taskViews.size() - 1; i >= 0; i--) {
taskViews.get(i).onResume(isResumingFromVisible);
}
// Reset the stack state
readSystemFlags();
mTaskViewsClipDirty = true;
mEnterAnimationComplete = false;
mUIDozeTrigger.stopDozing();
if (isResumingFromVisible) {
// Animate in the freeform workspace
int ffBgAlpha = mLayoutAlgorithm.getStackState().freeformBackgroundAlpha;
animateFreeformWorkspaceBackgroundAlpha(ffBgAlpha, new AnimationProps(150,
Interpolators.FAST_OUT_SLOW_IN));
} else {
mStackScroller.reset();
mLayoutAlgorithm.reset();
mAwaitingFirstLayout = true;
requestLayout();
}
}
@Override
protected void onAttachedToWindow() {
EventBus.getDefault().register(this, RecentsActivity.EVENT_BUS_PRIORITY + 1);
@@ -261,15 +295,20 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
EventBus.getDefault().unregister(this);
}
/** Sets the task stack */
void setStack(TaskStack stack) {
// Set the new stack
mStack = stack;
if (mStack != null) {
mStack.setCallbacks(this);
/**
* Sets the stack tasks of this TaskStackView from the given TaskStack.
*/
public void setTasks(TaskStack stack, boolean notifyStackChanges) {
boolean isInitialized = mLayoutAlgorithm.isInitialized();
mStack.setTasks(getContext(), stack.computeAllTasksList(),
notifyStackChanges && isInitialized);
if (isInitialized) {
// Only update the layout if we are notifying, otherwise, we will update it in the next
// measure/layout pass
updateLayoutAlgorithm(false /* boundScroll */, EMPTY_TASK_SET);
updateToInitialState();
relayoutTaskViewsOnNextFrame(AnimationProps.IMMEDIATE);
}
// Layout again with the new stack
requestLayout();
}
/** Returns the task stack. */
@@ -338,36 +377,6 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
return null;
}
/** Resets this TaskStackView for reuse. */
void reset() {
// Reset the focused task
resetFocusedTask(getFocusedTask());
// Return all the views to the pool
List<TaskView> taskViews = getTaskViews();
int taskViewCount = taskViews.size();
for (int i = taskViewCount - 1; i >= 0; i--) {
mViewPool.returnViewToPool(taskViews.get(i));
}
// Mark each task view for relayout
List<TaskView> poolViews = mViewPool.getViews();
for (TaskView tv : poolViews) {
tv.reset();
}
// Reset the stack state
mStack.reset();
mTaskViewsClipDirty = true;
mAwaitingFirstLayout = true;
mEnterAnimationComplete = false;
mUIDozeTrigger.stopDozing();
mStackScroller.reset();
mLayoutAlgorithm.reset();
readSystemFlags();
requestLayout();
}
/** Returns the stack algorithm for this task stack. */
public TaskStackLayoutAlgorithm getStackAlgorithm() {
return mLayoutAlgorithm;
@@ -1119,15 +1128,6 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
}
}
/**
* This is ONLY used from the Recents component to update the dummy stack view for purposes
* of getting the task rect to animate to.
*/
public void updateLayoutForStack(TaskStack stack) {
mStack = stack;
updateLayoutAlgorithm(false /* boundScroll */, EMPTY_TASK_SET);
}
/**
* Computes the maximum number of visible tasks and thumbnails. Requires that
* updateLayoutForStack() is called first.
@@ -1423,7 +1423,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
public void onReturnViewToPool(TaskView tv) {
final Task task = tv.getTask();
// Report that this tasks's data is no longer being used
// Report that this task's data is no longer being used
Recents.getTaskLoader().unloadTaskData(task);
// Reset the view properties and view state
@@ -1670,12 +1670,6 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
}
}
public final void onBusEvent(RecentsVisibilityChangedEvent event) {
if (!event.visible) {
reset();
}
}
public final void onBusEvent(DragStartEvent event) {
// Ensure that the drag task is not animated
addIgnoreTask(event.task);
@@ -1772,12 +1766,14 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
event.taskView.setLeftTopRightBottom(taskViewRect.left, taskViewRect.top,
taskViewRect.right, taskViewRect.bottom);
// Animate all the TaskViews back into position
// Animate the non-drag TaskViews back into position
mLayoutAlgorithm.getStackTransform(event.task, getScroller().getStackScroll(),
mTmpTransform, null);
event.getAnimationTrigger().increment();
relayoutTaskViews(new AnimationProps(DEFAULT_SYNC_STACK_DURATION,
Interpolators.FAST_OUT_SLOW_IN));
// Animate the drag TaskView back into position
updateTaskViewToTransform(event.taskView, mTmpTransform,
new AnimationProps(DEFAULT_SYNC_STACK_DURATION, Interpolators.FAST_OUT_SLOW_IN,
event.getAnimationTrigger().decrementOnAnimationEnd()));
@@ -1850,31 +1846,22 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
mAnimationHelper.startHideHistoryAnimation();
}
public final void onBusEvent(TaskStackUpdatedEvent event) {
public final void onBusEvent(MultiWindowStateChangedEvent event) {
if (!event.inMultiWindow) {
// Scroll the stack to the front after it has been updated
event.addPostAnimationCallback(new Runnable() {
// Scroll the stack to the front to see the undocked task
mStackScroller.animateScroll(mLayoutAlgorithm.mMaxScrollP, new Runnable() {
@Override
public void run() {
mStackScroller.animateScroll(mLayoutAlgorithm.mMaxScrollP,
null /* postScrollRunnable */);
List<TaskView> taskViews = getTaskViews();
int taskViewCount = taskViews.size();
for (int i = 0; i < taskViewCount; i++) {
TaskView tv = taskViews.get(i);
tv.getHeaderView().rebindToTask(tv.getTask(), tv.mTouchExplorationEnabled,
tv.mIsDisabledInSafeMode);
}
}
});
}
// When the multi-window state changes, rebind all task view headers again to update their
// dockable state
event.addPostAnimationCallback(new Runnable() {
@Override
public void run() {
List<TaskView> taskViews = getTaskViews();
int taskViewCount = taskViews.size();
for (int i = 0; i < taskViewCount; i++) {
TaskView tv = taskViews.get(i);
tv.getHeaderView().rebindToTask(tv.getTask(), tv.mTouchExplorationEnabled,
tv.mIsDisabledInSafeMode);
}
}
});
}
public final void onBusEvent(ConfigurationChangedEvent event) {

View File

@@ -193,11 +193,13 @@ public class TaskView extends FixedSizeFrameLayout implements Task.TaskCallbacks
}
/** Resets this TaskView for reuse. */
void reset() {
resetViewProperties();
void onResume(boolean isResumingFromVisible) {
resetNoUserInteractionState();
readSystemFlags();
setClipViewInStack(false);
if (!isResumingFromVisible) {
resetViewProperties();
setClipViewInStack(false);
}
setCallbacks(null);
}