Update visibility when finishing activity
Consider the situation where there is a translucent or non-fullscreen activity in foreground and a middle activity that is going to be finished. The black background will show below the foreground activity if the middle activity is in STARTED state because the activity in STARTED state was not treated as visible. Another case is that the task of non-fullscreen activity is different to the middle activity. We can't find any resumed activity from the task of the middle activity. This CL treat both PAUSED and STARTED activities as visible and update the visibility if current activity is opaque. Also clean up some dead code in Task and ActivityRecord. Bug: 172837736 Test: atest WmTests:ActivityRecordTests RootTaskTests Test: atest ActivityVisibilityTests Test: atest ActivityLifecycleTopResumedStateTests Test: atest CtsWindowManagerDeviceTestCases:ActivityLifecycleTests Test: atest MultiDisplayPolicyTests Change-Id: I43618e34383409b4805942b1923f1281f4226b1c
This commit is contained in:
@@ -114,8 +114,6 @@ import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
|
||||
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
|
||||
import static android.view.WindowManager.TRANSIT_CLOSE;
|
||||
import static android.view.WindowManager.TRANSIT_FLAG_OPEN_BEHIND;
|
||||
import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_CLOSE;
|
||||
import static android.view.WindowManager.TRANSIT_OLD_TASK_CLOSE;
|
||||
import static android.view.WindowManager.TRANSIT_OLD_UNSET;
|
||||
|
||||
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
|
||||
@@ -324,7 +322,6 @@ import com.android.internal.policy.AttributeCache;
|
||||
import com.android.internal.protolog.common.ProtoLog;
|
||||
import com.android.internal.util.ToBooleanFunction;
|
||||
import com.android.internal.util.XmlUtils;
|
||||
import com.android.internal.util.function.pooled.PooledConsumer;
|
||||
import com.android.internal.util.function.pooled.PooledFunction;
|
||||
import com.android.internal.util.function.pooled.PooledLambda;
|
||||
import com.android.server.LocalServices;
|
||||
@@ -2384,7 +2381,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
return occludesParent(false /* includingFinishing */);
|
||||
}
|
||||
|
||||
private boolean occludesParent(boolean includingFinishing) {
|
||||
@VisibleForTesting
|
||||
boolean occludesParent(boolean includingFinishing) {
|
||||
if (!includingFinishing && finishing) {
|
||||
return false;
|
||||
}
|
||||
@@ -2822,7 +2820,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
|
||||
final boolean endTask = task.getTopNonFinishingActivity() == null
|
||||
&& !task.isClearingToReuseTask();
|
||||
final int transit = endTask ? TRANSIT_OLD_TASK_CLOSE : TRANSIT_OLD_ACTIVITY_CLOSE;
|
||||
if (newTransition != null) {
|
||||
mAtmService.getTransitionController().requestStartTransition(newTransition,
|
||||
endTask ? task : null, null /* remote */);
|
||||
@@ -2879,7 +2876,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
} else if (!isState(PAUSING)) {
|
||||
if (mVisibleRequested) {
|
||||
// Prepare and execute close transition.
|
||||
prepareActivityHideTransitionAnimation(transit);
|
||||
prepareActivityHideTransitionAnimation();
|
||||
}
|
||||
|
||||
final boolean removedActivity = completeFinishing("finishIfPossible") == null;
|
||||
@@ -2897,11 +2894,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
// In this case, we can set the visibility of all the task overlay activities when
|
||||
// we detect the last one is finishing to keep them in sync.
|
||||
if (task.onlyHasTaskOverlayActivities(false /* includeFinishing */)) {
|
||||
final PooledConsumer c = PooledLambda.obtainConsumer(
|
||||
ActivityRecord::prepareActivityHideTransitionAnimationIfOvarlay,
|
||||
PooledLambda.__(ActivityRecord.class), transit);
|
||||
task.forAllActivities(c);
|
||||
c.recycle();
|
||||
task.forAllActivities((r) -> {
|
||||
r.prepareActivityHideTransitionAnimationIfOvarlay();
|
||||
});
|
||||
}
|
||||
return removedActivity ? FINISH_RESULT_REMOVED : FINISH_RESULT_REQUESTED;
|
||||
} else {
|
||||
@@ -2914,28 +2909,33 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
}
|
||||
}
|
||||
|
||||
private void prepareActivityHideTransitionAnimationIfOvarlay(@TransitionOldType int transit) {
|
||||
private void prepareActivityHideTransitionAnimationIfOvarlay() {
|
||||
if (mTaskOverlay) {
|
||||
prepareActivityHideTransitionAnimation(transit);
|
||||
prepareActivityHideTransitionAnimation();
|
||||
}
|
||||
}
|
||||
|
||||
private void prepareActivityHideTransitionAnimation(@TransitionOldType int transit) {
|
||||
private void prepareActivityHideTransitionAnimation() {
|
||||
final DisplayContent dc = mDisplayContent;
|
||||
dc.prepareAppTransition(TRANSIT_CLOSE);
|
||||
setVisibility(false);
|
||||
dc.executeAppTransition();
|
||||
}
|
||||
|
||||
ActivityRecord completeFinishing(String reason) {
|
||||
return completeFinishing(true /* updateVisibility */, reason);
|
||||
}
|
||||
|
||||
/**
|
||||
* Complete activity finish request that was initiated earlier. If the activity is still
|
||||
* pausing we will wait for it to complete its transition. If the activity that should appear in
|
||||
* place of this one is not visible yet - we'll wait for it first. Otherwise - activity can be
|
||||
* destroyed right away.
|
||||
* @param updateVisibility Indicate if need to update activity visibility.
|
||||
* @param reason Reason for finishing the activity.
|
||||
* @return Flag indicating whether the activity was removed from history.
|
||||
*/
|
||||
ActivityRecord completeFinishing(String reason) {
|
||||
ActivityRecord completeFinishing(boolean updateVisibility, String reason) {
|
||||
if (!finishing || isState(RESUMED)) {
|
||||
throw new IllegalArgumentException(
|
||||
"Activity must be finishing and not resumed to complete, r=" + this
|
||||
@@ -2947,13 +2947,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
return this;
|
||||
}
|
||||
|
||||
final boolean isCurrentVisible = mVisibleRequested || isState(PAUSED);
|
||||
if (isCurrentVisible) {
|
||||
final Task rootTask = getRootTask();
|
||||
final ActivityRecord activity = rootTask.getResumedActivity();
|
||||
final boolean isCurrentVisible = mVisibleRequested || isState(PAUSED, STARTED);
|
||||
if (updateVisibility && isCurrentVisible) {
|
||||
boolean ensureVisibility = false;
|
||||
if (activity != null && !activity.occludesParent()) {
|
||||
// If the resume activity is not opaque, we need to make sure the visibilities of
|
||||
if (occludesParent(true /* includingFinishing */)) {
|
||||
// If the current activity is not opaque, we need to make sure the visibilities of
|
||||
// activities be updated, they may be seen by users.
|
||||
ensureVisibility = true;
|
||||
} else if (mTaskSupervisor.getKeyguardController().isKeyguardLocked()
|
||||
|
||||
@@ -5828,8 +5828,13 @@ class Task extends WindowContainer<WindowContainer> {
|
||||
final boolean wasStopping = prev.isState(STOPPING);
|
||||
prev.setState(PAUSED, "completePausedLocked");
|
||||
if (prev.finishing) {
|
||||
// We will update the activity visibility later, no need to do in
|
||||
// completeFinishing(). Updating visibility here might also making the next
|
||||
// activities to be resumed, and could result in wrong app transition due to
|
||||
// lack of previous activity information.
|
||||
ProtoLog.v(WM_DEBUG_STATES, "Executing finish of activity: %s", prev);
|
||||
prev = prev.completeFinishing("completePausedLocked");
|
||||
prev = prev.completeFinishing(false /* updateVisibility */,
|
||||
"completePausedLocked");
|
||||
} else if (prev.hasProcess()) {
|
||||
ProtoLog.v(WM_DEBUG_STATES, "Enqueue pending stop if needed: %s "
|
||||
+ "wasStopping=%b visibleRequested=%b", prev, wasStopping,
|
||||
|
||||
@@ -1351,25 +1351,39 @@ public class ActivityRecordTests extends WindowTestsBase {
|
||||
* must ensure the visibilities of activities being updated.
|
||||
*/
|
||||
@Test
|
||||
public void testCompleteFinishing_ensureActivitiesVisible() {
|
||||
public void testCompleteFinishing_ensureActivitiesVisible_withConditions() {
|
||||
testCompleteFinishing_ensureActivitiesVisible(false, PAUSED);
|
||||
testCompleteFinishing_ensureActivitiesVisible(false, STARTED);
|
||||
testCompleteFinishing_ensureActivitiesVisible(true, PAUSED);
|
||||
testCompleteFinishing_ensureActivitiesVisible(true, STARTED);
|
||||
}
|
||||
|
||||
private void testCompleteFinishing_ensureActivitiesVisible(boolean diffTask,
|
||||
ActivityState secondActivityState) {
|
||||
final ActivityRecord activity = createActivityWithTask();
|
||||
final Task task = activity.getTask();
|
||||
final ActivityRecord firstActivity = new ActivityBuilder(mAtm).setTask(task).build();
|
||||
firstActivity.mVisibleRequested = false;
|
||||
firstActivity.nowVisible = false;
|
||||
firstActivity.setState(STOPPED, "true");
|
||||
firstActivity.setState(STOPPED, "test");
|
||||
|
||||
final ActivityRecord secondActivity = new ActivityBuilder(mAtm).setTask(task).build();
|
||||
secondActivity.mVisibleRequested = true;
|
||||
secondActivity.nowVisible = true;
|
||||
secondActivity.setState(PAUSED, "true");
|
||||
secondActivity.setState(secondActivityState, "test");
|
||||
|
||||
final ActivityRecord translucentActivity = new ActivityBuilder(mAtm).setTask(task).build();
|
||||
ActivityRecord translucentActivity;
|
||||
if (diffTask) {
|
||||
translucentActivity = new ActivityBuilder(mAtm).setCreateTask(true).build();
|
||||
} else {
|
||||
translucentActivity = new ActivityBuilder(mAtm).setTask(task).build();
|
||||
}
|
||||
translucentActivity.mVisibleRequested = true;
|
||||
translucentActivity.nowVisible = true;
|
||||
translucentActivity.setState(RESUMED, "true");
|
||||
translucentActivity.setState(RESUMED, "test");
|
||||
|
||||
doReturn(false).when(translucentActivity).occludesParent();
|
||||
doReturn(true).when(firstActivity).occludesParent(true);
|
||||
doReturn(true).when(secondActivity).occludesParent(true);
|
||||
|
||||
// Finish the second activity
|
||||
secondActivity.finishing = true;
|
||||
@@ -1385,6 +1399,10 @@ public class ActivityRecordTests extends WindowTestsBase {
|
||||
verify(firstActivity.mDisplayContent, times(2)).ensureActivitiesVisible(null /* starting */,
|
||||
0 /* configChanges */ , false /* preserveWindows */,
|
||||
true /* notifyClients */);
|
||||
|
||||
// Remove the translucent activity and clear invocations for next test
|
||||
translucentActivity.getTask().removeImmediately("test");
|
||||
clearInvocations(mDefaultDisplay);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -38,6 +38,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
|
||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
|
||||
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE;
|
||||
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
|
||||
import static com.android.server.wm.Task.ActivityState.DESTROYED;
|
||||
import static com.android.server.wm.Task.ActivityState.DESTROYING;
|
||||
import static com.android.server.wm.Task.ActivityState.FINISHING;
|
||||
import static com.android.server.wm.Task.ActivityState.PAUSING;
|
||||
@@ -1204,6 +1205,9 @@ public class RootTaskTests extends WindowTestsBase {
|
||||
// See {@link ActivityStack#destroyActivityLocked}.
|
||||
activity.app = null;
|
||||
overlayActivity.app = null;
|
||||
// Simulate the process is dead
|
||||
activity.mVisibleRequested = false;
|
||||
activity.setState(DESTROYED, "Test");
|
||||
|
||||
assertEquals(2, task.getChildCount());
|
||||
|
||||
|
||||
Reference in New Issue
Block a user