• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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