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 17 package android.server.wm.backnavigation; 18 19 import static android.server.wm.WindowManagerState.STATE_RESUMED; 20 import static android.view.Display.DEFAULT_DISPLAY; 21 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_PANEL; 22 import static android.window.BackNavigationInfo.TYPE_CALLBACK; 23 import static android.window.BackNavigationInfo.TYPE_CROSS_ACTIVITY; 24 import static android.window.BackNavigationInfo.TYPE_CROSS_TASK; 25 import static android.window.BackNavigationInfo.TYPE_DIALOG_CLOSE; 26 import static android.window.BackNavigationInfo.TYPE_RETURN_TO_HOME; 27 28 import static org.junit.Assert.assertEquals; 29 import static org.junit.Assert.assertTrue; 30 import static org.junit.Assume.assumeTrue; 31 32 import android.app.Activity; 33 import android.app.AlertDialog; 34 import android.content.ComponentName; 35 import android.content.Context; 36 import android.graphics.Color; 37 import android.os.Bundle; 38 import android.platform.test.annotations.Presubmit; 39 import android.server.wm.ActivityManagerTestBase; 40 import android.server.wm.ComponentNameUtils; 41 import android.server.wm.MockImeHelper; 42 import android.view.Gravity; 43 import android.view.ViewGroup; 44 import android.view.inputmethod.InputMethodManager; 45 import android.widget.EditText; 46 import android.widget.FrameLayout; 47 import android.widget.LinearLayout; 48 import android.widget.PopupWindow; 49 50 import androidx.annotation.Nullable; 51 52 import org.junit.Before; 53 import org.junit.Test; 54 55 /** 56 * Integration test for back navigation mode 57 * 58 * <p>Build/Install/Run: 59 * atest CtsWindowManagerDeviceBackNavigation:BackGestureInvokedTest 60 */ 61 @Presubmit 62 @android.server.wm.annotation.Group1 63 public class BackGestureInvokedTest extends ActivityManagerTestBase { 64 private TestActivitySession<BackInvokedActivity> mActivitySession; 65 private BackInvokedActivity mActivity; 66 67 @Before setUp()68 public void setUp() throws Exception { 69 super.setUp(); 70 enableAndAssumeGestureNavigationMode(); 71 72 mActivitySession = createManagedTestActivitySession(); 73 mActivitySession.launchTestActivityOnDisplaySync( 74 BackInvokedActivity.class, DEFAULT_DISPLAY); 75 mWmState.waitForAppTransitionIdleOnDisplay(DEFAULT_DISPLAY); 76 mActivity = mActivitySession.getActivity(); 77 mWmState.waitForActivityState(mActivity.getComponentName(), STATE_RESUMED); 78 mWmState.assertVisibility(mActivity.getComponentName(), true); 79 } 80 81 @Test popupWindowDismissedOnBackGesture()82 public void popupWindowDismissedOnBackGesture() { 83 mActivitySession.runOnMainSyncAndWait(mActivity::addPopupWindow); 84 final int taskId = mWmState.getTaskByActivity(mActivity.getComponentName()).getTaskId(); 85 assertTrue("Popup window must show", mWmState.waitFor(state -> 86 mWmState.getMatchingWindows(ws -> ws.getType() == TYPE_APPLICATION_PANEL 87 && ws.getStackId() == taskId && ws.isSurfaceShown()) 88 .findAny().isPresent(), "popup window show")); 89 triggerBackEventByGesture(DEFAULT_DISPLAY); 90 91 assertTrue("Popup window must be removed", mWmState.waitFor(state -> 92 mWmState.getMatchingWindows(ws -> ws.getType() == TYPE_APPLICATION_PANEL 93 && ws.getStackId() == taskId).findAny().isEmpty(), 94 "popup window removed")); 95 96 // activity remain focused 97 mWmState.assertFocusedActivity("Top activity must be focused", 98 mActivity.getComponentName()); 99 final String windowName = ComponentNameUtils.getWindowName(mActivity.getComponentName()); 100 mWmState.assertFocusedWindow( 101 "Top activity window must be focused window.", windowName); 102 } 103 104 @Test testBackToHome()105 public void testBackToHome() { 106 triggerBackEventByGesture(DEFAULT_DISPLAY); 107 mWmState.waitAndAssertActivityRemoved(mActivity.getComponentName()); 108 109 assertEquals(TYPE_RETURN_TO_HOME, mWmState.getBackNavigationState().getLastBackType()); 110 } 111 112 @Test testBackToTask()113 public void testBackToTask() { 114 final ComponentName 115 componentName = new ComponentName(mContext, NewTaskActivity.class); 116 launchActivityInNewTask(componentName); 117 mWmState.waitForActivityState(componentName, STATE_RESUMED); 118 mWmState.assertVisibility(componentName, true); 119 mWmState.waitForFocusedActivity("Wait for launched activity to be in front.", 120 componentName); 121 triggerBackEventByGesture(DEFAULT_DISPLAY); 122 mWmState.waitForActivityState(mActivity.getComponentName(), STATE_RESUMED); 123 124 assertEquals(TYPE_CROSS_TASK, mWmState.getBackNavigationState().getLastBackType()); 125 } 126 127 @Test testBackToActivity()128 public void testBackToActivity() { 129 final ComponentName componentName = new ComponentName(mContext, SecondActivity.class); 130 launchActivity(componentName); 131 mWmState.waitForActivityState(componentName, STATE_RESUMED); 132 mWmState.assertVisibility(componentName, true); 133 mWmState.waitForFocusedActivity("Wait for launched activity to be in front.", 134 componentName); 135 triggerBackEventByGesture(DEFAULT_DISPLAY); 136 mWmState.waitForActivityState(mActivity.getComponentName(), STATE_RESUMED); 137 138 assertEquals(TYPE_CROSS_ACTIVITY, mWmState.getBackNavigationState().getLastBackType()); 139 } 140 141 @Test testDialogDismissed()142 public void testDialogDismissed() { 143 mInstrumentation.runOnMainSync(() -> { 144 new AlertDialog.Builder(mActivity) 145 .setTitle("Dialog") 146 .show(); 147 }); 148 mInstrumentation.waitForIdleSync(); 149 150 triggerBackEventByGesture(DEFAULT_DISPLAY); 151 152 // activity remain focused 153 mWmState.waitForFocusedActivity("top activity to be focused", 154 mActivity.getComponentName()); 155 mWmState.assertFocusedActivity("Top activity must be focused", 156 mActivity.getComponentName()); 157 158 assertEquals(TYPE_DIALOG_CLOSE, mWmState.getBackNavigationState().getLastBackType()); 159 } 160 161 @Test testImeDismissed()162 public void testImeDismissed() { 163 assumeTrue(MSG_NO_MOCK_IME, supportsInstallableIme()); 164 MockImeHelper.createManagedMockImeSession(this); 165 166 ComponentName componentName = new ComponentName(mContext, ImeTestActivity.class); 167 launchActivity(componentName); 168 mWmState.waitForActivityState(componentName, STATE_RESUMED); 169 mWmState.assertVisibility(componentName, true); 170 mWmState.waitAndAssertImeWindowShownOnDisplay(DEFAULT_DISPLAY); 171 172 triggerBackEventByGesture(DEFAULT_DISPLAY); 173 mWmState.waitAndAssertImeWindowHiddenOnDisplay(DEFAULT_DISPLAY); 174 175 assertEquals(TYPE_CALLBACK, mWmState.getBackNavigationState().getLastBackType()); 176 } 177 178 public static class BackInvokedActivity extends Activity { 179 180 private FrameLayout mContentView; 181 getContentView()182 private FrameLayout getContentView() { 183 return mContentView; 184 } 185 186 @Override onCreate(@ullable Bundle savedInstanceState)187 protected void onCreate(@Nullable Bundle savedInstanceState) { 188 super.onCreate(savedInstanceState); 189 mContentView = new FrameLayout(this); 190 setContentView(mContentView); 191 } 192 addPopupWindow()193 public void addPopupWindow() { 194 FrameLayout contentView = new FrameLayout(this); 195 contentView.setBackgroundColor(Color.RED); 196 PopupWindow popup = new PopupWindow(contentView, ViewGroup.LayoutParams.MATCH_PARENT, 197 ViewGroup.LayoutParams.MATCH_PARENT); 198 199 // Ensure the window can get the focus by marking the views as focusable 200 popup.setFocusable(true); 201 contentView.setFocusable(true); 202 popup.showAtLocation(getContentView(), Gravity.FILL, 0, 0); 203 } 204 } 205 206 public static class NewTaskActivity extends Activity { 207 private FrameLayout mContentView; 208 209 @Override onCreate(@ullable Bundle savedInstanceState)210 protected void onCreate(@Nullable Bundle savedInstanceState) { 211 super.onCreate(savedInstanceState); 212 mContentView = new FrameLayout(this); 213 mContentView.setBackgroundColor(Color.GREEN); 214 setContentView(mContentView); 215 } 216 } 217 218 public static class SecondActivity extends Activity { 219 private FrameLayout mContentView; 220 221 @Override onCreate(@ullable Bundle savedInstanceState)222 protected void onCreate(@Nullable Bundle savedInstanceState) { 223 super.onCreate(savedInstanceState); 224 mContentView = new FrameLayout(this); 225 mContentView.setBackgroundColor(Color.BLUE); 226 setContentView(mContentView); 227 } 228 } 229 230 public static class ImeTestActivity extends Activity { 231 EditText mEditText; 232 233 @Override onCreate(Bundle icicle)234 protected void onCreate(Bundle icicle) { 235 super.onCreate(icicle); 236 mEditText = new EditText(this); 237 final LinearLayout layout = new LinearLayout(this); 238 layout.setOrientation(LinearLayout.VERTICAL); 239 layout.addView(mEditText); 240 if (mEditText.requestFocus()) { 241 InputMethodManager imm = (InputMethodManager) 242 getSystemService(Context.INPUT_METHOD_SERVICE); 243 imm.showSoftInput(mEditText, InputMethodManager.SHOW_IMPLICIT); 244 } 245 setContentView(layout); 246 } 247 } 248 } 249