blob: 79e56a05b8699e923822c1d59b3149837d8172ac [file] [log] [blame]
/*
* Copyright (C) 2018 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 android.server.wm.lifecycle;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.server.wm.lifecycle.LifecycleLog.ActivityCallback.ON_ACTIVITY_RESULT;
import static android.server.wm.lifecycle.LifecycleLog.ActivityCallback.ON_CREATE;
import static android.server.wm.lifecycle.LifecycleLog.ActivityCallback.ON_DESTROY;
import static android.server.wm.lifecycle.LifecycleLog.ActivityCallback.ON_MULTI_WINDOW_MODE_CHANGED;
import static android.server.wm.lifecycle.LifecycleLog.ActivityCallback.ON_PAUSE;
import static android.server.wm.lifecycle.LifecycleLog.ActivityCallback.ON_POST_CREATE;
import static android.server.wm.lifecycle.LifecycleLog.ActivityCallback.ON_RESUME;
import static android.server.wm.lifecycle.LifecycleLog.ActivityCallback.ON_START;
import static android.server.wm.lifecycle.LifecycleLog.ActivityCallback.ON_STOP;
import static android.server.wm.lifecycle.LifecycleLog.ActivityCallback.ON_TOP_POSITION_GAINED;
import static android.server.wm.lifecycle.LifecycleLog.ActivityCallback.ON_TOP_POSITION_LOST;
import static android.server.wm.lifecycle.LifecycleLog.ActivityCallback.ON_USER_LEAVE_HINT;
import static android.server.wm.lifecycle.LifecycleVerifier.transition;
import static androidx.test.InstrumentationRegistry.getInstrumentation;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assume.assumeTrue;
import android.app.Activity;
import android.app.Instrumentation;
import android.content.Intent;
import android.platform.test.annotations.Presubmit;
import androidx.test.filters.MediumTest;
import org.junit.Before;
import org.junit.Test;
import java.util.Arrays;
import java.util.List;
/**
* Build/Install/Run:
* atest CtsWindowManagerDeviceTestCases:ActivityLifecycleLegacySplitScreenTests
*/
@MediumTest
@Presubmit
@android.server.wm.annotation.Group3
public class ActivityLifecycleLegacySplitScreenTests extends ActivityLifecycleClientTestBase {
@Before
public void setUp() throws Exception {
super.setUp();
assumeTrue(supportsSplitScreenMultiWindow());
}
@Test
public void testResumedWhenRecreatedFromInNonFocusedTask() throws Exception {
// Launch an activity that will be moved to split-screen secondary
final Activity sideActivity = launchActivityAndWait(SideActivity.class);
// Launch first activity
final Activity firstActivity = launchActivityAndWait(FirstActivity.class);
// Launch second activity to stop first
final Activity secondActivity = launchActivityAndWait(SecondActivity.class);
// Wait for the first activity to stop, so that this event is not included in the logs.
waitAndAssertActivityStates(state(firstActivity, ON_STOP));
// Enter split screen
moveTaskToPrimarySplitScreenAndVerify(secondActivity, sideActivity);
// CLear logs so we can capture just the destroy sequence
getLifecycleLog().clear();
// Start an activity in separate task (will be placed in secondary stack)
mTaskOrganizer.setLaunchRoot(mTaskOrganizer.getSecondarySplitTaskId());
new Launcher(ThirdActivity.class)
.setFlags(FLAG_ACTIVITY_MULTIPLE_TASK | FLAG_ACTIVITY_NEW_TASK)
.launch();
// Finish top activity
secondActivity.finish();
waitAndAssertActivityStates(state(secondActivity, ON_DESTROY),
state(firstActivity, ON_RESUME));
// Verify that the first activity was recreated to resume as it was created before
// windowing mode was switched
LifecycleVerifier.assertRecreateAndResumeSequence(FirstActivity.class, getLifecycleLog());
// Verify that the lifecycle state did not change for activity in non-focused stack
LifecycleVerifier.assertLaunchSequence(ThirdActivity.class, getLifecycleLog());
}
@Test
public void testOccludingOnSplitSecondaryTask() throws Exception {
// Launch an activity that will be moved to split-screen secondary
final Activity sideActivity = launchActivityAndWait(SideActivity.class);
// Launch first activity
final Activity firstActivity = launchActivityAndWait(FirstActivity.class);
// Enter split screen
moveTaskToPrimarySplitScreenAndVerify(firstActivity, sideActivity);
// Launch third activity on top of second
getLifecycleLog().clear();
mTaskOrganizer.setLaunchRoot(mTaskOrganizer.getSecondarySplitTaskId());
new Launcher(ThirdActivity.class)
.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK)
.launch();
waitAndAssertActivityStates(state(sideActivity, ON_STOP));
}
@Test
public void testTranslucentOnSplitSecondaryTask() throws Exception {
// Launch an activity that will be moved to split-screen secondary
final Activity sideActivity = launchActivityAndWait(ThirdActivity.class);
// Launch an activity in a new task
final Activity firstActivity = new Launcher(FirstActivity.class)
.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK)
.launch();
// Enter split screen
moveTaskToPrimarySplitScreenAndVerify(firstActivity, sideActivity);
// Launch translucent activity on top of second
getLifecycleLog().clear();
mTaskOrganizer.setLaunchRoot(mTaskOrganizer.getSecondarySplitTaskId());
new Launcher(TranslucentActivity.class)
.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK)
.launch();
waitAndAssertActivityStates(state(sideActivity, ON_PAUSE));
}
@Test
public void testResultInNonFocusedTask() throws Exception {
// Launch an activity that will be moved to split-screen secondary
final Activity sideActivity = launchActivityAndWait(SideActivity.class);
// Launch first activity in another task
final Activity callbackTrackingActivity = launchActivityAndWait(
CallbackTrackingActivity.class);
// Launch second activity
// Create an ActivityMonitor that catch ChildActivity and return mock ActivityResult:
Instrumentation.ActivityMonitor activityMonitor = getInstrumentation()
.addMonitor(SecondActivity.class.getName(), null /* activityResult */,
false /* block */);
callbackTrackingActivity.startActivityForResult(
new Intent(callbackTrackingActivity, SecondActivity.class), 1 /* requestCode */);
// Wait for the ActivityMonitor to be hit
final Activity secondActivity = getInstrumentation()
.waitForMonitorWithTimeout(activityMonitor, 5 * 1000);
// Wait for second activity to resume
assertNotNull("Second activity should be started", secondActivity);
waitAndAssertActivityStates(state(secondActivity, ON_RESUME));
// Verify if the first activity stopped (since it is not currently visible)
waitAndAssertActivityStates(state(callbackTrackingActivity, ON_STOP));
// Enter split screen
moveTaskToPrimarySplitScreenAndVerify(secondActivity, sideActivity);
// Finish top activity and verify that activity below became focused.
getLifecycleLog().clear();
secondActivity.setResult(Activity.RESULT_OK);
secondActivity.finish();
// Check that activity was restarted and result was delivered
waitAndAssertActivityStates(state(callbackTrackingActivity, ON_RESUME));
LifecycleVerifier.assertSequence(CallbackTrackingActivity.class, getLifecycleLog(),
Arrays.asList(ON_DESTROY, ON_CREATE, ON_START, ON_POST_CREATE,
ON_ACTIVITY_RESULT, ON_RESUME), "restart");
}
@Test
public void testResumedWhenRestartedFromInNonFocusedTask() throws Exception {
// Launch first activity
final Activity sideActivity = launchActivityAndWait(SideActivity.class);
// Start an activity in separate task
final Activity firstActivity = launchActivityAndWait(FirstActivity.class);
// Enter split screen
moveTaskToPrimarySplitScreenAndVerify(firstActivity, sideActivity);
// Start an activity in separate task (will be placed in secondary stack)
mTaskOrganizer.setLaunchRoot(mTaskOrganizer.getSecondarySplitTaskId());
final Activity newTaskActivity = new Launcher(ThirdActivity.class)
.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK)
.launch();
// Launch second activity, first become stopped
getLifecycleLog().clear();
final Activity secondActivity = launchActivityAndWait(SecondActivity.class);
// Wait for second activity to resume and first to stop
waitAndAssertActivityStates(state(newTaskActivity, ON_STOP));
// Finish top activity
getLifecycleLog().clear();
secondActivity.finish();
waitAndAssertActivityStates(state(newTaskActivity, ON_RESUME));
waitAndAssertActivityStates(state(secondActivity, ON_DESTROY));
// Verify that the first activity was restarted to resumed state as it was brought back
// after windowing mode was switched
LifecycleVerifier.assertRestartAndResumeSequence(ThirdActivity.class, getLifecycleLog());
LifecycleVerifier.assertResumeToDestroySequence(SecondActivity.class, getLifecycleLog());
}
@Test
public void testResumedTranslucentWhenRestartedFromInNonFocusedTask() throws Exception {
// Launch an activity that will be moved to split-screen secondary
final Activity sideActivity = launchActivityAndWait(SideActivity.class);
// Launch first activity
final Activity firstActivity = launchActivityAndWait(FirstActivity.class);
// Launch a translucent activity, first become paused
final Activity translucentActivity = launchActivityAndWait(TranslucentActivity.class);
// Wait for first activity to pause
waitAndAssertActivityStates(state(firstActivity, ON_PAUSE));
// Enter split screen
mWmState.computeState(firstActivity.getComponentName());
moveActivitiesToSplitScreen(firstActivity.getComponentName(),
sideActivity.getComponentName());
// Finish top activity
getLifecycleLog().clear();
translucentActivity.finish();
waitAndAssertActivityStates(state(firstActivity, ON_RESUME));
waitAndAssertActivityStates(state(translucentActivity, ON_DESTROY));
// Verify that the first activity was resumed
LifecycleVerifier.assertSequence(FirstActivity.class, getLifecycleLog(),
Arrays.asList(ON_RESUME), "resume");
LifecycleVerifier.assertResumeToDestroySequence(TranslucentActivity.class,
getLifecycleLog());
}
@Test
public void testLifecycleOnMoveToFromSplitScreenRelaunch() throws Exception {
// Launch an activity that will be moved to split-screen secondary
final Activity sideActivity = launchActivityAndWait(SecondActivity.class);
// Launch a singleTop activity
final Activity firstActivity = new Launcher(CallbackTrackingActivity.class)
.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK)
.launch();
// Wait for the activity to resume
LifecycleVerifier.assertLaunchSequence(CallbackTrackingActivity.class, getLifecycleLog());
// Enter split screen
getLifecycleLog().clear();
moveTaskToPrimarySplitScreenAndVerify(firstActivity, sideActivity);
// Wait for the activity to relaunch and receive multi-window mode change
final List<LifecycleLog.ActivityCallback> expectedEnterSequence =
Arrays.asList(ON_TOP_POSITION_LOST, ON_PAUSE, ON_STOP, ON_DESTROY,
ON_CREATE, ON_START, ON_POST_CREATE, ON_RESUME, ON_TOP_POSITION_GAINED,
ON_TOP_POSITION_LOST, ON_PAUSE);
waitForActivityTransitions(CallbackTrackingActivity.class, expectedEnterSequence);
LifecycleVerifier.assertOrder(getLifecycleLog(), CallbackTrackingActivity.class,
Arrays.asList(ON_TOP_POSITION_LOST, ON_PAUSE, ON_STOP, ON_DESTROY, ON_CREATE,
ON_RESUME), "moveToSplitScreen");
// Exit split-screen
getLifecycleLog().clear();
dismissSplitScreen(true /* primaryOnTop */);
// Wait for the activity to relaunch and receive multi-window mode change
final List<LifecycleLog.ActivityCallback> expectedExitSequence =
Arrays.asList(ON_STOP, ON_DESTROY, ON_CREATE, ON_START,
ON_POST_CREATE, ON_RESUME, ON_PAUSE, ON_RESUME, ON_TOP_POSITION_GAINED);
waitForActivityTransitions(CallbackTrackingActivity.class, expectedExitSequence);
LifecycleVerifier.assertOrder(getLifecycleLog(), CallbackTrackingActivity.class,
Arrays.asList(ON_DESTROY, ON_CREATE, ON_RESUME, ON_TOP_POSITION_GAINED),
"moveFromSplitScreen");
}
@Test
public void testLifecycleOnMoveToFromSplitScreenNoRelaunch() throws Exception {
// Launch activities and enter split screen. Launched an activity on
// split-screen secondary stack to ensure the TOP_POSITION_LOST is send
// prior to MULTI_WINDOW_MODE_CHANGED.
launchActivitiesInSplitScreen(
getLaunchActivityBuilder().
setTargetActivity(getComponentName(ConfigChangeHandlingActivity.class)),
getLaunchActivityBuilder().
setTargetActivity(getComponentName(SecondActivity.class)));
final int displayWindowingMode = getDisplayWindowingModeByActivity(
getComponentName(ConfigChangeHandlingActivity.class));
if (displayWindowingMode == WINDOWING_MODE_FULLSCREEN) {
// Wait for the activity to receive the change.
waitForActivityTransitions(ConfigChangeHandlingActivity.class,
Arrays.asList(ON_TOP_POSITION_LOST, ON_MULTI_WINDOW_MODE_CHANGED));
LifecycleVerifier.assertOrder(getLifecycleLog(), ConfigChangeHandlingActivity.class,
Arrays.asList(ON_MULTI_WINDOW_MODE_CHANGED, ON_TOP_POSITION_LOST),
"moveToSplitScreen");
} else {
// For non-fullscreen display mode, there won't be a multi-window callback.
waitForActivityTransitions(ConfigChangeHandlingActivity.class,
Arrays.asList(ON_TOP_POSITION_LOST));
LifecycleVerifier.assertTransitionObserved(getLifecycleLog(),
transition(ConfigChangeHandlingActivity.class, ON_TOP_POSITION_LOST),
"moveToSplitScreen");
}
// Exit split-screen
getLifecycleLog().clear();
dismissSplitScreen(true /* primaryOnTop */);
// Wait for the activity to receive the change
final List<LifecycleLog.ActivityCallback> expectedSequence =
Arrays.asList(ON_TOP_POSITION_GAINED, ON_MULTI_WINDOW_MODE_CHANGED);
waitForActivityTransitions(ConfigChangeHandlingActivity.class, expectedSequence);
LifecycleVerifier.assertTransitionObserved(getLifecycleLog(),
transition(ConfigChangeHandlingActivity.class, ON_MULTI_WINDOW_MODE_CHANGED),
"exitSplitScreen");
LifecycleVerifier.assertTransitionObserved(getLifecycleLog(),
transition(ConfigChangeHandlingActivity.class, ON_TOP_POSITION_GAINED),
"exitSplitScreen");
}
@Test
public void testOnUserLeaveHint() throws Exception {
launchActivitiesInSplitScreen(
getLaunchActivityBuilder()
.setTargetActivity(getComponentName(ConfigChangeHandlingActivity.class)),
getLaunchActivityBuilder()
.setIntentExtra(
extra -> extra.putBoolean(EXTRA_ACTIVITY_ON_USER_LEAVE_HINT, true))
.setTargetActivity(getComponentName(FirstActivity.class)));
getLifecycleLog().clear();
launchActivityAndWait(SecondActivity.class);
waitForIdle();
LifecycleVerifier.assertOrder(getLifecycleLog(), FirstActivity.class,
Arrays.asList(ON_USER_LEAVE_HINT, ON_PAUSE, ON_STOP),
"moveFromSplitScreen");
}
}