1 /* 2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package android.server.wm; 17 18 import static android.server.wm.ActivityManagerTestBase.wakeUpAndUnlock; 19 20 import static org.junit.Assert.assertFalse; 21 import static org.junit.Assert.assertTrue; 22 import static org.junit.Assert.fail; 23 24 import android.app.Dialog; 25 import android.app.Instrumentation; 26 import android.platform.test.annotations.Presubmit; 27 import android.view.KeyEvent; 28 import android.window.OnBackInvokedCallback; 29 import android.window.OnBackInvokedDispatcher; 30 31 import androidx.lifecycle.Lifecycle; 32 import androidx.test.core.app.ActivityScenario; 33 import androidx.test.ext.junit.rules.ActivityScenarioRule; 34 import androidx.test.platform.app.InstrumentationRegistry; 35 36 import org.junit.Before; 37 import org.junit.Rule; 38 import org.junit.Test; 39 40 import java.util.concurrent.CountDownLatch; 41 import java.util.concurrent.TimeUnit; 42 43 /** 44 * Integration test for back navigation 45 */ 46 @Presubmit 47 public class BackNavigationTests { 48 @Rule 49 public final ActivityScenarioRule<BackNavigationActivity> mScenarioRule = 50 new ActivityScenarioRule<>(BackNavigationActivity.class); 51 private ActivityScenario<BackNavigationActivity> mScenario; 52 private Instrumentation mInstrumentation; 53 54 @Before setup()55 public void setup() { 56 mInstrumentation = InstrumentationRegistry.getInstrumentation(); 57 wakeUpAndUnlock(mInstrumentation.getContext()); 58 mScenario = mScenarioRule.getScenario(); 59 } 60 61 @Test registerCallback_initialized()62 public void registerCallback_initialized() { 63 CountDownLatch latch = registerBackCallback(); 64 mScenario.moveToState(Lifecycle.State.RESUMED); 65 invokeBackAndAssertCallbackIsCalled(latch); 66 } 67 68 @Test registerCallback_created()69 public void registerCallback_created() { 70 mScenario.moveToState(Lifecycle.State.CREATED); 71 CountDownLatch latch = registerBackCallback(); 72 mScenario.moveToState(Lifecycle.State.RESUMED); 73 invokeBackAndAssertCallbackIsCalled(latch); 74 } 75 76 @Test registerCallback_resumed()77 public void registerCallback_resumed() { 78 mScenario.moveToState(Lifecycle.State.RESUMED); 79 CountDownLatch latch = registerBackCallback(); 80 invokeBackAndAssertCallbackIsCalled(latch); 81 } 82 83 @Test registerCallback_dialog()84 public void registerCallback_dialog() { 85 CountDownLatch backInvokedLatch = new CountDownLatch(1); 86 mScenario.onActivity(activity -> { 87 Dialog dialog = new Dialog(activity, 0); 88 dialog.getOnBackInvokedDispatcher().registerOnBackInvokedCallback( 89 OnBackInvokedDispatcher.PRIORITY_DEFAULT, () -> { 90 backInvokedLatch.countDown(); 91 }); 92 dialog.show(); 93 }); 94 invokeBackAndAssertCallbackIsCalled(backInvokedLatch); 95 } 96 97 @Test onBackPressedNotCalled()98 public void onBackPressedNotCalled() { 99 mScenario.moveToState(Lifecycle.State.RESUMED); 100 CountDownLatch latch = registerBackCallback(); 101 invokeBackAndAssertCallbackIsCalled(latch); 102 mScenario.onActivity((activity) -> 103 assertFalse("Activity.onBackPressed should not be called", 104 activity.mOnBackPressedCalled)); 105 } 106 107 @Test registerCallback_relaunch()108 public void registerCallback_relaunch() { 109 mScenario.moveToState(Lifecycle.State.RESUMED); 110 CountDownLatch latch1 = registerBackCallback(); 111 112 ActivityScenario<BackNavigationActivity> newScenario = mScenario.recreate(); 113 newScenario.moveToState(Lifecycle.State.RESUMED); 114 CountDownLatch latch2 = registerBackCallback(newScenario, true); 115 116 invokeBackAndAssertCallbackIsCalled(latch2); 117 invokeBackAndAssertCallback(latch1, false); 118 } 119 invokeBackAndAssertCallbackIsCalled(CountDownLatch latch)120 private void invokeBackAndAssertCallbackIsCalled(CountDownLatch latch) { 121 invokeBackAndAssertCallback(latch, true); 122 } 123 invokeBackAndAssertCallback(CountDownLatch latch, boolean isCalled)124 private void invokeBackAndAssertCallback(CountDownLatch latch, boolean isCalled) { 125 try { 126 // Make sure the application is idle and input windows is up-to-date. 127 mInstrumentation.waitForIdleSync(); 128 mInstrumentation.getUiAutomation().syncInputTransactions(); 129 TouchHelper.injectKey(KeyEvent.KEYCODE_BACK, false /* longpress */, true /* sync */); 130 if (isCalled) { 131 assertTrue("OnBackInvokedCallback.onBackInvoked() was not called", 132 latch.await(500, TimeUnit.MILLISECONDS)); 133 } else { 134 assertFalse("OnBackInvokedCallback.onBackInvoked() was called", 135 latch.await(500, TimeUnit.MILLISECONDS)); 136 } 137 } catch (InterruptedException ex) { 138 fail("Application died before invoking the callback.\n" + ex.getMessage()); 139 } 140 } 141 registerBackCallback()142 private CountDownLatch registerBackCallback() { 143 return registerBackCallback(mScenario, false); 144 } 145 registerBackCallback(ActivityScenario<?> scenario, boolean unregisterAfterCalled)146 private CountDownLatch registerBackCallback(ActivityScenario<?> scenario, 147 boolean unregisterAfterCalled) { 148 CountDownLatch backInvokedLatch = new CountDownLatch(1); 149 CountDownLatch backRegisteredLatch = new CountDownLatch(1); 150 final OnBackInvokedCallback callback = new OnBackInvokedCallback() { 151 @Override 152 public void onBackInvoked() { 153 backInvokedLatch.countDown(); 154 if (unregisterAfterCalled) { 155 scenario.onActivity(activity -> activity.getOnBackInvokedDispatcher() 156 .unregisterOnBackInvokedCallback(this)); 157 } 158 } 159 }; 160 161 scenario.onActivity(activity -> { 162 activity.getOnBackInvokedDispatcher().registerOnBackInvokedCallback(0, callback); 163 backRegisteredLatch.countDown(); 164 }); 165 try { 166 if (!backRegisteredLatch.await(100, TimeUnit.MILLISECONDS)) { 167 fail("Back callback was not registered on the Activity thread. This might be " 168 + "an error with the test itself."); 169 } 170 } catch (InterruptedException e) { 171 fail(e.getMessage()); 172 } 173 return backInvokedLatch; 174 } 175 } 176