| /* |
| * 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.Instrumentation.ActivityMonitor; |
| import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; |
| import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP; |
| import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK; |
| import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; |
| import static android.content.Intent.FLAG_ACTIVITY_NO_USER_ACTION; |
| import static android.content.Intent.FLAG_ACTIVITY_SINGLE_TOP; |
| import static android.server.wm.WindowManagerState.STATE_PAUSED; |
| import static android.server.wm.WindowManagerState.STATE_STOPPED; |
| import static android.server.wm.UiDeviceUtils.pressBackButton; |
| import static android.server.wm.lifecycle.ActivityLifecycleClientTestBase.LaunchForResultActivity.EXTRA_LAUNCH_ON_RESULT; |
| import static android.server.wm.lifecycle.ActivityLifecycleClientTestBase.LaunchForResultActivity.EXTRA_LAUNCH_ON_RESUME_AFTER_RESULT; |
| import static android.server.wm.lifecycle.ActivityLifecycleClientTestBase.NoDisplayActivity.EXTRA_LAUNCH_ACTIVITY; |
| import static android.server.wm.lifecycle.ActivityLifecycleClientTestBase.NoDisplayActivity.EXTRA_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_NEW_INTENT; |
| 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_RESTART; |
| 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 android.view.Surface.ROTATION_0; |
| import static android.view.Surface.ROTATION_180; |
| import static android.view.Surface.ROTATION_270; |
| import static android.view.Surface.ROTATION_90; |
| |
| import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; |
| |
| import static org.junit.Assert.fail; |
| |
| import android.app.Activity; |
| import android.app.ActivityOptions; |
| import android.content.ComponentName; |
| import android.content.Intent; |
| import android.content.pm.ActivityInfo; |
| import android.platform.test.annotations.Presubmit; |
| |
| import androidx.test.filters.FlakyTest; |
| import androidx.test.filters.MediumTest; |
| |
| import com.android.compatibility.common.util.AmUtils; |
| |
| import org.junit.Test; |
| |
| import java.util.Arrays; |
| import java.util.List; |
| |
| /** |
| * Build/Install/Run: |
| * atest CtsWindowManagerDeviceTestCases:ActivityLifecycleTests |
| */ |
| @MediumTest |
| @Presubmit |
| @android.server.wm.annotation.Group3 |
| public class ActivityLifecycleTests extends ActivityLifecycleClientTestBase { |
| |
| @Test |
| public void testSingleLaunch() throws Exception { |
| launchActivityAndWait(FirstActivity.class); |
| |
| LifecycleVerifier.assertLaunchSequence(FirstActivity.class, getLifecycleLog()); |
| } |
| |
| @Test |
| public void testLaunchOnTop() throws Exception { |
| final Activity firstActivity = launchActivityAndWait(FirstActivity.class); |
| |
| getLifecycleLog().clear(); |
| launchActivityAndWait(SecondActivity.class); |
| waitAndAssertActivityStates(state(firstActivity, ON_STOP)); |
| |
| LifecycleVerifier.assertLaunchSequence(SecondActivity.class, FirstActivity.class, |
| getLifecycleLog(), false /* launchIsTranslucent */); |
| } |
| |
| @Test |
| public void testLaunchTranslucentOnTop() throws Exception { |
| // Launch fullscreen activity |
| final Activity firstActivity = launchActivityAndWait(FirstActivity.class); |
| |
| // Launch translucent activity on top |
| getLifecycleLog().clear(); |
| final Activity translucentActivity = launchActivityAndWait(TranslucentActivity.class); |
| waitAndAssertActivityStates(occludedActivityState(firstActivity, translucentActivity)); |
| |
| LifecycleVerifier.assertLaunchSequence(TranslucentActivity.class, FirstActivity.class, |
| getLifecycleLog(), true /* launchIsTranslucent */); |
| } |
| |
| @Test |
| public void testLaunchDoubleTranslucentOnTop() throws Exception { |
| final Activity firstActivity = launchActivityAndWait(FirstActivity.class); |
| |
| // Launch translucent activity on top |
| getLifecycleLog().clear(); |
| final Activity translucentActivity = launchActivityAndWait(TranslucentActivity.class); |
| waitAndAssertActivityStates(occludedActivityState(firstActivity, translucentActivity)); |
| |
| LifecycleVerifier.assertLaunchSequence(TranslucentActivity.class, FirstActivity.class, |
| getLifecycleLog(), true /* launchIsTranslucent */); |
| |
| // Launch another translucent activity on top |
| getLifecycleLog().clear(); |
| final Activity secondTranslucentActivity = |
| launchActivityAndWait(SecondTranslucentActivity.class); |
| waitAndAssertActivityStates( |
| occludedActivityState(translucentActivity, secondTranslucentActivity)); |
| LifecycleVerifier.assertSequence(TranslucentActivity.class, getLifecycleLog(), |
| Arrays.asList(ON_PAUSE), "launch"); |
| LifecycleVerifier.assertEmptySequence(FirstActivity.class, getLifecycleLog(), "launch"); |
| |
| // Finish top translucent activity |
| getLifecycleLog().clear(); |
| secondTranslucentActivity.finish(); |
| |
| waitAndAssertActivityStates(state(translucentActivity, ON_RESUME)); |
| waitAndAssertActivityStates(state(secondTranslucentActivity, ON_DESTROY)); |
| LifecycleVerifier.assertResumeToDestroySequence(SecondTranslucentActivity.class, |
| getLifecycleLog()); |
| LifecycleVerifier.assertSequence(TranslucentActivity.class, getLifecycleLog(), |
| Arrays.asList(ON_RESUME), "launch"); |
| LifecycleVerifier.assertEmptySequence(FirstActivity.class, getLifecycleLog(), "launch"); |
| } |
| |
| @Test |
| public void testTranslucentMovedIntoStack() throws Exception { |
| // Launch a translucent activity and a regular activity in separate stacks |
| final Activity translucentActivity = new Launcher(TranslucentActivity.class) |
| .setOptions(getLaunchOptionsForFullscreen()) |
| .launch(); |
| final Activity firstActivity = new Launcher(FirstActivity.class) |
| .setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK) |
| .setOptions(getLaunchOptionsForFullscreen()) |
| .launch(); |
| waitAndAssertActivityStates(state(translucentActivity, ON_STOP)); |
| |
| final ComponentName firstActivityName = getComponentName(FirstActivity.class); |
| mWmState.computeState(firstActivityName); |
| int firstActivityStack = mWmState.getRootTaskIdByActivity(firstActivityName); |
| |
| // Move translucent activity into the stack with the first activity |
| getLifecycleLog().clear(); |
| moveActivityToRootTaskOrOnTop(getComponentName(TranslucentActivity.class), firstActivityStack); |
| |
| // Wait for translucent activity to resume and first activity to pause |
| waitAndAssertActivityStates(state(translucentActivity, ON_RESUME), |
| state(firstActivity, ON_PAUSE)); |
| LifecycleVerifier.assertSequence(FirstActivity.class, getLifecycleLog(), |
| Arrays.asList(ON_PAUSE), "launchOnTop"); |
| LifecycleVerifier.assertRestartAndResumeSequence(TranslucentActivity.class, |
| getLifecycleLog()); |
| } |
| |
| @Test |
| public void testDestroyTopTranslucent() throws Exception { |
| // Launch a regular activity and a a translucent activity in the same stack |
| final Activity firstActivity = launchActivityAndWait(FirstActivity.class); |
| final Activity translucentActivity = launchActivityAndWait(TranslucentActivity.class); |
| waitAndAssertActivityStates(occludedActivityState(firstActivity, translucentActivity)); |
| |
| // Finish translucent activity |
| getLifecycleLog().clear(); |
| translucentActivity.finish(); |
| |
| waitAndAssertActivityStates(state(firstActivity, ON_RESUME), |
| state(translucentActivity, ON_DESTROY)); |
| |
| // Verify destruction lifecycle |
| LifecycleVerifier.assertResumeToDestroySequence(TranslucentActivity.class, |
| getLifecycleLog()); |
| LifecycleVerifier.assertSequence(FirstActivity.class, getLifecycleLog(), |
| Arrays.asList(ON_RESUME), "resumeAfterTopDestroyed"); |
| } |
| |
| @Test |
| public void testDestroyOnTopOfTranslucent() throws Exception { |
| // Launch fullscreen activity |
| final Activity firstActivity = launchActivityAndWait(FirstActivity.class); |
| // Launch translucent activity |
| final Activity translucentActivity = launchActivityAndWait(TranslucentActivity.class); |
| // Launch another fullscreen activity |
| final Activity secondActivity = launchActivityAndWait(SecondActivity.class); |
| |
| // Wait for top activity to resume |
| waitAndAssertActivityStates(state(translucentActivity, ON_STOP), |
| state(firstActivity, ON_STOP)); |
| |
| getLifecycleLog().clear(); |
| |
| final boolean secondActivityIsTranslucent = ActivityInfo.isTranslucentOrFloating( |
| secondActivity.getWindow().getWindowStyle()); |
| |
| // Finish top activity |
| secondActivity.finish(); |
| |
| waitAndAssertActivityStates(state(secondActivity, ON_DESTROY)); |
| LifecycleVerifier.assertResumeToDestroySequence(SecondActivity.class, getLifecycleLog()); |
| if (secondActivityIsTranslucent) { |
| // In this case we don't expect the state of the firstActivity to change since it is |
| // already in the visible paused state. So, we just verify that translucentActivity |
| // transitions to resumed state. |
| waitAndAssertActivityStates(state(translucentActivity, ON_RESUME)); |
| } else { |
| // Wait for translucent activity to resume |
| waitAndAssertActivityStates(state(translucentActivity, ON_RESUME), |
| state(firstActivity, ON_START)); |
| |
| // Verify that the first activity was restarted |
| LifecycleVerifier.assertRestartSequence(FirstActivity.class, getLifecycleLog()); |
| } |
| } |
| |
| @Test |
| public void testDestroyDoubleTranslucentOnTop() throws Exception { |
| final Activity firstActivity = launchActivityAndWait(FirstActivity.class); |
| final Activity translucentActivity = launchActivityAndWait(TranslucentActivity.class); |
| final Activity secondTranslucentActivity = |
| launchActivityAndWait(SecondTranslucentActivity.class); |
| waitAndAssertActivityStates(occludedActivityState(firstActivity, secondTranslucentActivity), |
| occludedActivityState(translucentActivity, secondTranslucentActivity)); |
| |
| // Finish top translucent activity |
| getLifecycleLog().clear(); |
| secondTranslucentActivity.finish(); |
| |
| waitAndAssertActivityStates(state(translucentActivity, ON_RESUME)); |
| waitAndAssertActivityStates(state(secondTranslucentActivity, ON_DESTROY)); |
| LifecycleVerifier.assertResumeToDestroySequence(SecondTranslucentActivity.class, |
| getLifecycleLog()); |
| LifecycleVerifier.assertSequence(TranslucentActivity.class, getLifecycleLog(), |
| Arrays.asList(ON_RESUME), "destroy"); |
| LifecycleVerifier.assertEmptySequence(FirstActivity.class, getLifecycleLog(), "destroy"); |
| |
| // Finish first translucent activity |
| getLifecycleLog().clear(); |
| translucentActivity.finish(); |
| |
| waitAndAssertActivityStates(state(firstActivity, ON_RESUME)); |
| waitAndAssertActivityStates(state(translucentActivity, ON_DESTROY)); |
| LifecycleVerifier.assertResumeToDestroySequence(TranslucentActivity.class, |
| getLifecycleLog()); |
| LifecycleVerifier.assertSequence(FirstActivity.class, getLifecycleLog(), |
| Arrays.asList(ON_RESUME), "secondDestroy"); |
| } |
| |
| @Test |
| public void testFinishBottom() throws Exception { |
| final Activity bottomActivity = launchActivityAndWait(FirstActivity.class); |
| final Activity topActivity = launchActivityAndWait(SecondActivity.class); |
| waitAndAssertActivityStates(state(bottomActivity, ON_STOP)); |
| |
| // Finish the activity on the bottom |
| getLifecycleLog().clear(); |
| bottomActivity.finish(); |
| |
| // Assert that activity on the bottom went directly to destroyed state, and activity on top |
| // did not get any lifecycle changes. |
| waitAndAssertActivityStates(state(bottomActivity, ON_DESTROY)); |
| LifecycleVerifier.assertSequence(FirstActivity.class, getLifecycleLog(), |
| Arrays.asList(ON_DESTROY), "destroyOnBottom"); |
| LifecycleVerifier.assertEmptySequence(SecondActivity.class, getLifecycleLog(), |
| "destroyOnBottom"); |
| } |
| |
| @Test |
| public void testFinishAndLaunchOnResult() throws Exception { |
| testLaunchForResultAndLaunchAfterResultSequence(EXTRA_LAUNCH_ON_RESULT); |
| } |
| |
| @Test |
| public void testFinishAndLaunchAfterOnResultInOnResume() throws Exception { |
| testLaunchForResultAndLaunchAfterResultSequence(EXTRA_LAUNCH_ON_RESUME_AFTER_RESULT); |
| } |
| |
| /** |
| * This triggers launch of an activity for result, which immediately finishes. After receiving |
| * result new activity launch is triggered automatically. |
| * @see android.server.wm.lifecycle.ActivityLifecycleClientTestBase.LaunchForResultActivity |
| */ |
| private void testLaunchForResultAndLaunchAfterResultSequence(String flag) throws Exception { |
| new Launcher(LaunchForResultActivity.class) |
| .customizeIntent(LaunchForResultActivity.forwardFlag(EXTRA_FINISH_IN_ON_RESUME)) |
| .setExtraFlags(flag) |
| .setExpectedState(ON_STOP) |
| .setNoInstance() |
| .launch(); |
| |
| waitAndAssertActivityStates(state(CallbackTrackingActivity.class, ON_TOP_POSITION_GAINED), |
| state(ResultActivity.class, ON_DESTROY)); |
| LifecycleVerifier.assertOrder(getLifecycleLog(), Arrays.asList( |
| // Base launching activity starting. |
| transition(LaunchForResultActivity.class, ON_CREATE), |
| transition(LaunchForResultActivity.class, ON_START), |
| transition(LaunchForResultActivity.class, ON_POST_CREATE), |
| transition(LaunchForResultActivity.class, ON_RESUME), |
| transition(LaunchForResultActivity.class, ON_TOP_POSITION_GAINED), |
| // An activity is automatically launched for result. |
| transition(LaunchForResultActivity.class, ON_TOP_POSITION_LOST), |
| transition(LaunchForResultActivity.class, ON_PAUSE), |
| transition(ResultActivity.class, ON_CREATE), |
| transition(ResultActivity.class, ON_START), |
| transition(ResultActivity.class, ON_RESUME), |
| transition(ResultActivity.class, ON_TOP_POSITION_GAINED), |
| // Activity that was launched for result is finished automatically - the base |
| // launching activity is brought to front. |
| transition(LaunchForResultActivity.class, ON_ACTIVITY_RESULT), |
| transition(LaunchForResultActivity.class, ON_RESUME), |
| transition(LaunchForResultActivity.class, ON_TOP_POSITION_GAINED), |
| // New activity is launched after receiving result in base activity. |
| transition(LaunchForResultActivity.class, ON_TOP_POSITION_LOST), |
| transition(LaunchForResultActivity.class, ON_PAUSE), |
| transition(CallbackTrackingActivity.class, ON_CREATE), |
| transition(CallbackTrackingActivity.class, ON_START), |
| transition(CallbackTrackingActivity.class, ON_RESUME), |
| transition(CallbackTrackingActivity.class, ON_TOP_POSITION_GAINED)), |
| "launchForResultAndLaunchAfterOnResult"); |
| } |
| |
| @Test |
| public void testLaunchAndDestroy() throws Exception { |
| final Activity activity = launchActivityAndWait(FirstActivity.class); |
| |
| activity.finish(); |
| waitAndAssertActivityStates(state(activity, ON_DESTROY)); |
| |
| LifecycleVerifier.assertLaunchAndDestroySequence(FirstActivity.class, getLifecycleLog()); |
| } |
| |
| @Test |
| public void testTrampoline() throws Exception { |
| testTrampolineLifecycle(false /* newTask */); |
| } |
| |
| @Test |
| public void testTrampolineNewTask() throws Exception { |
| testTrampolineLifecycle(true /* newTask */); |
| } |
| |
| /** |
| * Verifies that activity start from a trampoline will have the correct lifecycle and complete |
| * in time. The expected lifecycle is that the trampoline will skip ON_START - ON_STOP part of |
| * the usual sequence, and will go straight to ON_DESTROY after creation. |
| */ |
| private void testTrampolineLifecycle(boolean newTask) throws Exception { |
| // Run activity start manually (without using instrumentation) to make it async and measure |
| // time from the request correctly. |
| // TODO verify |
| final Launcher launcher = new Launcher(NoDisplayActivity.class) |
| .setFlags(FLAG_ACTIVITY_NEW_TASK) |
| .setExtraFlags(EXTRA_LAUNCH_ACTIVITY, EXTRA_FINISH_IN_ON_CREATE) |
| .setExpectedState(ON_DESTROY) |
| .setNoInstance(); |
| if (newTask) { |
| launcher.setExtraFlags(EXTRA_NEW_TASK); |
| } |
| launcher.launch(); |
| waitAndAssertActivityStates(state(CallbackTrackingActivity.class, ON_TOP_POSITION_GAINED)); |
| |
| LifecycleVerifier.assertEntireSequence(Arrays.asList( |
| transition(NoDisplayActivity.class, ON_CREATE), |
| transition(CallbackTrackingActivity.class, ON_CREATE), |
| transition(CallbackTrackingActivity.class, ON_START), |
| transition(CallbackTrackingActivity.class, ON_POST_CREATE), |
| transition(CallbackTrackingActivity.class, ON_RESUME), |
| transition(CallbackTrackingActivity.class, ON_TOP_POSITION_GAINED), |
| transition(NoDisplayActivity.class, ON_DESTROY)), |
| getLifecycleLog(), "trampolineLaunch"); |
| } |
| |
| /** @see #testTrampolineWithAnotherProcess */ |
| @Test |
| public void testTrampolineAnotherProcessNewTask() { |
| testTrampolineWithAnotherProcess(); |
| } |
| |
| /** |
| * Same as {@link #testTrampolineAnotherProcessNewTask()}, but with a living second process. |
| */ |
| @Test |
| public void testTrampolineAnotherExistingProcessNewTask() { |
| // Start the second process before running the test. It is to make a specific path that the |
| // the activity may be started when checking visibility instead of attaching its process. |
| mContext.startActivity(new Intent(mContext, SecondProcessCallbackTrackingActivity.class) |
| .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); |
| waitAndAssertActivityStates( |
| state(SecondProcessCallbackTrackingActivity.class, ON_TOP_POSITION_GAINED)); |
| getLifecycleLog().clear(); |
| |
| testTrampolineWithAnotherProcess(); |
| } |
| |
| /** |
| * Simulates X starts Y in the same task, and Y starts Z in another task then finishes itself: |
| * <pre> |
| * Top Task B: SecondProcessCallbackTrackingActivity (Z) |
| * Task A: SecondProcessCallbackTrackingActivity (Y) (finishing) |
| * FirstActivity (X) |
| * </pre> |
| * Expect Y to become invisible and then destroyed when the transition is done. |
| */ |
| private void testTrampolineWithAnotherProcess() { |
| // Use another process so its lifecycle won't be affected by the caller activity. |
| final Intent intent2 = new Intent(mContext, SecondProcessCallbackTrackingActivity.class) |
| .addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK); |
| final Intent intent1 = new Intent(mContext, SecondProcessCallbackTrackingActivity.class) |
| .putExtra(EXTRA_START_ACTIVITY_IN_ON_CREATE, intent2) |
| .putExtra(EXTRA_FINISH_IN_ON_CREATE, true); |
| mContext.startActivity(new Intent(mContext, FirstActivity.class) |
| .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK) |
| .putExtra(EXTRA_START_ACTIVITY_WHEN_IDLE, intent1)); |
| waitAndAssertActivityStates(state(SecondProcessCallbackTrackingActivity.class, ON_DESTROY)); |
| |
| // Finish and wait for test activity to be destroyed to avoid affecting the following test. |
| // The way we use here is to launch the same instance of |
| // SecondProcessCallbackTrackingActivity and then ask it to finish itself. |
| final Intent intent3 = new Intent(mContext, SecondProcessCallbackTrackingActivity.class) |
| .addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_SINGLE_TOP) |
| .putExtra(EXTRA_FINISH_IN_ON_RESUME, true); |
| mContext.startActivity(intent3); |
| mWmState.waitForActivityRemoved( |
| getComponentName(SecondProcessCallbackTrackingActivity.class)); |
| } |
| |
| @Test |
| public void testRelaunchResumed() throws Exception { |
| final Activity activity = launchActivityAndWait(FirstActivity.class); |
| |
| getLifecycleLog().clear(); |
| getInstrumentation().runOnMainSync(activity::recreate); |
| waitAndAssertActivityStates(state(activity, ON_RESUME)); |
| |
| LifecycleVerifier.assertRelaunchSequence(FirstActivity.class, getLifecycleLog(), ON_RESUME); |
| } |
| |
| @Test |
| public void testRelaunchPaused() throws Exception { |
| final Activity pausedActivity = launchActivityAndWait(FirstActivity.class); |
| final Activity translucentActivity = launchActivityAndWait(TranslucentActivity.class); |
| |
| waitAndAssertActivityStates(occludedActivityState(pausedActivity, translucentActivity)); |
| |
| getLifecycleLog().clear(); |
| getInstrumentation().runOnMainSync(pausedActivity::recreate); |
| waitAndAssertActivityStates(state(pausedActivity, ON_PAUSE)); |
| |
| LifecycleVerifier.assertRelaunchSequence(FirstActivity.class, getLifecycleLog(), ON_PAUSE); |
| } |
| |
| @Test |
| public void testRelaunchStopped() throws Exception { |
| final Activity stoppedActivity = launchActivityAndWait(FirstActivity.class); |
| launchActivityAndWait(SecondActivity.class); |
| |
| waitAndAssertActivityStates(state(stoppedActivity, ON_STOP)); |
| |
| getLifecycleLog().clear(); |
| getInstrumentation().runOnMainSync(stoppedActivity::recreate); |
| waitAndAssertActivityStates(state(stoppedActivity, ON_STOP)); |
| |
| LifecycleVerifier.assertRelaunchSequence(FirstActivity.class, getLifecycleLog(), |
| ON_STOP); |
| } |
| |
| @Test |
| public void testRelaunchConfigurationChangedWhileBecomingVisible() throws Exception { |
| if (!supportsRotation()) { |
| // Skip rotation test if device doesn't support it. |
| return; |
| } |
| |
| final ActivityOptions options = ActivityOptions.makeBasic(); |
| options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN); |
| |
| final Activity becomingVisibleActivity = |
| new Launcher(FirstActivity.class).setOptions(options).launch(); |
| final Activity translucentActivity = |
| new Launcher(TranslucentActivity.class).setOptions(options).launch(); |
| final Activity topOpaqueActivity = |
| new Launcher(SecondActivity.class).setOptions(options).launch(); |
| |
| waitAndAssertActivityStates( |
| state(becomingVisibleActivity, ON_STOP), |
| state(translucentActivity, ON_STOP)); |
| |
| final RotationSession rotationSession = createManagedRotationSession(); |
| if (!supportsLockedUserRotation( |
| rotationSession, translucentActivity.getDisplay().getDisplayId())) { |
| return; |
| } |
| |
| getLifecycleLog().clear(); |
| |
| final int current = rotationSession.get(); |
| // Set new rotation to cause a configuration change. |
| switch (current) { |
| case ROTATION_0: |
| case ROTATION_180: |
| rotationSession.set(ROTATION_90); |
| break; |
| case ROTATION_90: |
| case ROTATION_270: |
| rotationSession.set(ROTATION_0); |
| break; |
| default: |
| fail("Unknown rotation:" + current); |
| } |
| |
| // Assert that the top activity was relaunched. |
| waitAndAssertActivityStates(state(topOpaqueActivity, ON_RESUME)); |
| LifecycleVerifier.assertRelaunchSequence( |
| SecondActivity.class, getLifecycleLog(), ON_RESUME); |
| |
| // Finish the top activity |
| getLifecycleLog().clear(); |
| topOpaqueActivity.finish(); |
| |
| // Assert that the translucent activity and the activity visible behind it were |
| // relaunched. |
| waitAndAssertActivityStates(state(becomingVisibleActivity, ON_PAUSE), |
| state(translucentActivity, ON_RESUME)); |
| |
| LifecycleVerifier.assertSequence(FirstActivity.class, getLifecycleLog(), |
| Arrays.asList(ON_DESTROY, ON_CREATE, ON_START, ON_RESUME, |
| ON_PAUSE), "becomingVisiblePaused"); |
| final List<LifecycleLog.ActivityCallback> expectedSequence = |
| Arrays.asList(ON_DESTROY, ON_CREATE, ON_START, ON_RESUME); |
| LifecycleVerifier.assertSequence(TranslucentActivity.class, getLifecycleLog(), |
| expectedSequence, "becomingVisibleResumed"); |
| } |
| |
| @Test |
| public void testLaunchActivityWithFlagForwardResult() throws Exception { |
| final ActivityMonitor resultMonitor = getInstrumentation().addMonitor( |
| ResultActivity.class.getName(), null /* result */, false /* block */); |
| |
| new Launcher(LaunchForwardResultActivity.class) |
| .setExpectedState(ON_STOP) |
| .setNoInstance() |
| .launch(); |
| |
| final Activity resultActivity = getInstrumentation() |
| .waitForMonitorWithTimeout(resultMonitor, 5000); |
| getInstrumentation().runOnMainSync(resultActivity::finish); |
| waitAndAssertActivityStates(state(LaunchForwardResultActivity.class, |
| ON_TOP_POSITION_GAINED)); |
| |
| // verify the result have sent back to original activity |
| final List<LifecycleLog.ActivityCallback> expectedSequence = |
| Arrays.asList(ON_CREATE, ON_START, ON_POST_CREATE, ON_RESUME, |
| ON_TOP_POSITION_GAINED, ON_TOP_POSITION_LOST, ON_PAUSE, ON_STOP, |
| ON_ACTIVITY_RESULT, ON_RESTART, ON_START, ON_RESUME, |
| ON_TOP_POSITION_GAINED); |
| LifecycleVerifier.assertSequence(LaunchForwardResultActivity.class, getLifecycleLog(), |
| expectedSequence, "becomingVisibleResumed"); |
| } |
| |
| @Test |
| public void testOnActivityResult() throws Exception { |
| new Launcher(LaunchForResultActivity.class) |
| .customizeIntent(LaunchForResultActivity.forwardFlag(EXTRA_FINISH_IN_ON_RESUME)) |
| .launch(); |
| |
| final List<LifecycleLog.ActivityCallback> expectedSequence = |
| Arrays.asList(ON_CREATE, ON_START, ON_POST_CREATE, ON_RESUME, |
| ON_TOP_POSITION_GAINED, ON_TOP_POSITION_LOST, |
| ON_PAUSE, ON_ACTIVITY_RESULT, ON_RESUME, ON_TOP_POSITION_GAINED); |
| waitForActivityTransitions(LaunchForResultActivity.class, expectedSequence); |
| |
| // TODO(b/79218023): First activity might also be stopped before getting result. |
| final List<LifecycleLog.ActivityCallback> sequenceWithStop = |
| Arrays.asList(ON_CREATE, ON_START, ON_POST_CREATE, ON_RESUME, |
| ON_TOP_POSITION_GAINED, ON_TOP_POSITION_LOST, |
| ON_PAUSE, ON_STOP, ON_ACTIVITY_RESULT, ON_RESTART, ON_START, ON_RESUME, |
| ON_TOP_POSITION_GAINED); |
| final List<LifecycleLog.ActivityCallback> thirdSequence = |
| Arrays.asList(ON_CREATE, ON_START, ON_POST_CREATE, ON_RESUME, |
| ON_TOP_POSITION_GAINED, ON_TOP_POSITION_LOST, |
| ON_PAUSE, ON_STOP, ON_ACTIVITY_RESULT, ON_RESTART, ON_START, ON_RESUME, |
| ON_TOP_POSITION_GAINED); |
| final List<LifecycleLog.ActivityCallback> fourthSequence = |
| Arrays.asList(ON_CREATE, ON_START, ON_POST_CREATE, ON_RESUME, |
| ON_TOP_POSITION_GAINED, ON_TOP_POSITION_LOST, |
| ON_PAUSE, ON_STOP, ON_RESTART, ON_START, ON_ACTIVITY_RESULT, ON_RESUME, |
| ON_TOP_POSITION_GAINED); |
| LifecycleVerifier.assertSequenceMatchesOneOf(LaunchForResultActivity.class, |
| getLifecycleLog(), |
| Arrays.asList(expectedSequence, sequenceWithStop, thirdSequence, fourthSequence), |
| "activityResult"); |
| } |
| |
| @Test |
| public void testOnActivityResultAfterStop() throws Exception { |
| final ActivityMonitor resultMonitor = getInstrumentation().addMonitor( |
| ResultActivity.class.getName(), null /* result */, false /* block */); |
| final ActivityMonitor launchMonitor = getInstrumentation().addMonitor( |
| LaunchForResultActivity.class.getName(), null/* result */, false /* block */); |
| new Launcher(LaunchForResultActivity.class) |
| // TODO (b/127741025) temporarily use setNoInstance, because startActivitySync will |
| // cause launch timeout when more than 2 activities start consecutively. |
| .setNoInstance() |
| .setExpectedState(ON_STOP) |
| .launch(); |
| final Activity activity = getInstrumentation() |
| .waitForMonitorWithTimeout(launchMonitor, 5000); |
| waitAndAssertActivityStates(state(activity, ON_STOP)); |
| final Activity resultActivity = getInstrumentation() |
| .waitForMonitorWithTimeout(resultMonitor, 5000); |
| waitAndAssertActivityStates(state(resultActivity, ON_TOP_POSITION_GAINED)); |
| getInstrumentation().runOnMainSync(resultActivity::finish); |
| |
| final boolean isTranslucent = isTranslucent(activity); |
| |
| final List<List<LifecycleLog.ActivityCallback>> expectedSequences; |
| if (isTranslucent) { |
| expectedSequences = Arrays.asList( |
| Arrays.asList(ON_CREATE, ON_START, ON_POST_CREATE, ON_RESUME, |
| ON_TOP_POSITION_GAINED, ON_TOP_POSITION_LOST, ON_PAUSE, |
| ON_ACTIVITY_RESULT, ON_RESUME, ON_TOP_POSITION_GAINED) |
| ); |
| } else { |
| expectedSequences = Arrays.asList( |
| Arrays.asList(ON_CREATE, ON_START, ON_POST_CREATE, ON_RESUME, |
| ON_TOP_POSITION_GAINED, ON_TOP_POSITION_LOST, |
| ON_PAUSE, ON_STOP, ON_RESTART, ON_START, ON_ACTIVITY_RESULT, ON_RESUME, |
| ON_TOP_POSITION_GAINED), |
| Arrays.asList(ON_CREATE, ON_START, ON_POST_CREATE, ON_RESUME, |
| ON_TOP_POSITION_GAINED, ON_TOP_POSITION_LOST, |
| ON_PAUSE, ON_STOP, ON_ACTIVITY_RESULT, ON_RESTART, ON_START, ON_RESUME, |
| ON_TOP_POSITION_GAINED) |
| ); |
| } |
| waitForActivityTransitions(LaunchForResultActivity.class, expectedSequences.get(0)); |
| |
| LifecycleVerifier.assertSequenceMatchesOneOf(LaunchForResultActivity.class, |
| getLifecycleLog(), expectedSequences, "activityResult"); |
| } |
| |
| @Test |
| public void testOnPostCreateAfterRecreateInOnResume() throws Exception { |
| final Activity trackingActivity = launchActivityAndWait(CallbackTrackingActivity.class); |
| |
| // Call "recreate" and assert sequence |
| getLifecycleLog().clear(); |
| getInstrumentation().runOnMainSync(trackingActivity::recreate); |
| waitAndAssertActivityStates(state(trackingActivity, ON_TOP_POSITION_GAINED)); |
| |
| LifecycleVerifier.assertSequence(CallbackTrackingActivity.class, |
| getLifecycleLog(), |
| 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), |
| "recreate"); |
| } |
| |
| @Test |
| public void testOnPostCreateAfterRecreateInOnPause() throws Exception { |
| final Activity trackingActivity = launchActivityAndWait(CallbackTrackingActivity.class); |
| |
| // Launch translucent activity, which will make the first one paused. |
| final Activity translucentActivity = launchActivityAndWait(TranslucentActivity.class); |
| |
| // Wait for first activity to become paused |
| waitAndAssertActivityStates(occludedActivityState(trackingActivity, translucentActivity)); |
| |
| // Call "recreate" and assert sequence |
| getLifecycleLog().clear(); |
| getInstrumentation().runOnMainSync(trackingActivity::recreate); |
| waitAndAssertActivityStates(occludedActivityState(trackingActivity, translucentActivity)); |
| |
| LifecycleVerifier.assertSequence(CallbackTrackingActivity.class, |
| getLifecycleLog(), |
| Arrays.asList(ON_STOP, ON_DESTROY, ON_CREATE, ON_START, |
| ON_POST_CREATE, ON_RESUME, ON_PAUSE), |
| "recreate"); |
| } |
| |
| @Test |
| public void testOnPostCreateAfterRecreateInOnStop() throws Exception { |
| // Launch first activity |
| final Activity trackingActivity = launchActivityAndWait(CallbackTrackingActivity.class); |
| // Launch second activity to cover and stop first |
| final Activity secondActivity = launchActivityAndWait(SecondActivity.class); |
| // Wait for first activity to become stopped |
| waitAndAssertActivityStates(state(trackingActivity, ON_STOP)); |
| |
| // Call "recreate" and assert sequence |
| getLifecycleLog().clear(); |
| getInstrumentation().runOnMainSync(trackingActivity::recreate); |
| waitAndAssertActivityStates(state(trackingActivity, ON_STOP)); |
| |
| final List<LifecycleLog.ActivityCallback> callbacks; |
| if (isTranslucent(secondActivity)) { |
| callbacks = Arrays.asList(ON_STOP, ON_DESTROY, ON_CREATE, ON_START, |
| ON_POST_CREATE, ON_RESUME, ON_PAUSE); |
| } else { |
| callbacks = Arrays.asList(ON_DESTROY, ON_CREATE, ON_START, |
| ON_POST_CREATE, ON_RESUME, ON_PAUSE, ON_STOP); |
| } |
| |
| LifecycleVerifier.assertSequence( |
| CallbackTrackingActivity.class, getLifecycleLog(), callbacks, "recreate"); |
| } |
| |
| /** |
| * The following test ensures an activity is brought back if its process is ended in the |
| * background. |
| */ |
| @Test |
| public void testRestoreFromKill() throws Exception { |
| final LaunchActivityBuilder builder = getLaunchActivityBuilder(); |
| final ComponentName targetActivity = builder.getTargetActivity(); |
| |
| // Launch activity whose process will be killed |
| builder.execute(); |
| |
| // Start fullscreen activity in another process to put original activity in background. |
| final Activity testActivity = new Launcher(FirstActivity.class) |
| .setOptions(getLaunchOptionsForFullscreen()) |
| .launch(); |
| final boolean isTranslucent = isTranslucent(testActivity); |
| mWmState.waitForActivityState( |
| targetActivity, isTranslucent ? STATE_PAUSED : STATE_STOPPED); |
| |
| // Only try to kill targetActivity if the top activity isn't translucent. If the top |
| // activity is translucent then targetActivity will be visible, so the process will be |
| // started again really quickly. |
| if (!isTranslucent) { |
| // Kill first activity |
| AmUtils.runKill(targetActivity.getPackageName(), true /* wait */); |
| } |
| |
| // Return back to first activity |
| pressBackButton(); |
| |
| // Verify activity is resumed |
| mWmState.waitForValidState(targetActivity); |
| mWmState.assertResumedActivity("Originally launched activity should be resumed", |
| targetActivity); |
| } |
| |
| /** |
| * Tests that recreate request from an activity is executed immediately. |
| */ |
| @Test |
| public void testLocalRecreate() throws Exception { |
| // Launch the activity that will recreate itself |
| final Activity recreatingActivity = launchActivityAndWait(SingleTopActivity.class); |
| |
| // Launch second activity to cover and stop first |
| final Activity secondActivity = new Launcher(SecondActivity.class) |
| .setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK) |
| .setOptions(getLaunchOptionsForFullscreen()) |
| .launch(); |
| |
| // Wait for first activity to become occluded |
| waitAndAssertActivityStates(state(recreatingActivity, ON_STOP)); |
| |
| // Launch the activity again to recreate |
| getLifecycleLog().clear(); |
| new Launcher(SingleTopActivity.class) |
| .setFlags(FLAG_ACTIVITY_NEW_TASK) |
| .setExtraFlags(EXTRA_RECREATE) |
| .launch(); |
| |
| // Wait for activity to relaunch and resume |
| final List<LifecycleLog.ActivityCallback> expectedRelaunchSequence; |
| if (isTranslucent(secondActivity)) { |
| expectedRelaunchSequence = Arrays.asList(ON_NEW_INTENT, ON_RESUME, |
| ON_TOP_POSITION_GAINED, ON_TOP_POSITION_LOST, |
| ON_PAUSE, ON_STOP, ON_DESTROY, ON_CREATE, ON_START, |
| ON_POST_CREATE, ON_RESUME, ON_TOP_POSITION_GAINED); |
| } else { |
| expectedRelaunchSequence = Arrays.asList(ON_RESTART, ON_START, ON_NEW_INTENT, ON_RESUME, |
| ON_TOP_POSITION_GAINED, ON_TOP_POSITION_LOST, ON_PAUSE, ON_STOP, ON_DESTROY, |
| ON_CREATE, ON_START, ON_POST_CREATE, ON_RESUME, ON_TOP_POSITION_GAINED); |
| } |
| |
| waitForActivityTransitions(SingleTopActivity.class, expectedRelaunchSequence); |
| LifecycleVerifier.assertSequence(SingleTopActivity.class, getLifecycleLog(), |
| expectedRelaunchSequence, "recreate"); |
| } |
| |
| @Test |
| public void testOnNewIntent() throws Exception { |
| // Launch a singleTop activity |
| launchActivityAndWait(SingleTopActivity.class); |
| |
| LifecycleVerifier.assertLaunchSequence(SingleTopActivity.class, getLifecycleLog()); |
| |
| // Try to launch again |
| getLifecycleLog().clear(); |
| new Launcher(SingleTopActivity.class) |
| .setFlags(FLAG_ACTIVITY_NEW_TASK) |
| .setNoInstance() |
| .launch(); |
| |
| // Verify that the first activity was paused, new intent was delivered and resumed again |
| LifecycleVerifier.assertSequence(SingleTopActivity.class, getLifecycleLog(), |
| Arrays.asList(ON_TOP_POSITION_LOST, ON_PAUSE, ON_NEW_INTENT, ON_RESUME, |
| ON_TOP_POSITION_GAINED), "newIntent"); |
| } |
| |
| @Test |
| public void testOnNewIntentFromHidden() throws Exception { |
| // Launch a singleTop activity |
| final Activity singleTopActivity = launchActivityAndWait(SingleTopActivity.class); |
| LifecycleVerifier.assertLaunchSequence(SingleTopActivity.class, getLifecycleLog()); |
| |
| // Launch something on top |
| final Activity secondActivity = new Launcher(SecondActivity.class) |
| .setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK) |
| .setOptions(getLaunchOptionsForFullscreen()) |
| .launch(); |
| |
| waitAndAssertActivityStates(state(singleTopActivity, ON_STOP)); |
| |
| // Try to launch again |
| getLifecycleLog().clear(); |
| new Launcher(SingleTopActivity.class) |
| .setFlags(FLAG_ACTIVITY_NEW_TASK) |
| .setNoInstance() |
| .launch(); |
| |
| // Verify that the first activity was restarted, new intent was delivered and resumed again |
| final List<LifecycleLog.ActivityCallback> expectedSequence; |
| if (isTranslucent(singleTopActivity)) { |
| expectedSequence = Arrays.asList(ON_NEW_INTENT, ON_RESUME, ON_TOP_POSITION_GAINED); |
| } else { |
| expectedSequence = Arrays.asList(ON_RESTART, ON_START, ON_NEW_INTENT, ON_RESUME, |
| ON_TOP_POSITION_GAINED); |
| } |
| LifecycleVerifier.assertSequence(SingleTopActivity.class, getLifecycleLog(), |
| expectedSequence, "newIntent"); |
| } |
| |
| @Test |
| public void testOnNewIntentFromPaused() throws Exception { |
| // Launch a singleTop activity |
| final Activity singleTopActivity = launchActivityAndWait(SingleTopActivity.class); |
| LifecycleVerifier.assertLaunchSequence(SingleTopActivity.class, getLifecycleLog()); |
| |
| // Launch translucent activity, which will make the first one paused. |
| launchActivityAndWait(TranslucentActivity.class); |
| |
| // Wait for the activity below to pause |
| waitAndAssertActivityStates(state(singleTopActivity, ON_PAUSE)); |
| |
| // Try to launch again |
| getLifecycleLog().clear(); |
| new Launcher(SingleTopActivity.class) |
| .setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TOP) |
| .setNoInstance() |
| .launch(); |
| |
| // Wait for the activity to resume again |
| // Verify that the new intent was delivered and resumed again |
| final List<LifecycleLog.ActivityCallback> expectedSequence = |
| Arrays.asList(ON_NEW_INTENT, ON_RESUME, ON_TOP_POSITION_GAINED); |
| waitForActivityTransitions(SingleTopActivity.class, expectedSequence); |
| LifecycleVerifier.assertSequence(SingleTopActivity.class, getLifecycleLog(), |
| expectedSequence, "newIntent"); |
| } |
| |
| @Test |
| public void testFinishInOnCreate() throws Exception { |
| verifyFinishAtStage( ResultActivity.class, EXTRA_FINISH_IN_ON_CREATE, |
| Arrays.asList(ON_CREATE, ON_DESTROY), "onCreate"); |
| } |
| |
| @Test |
| public void testFinishInOnCreateNoDisplay() throws Exception { |
| verifyFinishAtStage(NoDisplayActivity.class, EXTRA_FINISH_IN_ON_CREATE, |
| Arrays.asList(ON_CREATE, ON_DESTROY), "onCreate"); |
| } |
| |
| @Test |
| public void testFinishInOnStart() throws Exception { |
| verifyFinishAtStage(ResultActivity.class, EXTRA_FINISH_IN_ON_START, |
| Arrays.asList(ON_CREATE, ON_START, ON_POST_CREATE, ON_STOP, |
| ON_DESTROY), "onStart"); |
| } |
| |
| @Test |
| public void testFinishInOnStartNoDisplay() throws Exception { |
| verifyFinishAtStage(NoDisplayActivity.class, EXTRA_FINISH_IN_ON_START, |
| Arrays.asList(ON_CREATE, ON_START, ON_POST_CREATE, ON_STOP, |
| ON_DESTROY), "onStart"); |
| } |
| |
| @Test |
| public void testFinishInOnResume() throws Exception { |
| verifyFinishAtStage(ResultActivity.class, EXTRA_FINISH_IN_ON_RESUME, |
| Arrays.asList(ON_CREATE, ON_START, ON_POST_CREATE, ON_RESUME, |
| ON_TOP_POSITION_GAINED, ON_TOP_POSITION_LOST, ON_PAUSE, ON_STOP, |
| ON_DESTROY), "onResume"); |
| } |
| |
| @Test |
| public void testFinishInOnResumeNoDisplay() throws Exception { |
| verifyFinishAtStage(NoDisplayActivity.class, EXTRA_FINISH_IN_ON_RESUME, |
| Arrays.asList(ON_CREATE, ON_START, ON_POST_CREATE, ON_RESUME, |
| ON_TOP_POSITION_GAINED, ON_TOP_POSITION_LOST, ON_PAUSE, ON_STOP, |
| ON_DESTROY), "onResume"); |
| } |
| |
| private void verifyFinishAtStage(Class<? extends Activity> activityClass, |
| String finishStageExtra, List<LifecycleLog.ActivityCallback> expectedSequence, |
| String stageName) throws Exception { |
| new Launcher(activityClass) |
| .setExpectedState(ON_DESTROY) |
| .setExtraFlags(finishStageExtra) |
| .setNoInstance() |
| .launch(); |
| |
| waitAndAssertActivityTransitions(activityClass, expectedSequence, "finish in " + stageName); |
| } |
| |
| @Test |
| public void testFinishInOnPause() throws Exception { |
| verifyFinishAtStage(ResultActivity.class, EXTRA_FINISH_IN_ON_PAUSE, "onPause", |
| TranslucentActivity.class); |
| } |
| |
| @Test |
| public void testFinishInOnStop() throws Exception { |
| verifyFinishAtStage(ResultActivity.class, EXTRA_FINISH_IN_ON_STOP, "onStop", |
| FirstActivity.class); |
| } |
| |
| @Test |
| public void testFinishBelowDialogActivity() throws Exception { |
| verifyFinishAtStage(ResultActivity.class, EXTRA_FINISH_IN_ON_PAUSE, "onPause", |
| TranslucentCallbackTrackingActivity.class); |
| } |
| |
| private void verifyFinishAtStage(Class<? extends Activity> activityClass, |
| String finishStageExtra, String stageName, Class<? extends Activity> launchOnTopClass) |
| throws Exception { |
| |
| // Activity will finish itself after onResume, so need to launch an extra activity on |
| // top to get it there. |
| new Launcher(activityClass) |
| .setExtraFlags(finishStageExtra) |
| .launch(); |
| |
| // Launch an activity on top, which will make the first one paused or stopped. |
| launchActivityAndWait(launchOnTopClass); |
| |
| final List<LifecycleLog.ActivityCallback> expectedSequence = |
| LifecycleVerifier.getLaunchAndDestroySequence(activityClass); |
| waitAndAssertActivityTransitions(activityClass, expectedSequence, "finish in " + stageName); |
| } |
| |
| @Test |
| public void testFinishBelowTranslucentActivityAfterDelay() throws Exception { |
| final Activity bottomActivity = launchActivityAndWait(CallbackTrackingActivity.class); |
| |
| launchActivityAndWait(TranslucentCallbackTrackingActivity.class); |
| waitAndAssertActivityStates(state(bottomActivity, ON_PAUSE)); |
| getLifecycleLog().clear(); |
| |
| waitForIdle(); |
| bottomActivity.finish(); |
| waitAndAssertActivityStates(state(bottomActivity, ON_DESTROY)); |
| LifecycleVerifier.assertEmptySequence(TranslucentCallbackTrackingActivity.class, |
| getLifecycleLog(), "finishBelow"); |
| } |
| |
| @Test |
| public void testFinishBelowFullscreenActivityAfterDelay() throws Exception { |
| final Activity bottomActivity = launchActivityAndWait(CallbackTrackingActivity.class); |
| |
| launchActivityAndWait(FirstActivity.class); |
| waitAndAssertActivityStates(state(bottomActivity, ON_STOP)); |
| getLifecycleLog().clear(); |
| |
| waitForIdle(); |
| bottomActivity.finish(); |
| waitAndAssertActivityStates(state(bottomActivity, ON_DESTROY)); |
| LifecycleVerifier.assertEmptySequence(FirstActivity.class, getLifecycleLog(), |
| "finishBelow"); |
| } |
| |
| @Test |
| public void testSingleTopActivityOnActivityResultNewTask() throws Exception { |
| testSingleTopActivityForResult(true /* newTask */); |
| } |
| |
| @Test |
| public void testSingleTopActivityOnActivityResult() throws Exception { |
| testSingleTopActivityForResult(false /* newTask */); |
| } |
| |
| private void testSingleTopActivityForResult(boolean newTask) throws Exception { |
| // Launch a singleTop activity |
| final Launcher launcher = new Launcher(SingleTopActivity.class) |
| .setExtraFlags(EXTRA_LAUNCH_ACTIVITY); |
| |
| if (newTask) { |
| launcher.setExtraFlags(EXTRA_NEW_TASK); |
| } |
| final Activity activity = launcher.launch(); |
| waitAndAssertActivityStates(state(activity, ON_TOP_POSITION_GAINED)); |
| |
| // Verify the result have been sent back to original activity |
| LifecycleVerifier.assertTransitionObserved(getLifecycleLog(), |
| transition(SingleTopActivity.class, ON_ACTIVITY_RESULT),"activityResult"); |
| } |
| |
| @Test |
| public void testLaunchOnUserLeaveHint() throws Exception { |
| new Launcher(FirstActivity.class) |
| .setExtraFlags(EXTRA_ACTIVITY_ON_USER_LEAVE_HINT) |
| .launch(); |
| |
| getLifecycleLog().clear(); |
| launchActivityAndWait(SecondActivity.class); |
| waitAndAssertActivityStates(state(FirstActivity.class, ON_STOP)); |
| |
| LifecycleVerifier.assertTransitionObserved(getLifecycleLog(), |
| transition(FirstActivity.class, ON_USER_LEAVE_HINT),"userLeaveHint"); |
| } |
| |
| @Test |
| public void testLaunchOnUserLeaveHintWithNoUserAction() throws Exception { |
| new Launcher(FirstActivity.class) |
| .setExtraFlags(EXTRA_ACTIVITY_ON_USER_LEAVE_HINT) |
| .launch(); |
| |
| getLifecycleLog().clear(); |
| new Launcher(SecondActivity.class) |
| .setFlags(FLAG_ACTIVITY_NO_USER_ACTION | FLAG_ACTIVITY_NEW_TASK) |
| .launch(); |
| waitAndAssertActivityStates(state(FirstActivity.class, ON_STOP)); |
| |
| LifecycleVerifier.assertTransitionNotObserved(getLifecycleLog(), |
| transition(FirstActivity.class, ON_USER_LEAVE_HINT),"userLeaveHint"); |
| } |
| } |