| /* |
| * Copyright (C) 2016 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.accessibilityservice.cts; |
| |
| import static android.accessibilityservice.cts.utils.AccessibilityEventFilterUtils.filterWindowsChangeTypesAndWindowTitle; |
| import static android.accessibilityservice.cts.utils.AccessibilityEventFilterUtils.filterWindowsChangedWithChangeTypes; |
| import static android.accessibilityservice.cts.utils.ActivityLaunchUtils.findWindowByTitle; |
| import static android.accessibilityservice.cts.utils.ActivityLaunchUtils.findWindowByTitleAndDisplay; |
| import static android.accessibilityservice.cts.utils.ActivityLaunchUtils.getActivityTitle; |
| import static android.accessibilityservice.cts.utils.ActivityLaunchUtils.launchActivityAndWaitForItToBeOnscreen; |
| import static android.accessibilityservice.cts.utils.ActivityLaunchUtils.launchActivityOnSpecifiedDisplayAndWaitForItToBeOnscreen; |
| import static android.accessibilityservice.cts.utils.ActivityLaunchUtils.supportsMultiDisplay; |
| import static android.accessibilityservice.cts.utils.AsyncUtils.DEFAULT_TIMEOUT_MS; |
| import static android.accessibilityservice.cts.utils.DisplayUtils.VirtualDisplaySession; |
| import static android.accessibilityservice.cts.utils.DisplayUtils.getStatusBarHeight; |
| import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE; |
| import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOWS_CHANGED; |
| import static android.view.accessibility.AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED; |
| import static android.view.accessibility.AccessibilityEvent.WINDOWS_CHANGE_ACTIVE; |
| import static android.view.accessibility.AccessibilityEvent.WINDOWS_CHANGE_ADDED; |
| import static android.view.accessibility.AccessibilityEvent.WINDOWS_CHANGE_BOUNDS; |
| import static android.view.accessibility.AccessibilityEvent.WINDOWS_CHANGE_CHILDREN; |
| import static android.view.accessibility.AccessibilityEvent.WINDOWS_CHANGE_FOCUSED; |
| import static android.view.accessibility.AccessibilityEvent.WINDOWS_CHANGE_REMOVED; |
| import static android.view.accessibility.AccessibilityEvent.WINDOWS_CHANGE_TITLE; |
| |
| import static junit.framework.TestCase.assertTrue; |
| |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertFalse; |
| import static org.junit.Assert.assertNotNull; |
| import static org.junit.Assume.assumeTrue; |
| |
| import android.Manifest; |
| import android.accessibility.cts.common.AccessibilityDumpOnFailureRule; |
| import android.accessibilityservice.AccessibilityServiceInfo; |
| import android.accessibilityservice.cts.activities.AccessibilityWindowReportingActivity; |
| import android.accessibilityservice.cts.activities.NonDefaultDisplayActivity; |
| import android.accessibilityservice.cts.activities.NotTouchableWindowTestActivity; |
| import android.app.Activity; |
| import android.app.Instrumentation; |
| import android.app.UiAutomation; |
| import android.content.ComponentName; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.graphics.Rect; |
| import android.os.SystemClock; |
| import android.platform.test.annotations.AppModeFull; |
| import android.provider.Settings; |
| import android.view.Gravity; |
| import android.view.InputDevice; |
| import android.view.MotionEvent; |
| import android.view.View; |
| import android.view.WindowManager; |
| import android.view.accessibility.AccessibilityNodeInfo; |
| import android.view.accessibility.AccessibilityWindowInfo; |
| import android.widget.ArrayAdapter; |
| import android.widget.AutoCompleteTextView; |
| import android.widget.Button; |
| |
| import androidx.test.InstrumentationRegistry; |
| import androidx.test.rule.ActivityTestRule; |
| import androidx.test.runner.AndroidJUnit4; |
| |
| import com.android.compatibility.common.util.SystemUtil; |
| |
| import org.junit.AfterClass; |
| import org.junit.Before; |
| import org.junit.BeforeClass; |
| import org.junit.Rule; |
| import org.junit.Test; |
| import org.junit.rules.RuleChain; |
| import org.junit.runner.RunWith; |
| |
| import java.io.IOException; |
| import java.util.List; |
| import java.util.concurrent.TimeoutException; |
| |
| /** |
| * Tests that window changes produce the correct events and that AccessibilityWindowInfos are |
| * properly populated |
| */ |
| @RunWith(AndroidJUnit4.class) |
| public class AccessibilityWindowReportingTest { |
| private static final int TIMEOUT_ASYNC_PROCESSING = 5000; |
| private static final CharSequence TOP_WINDOW_TITLE = |
| "android.accessibilityservice.cts.AccessibilityWindowReportingTest.TOP_WINDOW_TITLE"; |
| |
| private static Instrumentation sInstrumentation; |
| private static UiAutomation sUiAutomation; |
| private Activity mActivity; |
| private CharSequence mActivityTitle; |
| |
| private ActivityTestRule<AccessibilityWindowReportingActivity> mActivityRule = |
| new ActivityTestRule<>(AccessibilityWindowReportingActivity.class, false, false); |
| |
| private AccessibilityDumpOnFailureRule mDumpOnFailureRule = |
| new AccessibilityDumpOnFailureRule(); |
| |
| @Rule |
| public final RuleChain mRuleChain = RuleChain |
| .outerRule(mActivityRule) |
| .around(mDumpOnFailureRule); |
| |
| @BeforeClass |
| public static void oneTimeSetup() throws Exception { |
| sInstrumentation = InstrumentationRegistry.getInstrumentation(); |
| sUiAutomation = sInstrumentation.getUiAutomation(); |
| AccessibilityServiceInfo info = sUiAutomation.getServiceInfo(); |
| info.flags |= AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS; |
| sUiAutomation.setServiceInfo(info); |
| } |
| |
| @AfterClass |
| public static void finalTearDown() throws Exception { |
| sUiAutomation.destroy(); |
| } |
| |
| @Before |
| public void setUp() throws Exception { |
| mActivity = launchActivityAndWaitForItToBeOnscreen( |
| sInstrumentation, sUiAutomation, mActivityRule); |
| mActivityTitle = getActivityTitle(sInstrumentation, mActivity); |
| } |
| |
| private static boolean perDisplayFocusEnabled() { |
| return sInstrumentation.getTargetContext().getResources() |
| .getBoolean(android.R.bool.config_perDisplayFocusEnabled); |
| } |
| |
| @Test |
| public void testUpdatedWindowTitle_generatesEventAndIsReturnedByGetTitle() { |
| final String updatedTitle = "Updated Title"; |
| try { |
| sUiAutomation.executeAndWaitForEvent(() -> sInstrumentation.runOnMainSync( |
| () -> mActivity.setTitle(updatedTitle)), |
| filterWindowsChangedWithChangeTypes(WINDOWS_CHANGE_TITLE), |
| TIMEOUT_ASYNC_PROCESSING); |
| } catch (TimeoutException exception) { |
| throw new RuntimeException( |
| "Failed to get windows changed event for title update", exception); |
| } |
| final AccessibilityWindowInfo window = findWindowByTitle(sUiAutomation, updatedTitle); |
| assertNotNull("Updated window title not reported to accessibility", window); |
| window.recycle(); |
| } |
| |
| @Test |
| public void testWindowAddedMovedAndRemoved_generatesEventsForAllThree() throws Exception { |
| final WindowManager.LayoutParams paramsForTop = layoutParmsForWindowOnTop(); |
| final WindowManager.LayoutParams paramsForBottom = layoutParmsForWindowOnBottom(); |
| final Button button = new Button(mActivity); |
| button.setText(R.string.button1); |
| |
| addWindowAndWaitForEvent(button, paramsForTop, |
| filterWindowsChangedWithChangeTypes(WINDOWS_CHANGE_ADDED)); |
| |
| // Move window from top to bottom |
| sUiAutomation.executeAndWaitForEvent(() -> sInstrumentation.runOnMainSync( |
| () -> mActivity.getWindowManager().updateViewLayout(button, paramsForBottom)), |
| filterWindowsChangedWithChangeTypes(WINDOWS_CHANGE_BOUNDS), |
| TIMEOUT_ASYNC_PROCESSING); |
| // Remove the view |
| sUiAutomation.executeAndWaitForEvent(() -> sInstrumentation.runOnMainSync( |
| () -> mActivity.getWindowManager().removeView(button)), |
| filterWindowsChangedWithChangeTypes(WINDOWS_CHANGE_REMOVED), |
| TIMEOUT_ASYNC_PROCESSING); |
| } |
| |
| @Test |
| public void putWindowInPictureInPicture_generatesEventAndReportsProperty() throws Exception { |
| if (!sInstrumentation.getContext().getPackageManager() |
| .hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)) { |
| return; |
| } |
| sUiAutomation.executeAndWaitForEvent( |
| () -> sInstrumentation.runOnMainSync(() -> mActivity.enterPictureInPictureMode()), |
| (event) -> { |
| if (event.getEventType() != TYPE_WINDOWS_CHANGED) return false; |
| // Look for a picture-in-picture window |
| final List<AccessibilityWindowInfo> windows = sUiAutomation.getWindows(); |
| final int windowCount = windows.size(); |
| for (int i = 0; i < windowCount; i++) { |
| if (windows.get(i).isInPictureInPictureMode()) { |
| return true; |
| } |
| } |
| return false; |
| }, TIMEOUT_ASYNC_PROCESSING); |
| |
| // There should be exactly one picture-in-picture window now |
| int numPictureInPictureWindows = 0; |
| final List<AccessibilityWindowInfo> windows = sUiAutomation.getWindows(); |
| final int windowCount = windows.size(); |
| for (int i = 0; i < windowCount; i++) { |
| final AccessibilityWindowInfo window = windows.get(i); |
| if (window.isInPictureInPictureMode()) { |
| numPictureInPictureWindows++; |
| } |
| } |
| assertTrue(numPictureInPictureWindows >= 1); |
| } |
| |
| @Test |
| public void moveFocusToAnotherWindow_generatesEventsAndMovesActiveAndFocus() throws Exception { |
| final View topWindowView = showTopWindowAndWaitForItToShowUp(); |
| final AccessibilityWindowInfo topWindow = |
| findWindowByTitle(sUiAutomation, TOP_WINDOW_TITLE); |
| |
| AccessibilityWindowInfo activityWindow = findWindowByTitle(sUiAutomation, mActivityTitle); |
| final AccessibilityNodeInfo buttonNode = |
| topWindow.getRoot().findAccessibilityNodeInfosByText( |
| sInstrumentation.getContext().getString(R.string.button1)).get(0); |
| |
| // Make sure activityWindow is not focused |
| if (activityWindow.isFocused()) { |
| sUiAutomation.executeAndWaitForEvent( |
| () -> buttonNode.performAction(AccessibilityNodeInfo.ACTION_FOCUS), |
| filterWindowsChangedWithChangeTypes(WINDOWS_CHANGE_FOCUSED), |
| TIMEOUT_ASYNC_PROCESSING); |
| } |
| |
| // Windows may have changed - refresh |
| activityWindow = findWindowByTitle(sUiAutomation, mActivityTitle); |
| assertFalse(activityWindow.isActive()); |
| assertFalse(activityWindow.isFocused()); |
| |
| // Find a focusable view in the main activity menu |
| final AccessibilityNodeInfo autoCompleteTextInfo = activityWindow.getRoot() |
| .findAccessibilityNodeInfosByViewId( |
| "android.accessibilityservice.cts:id/autoCompleteLayout") |
| .get(0); |
| |
| // Remove the top window and focus on the main activity |
| sUiAutomation.executeAndWaitForEvent( |
| () -> { |
| sInstrumentation.runOnMainSync( |
| () -> mActivity.getWindowManager().removeView(topWindowView)); |
| buttonNode.performAction(AccessibilityNodeInfo.ACTION_FOCUS); |
| }, |
| filterWindowsChangedWithChangeTypes(WINDOWS_CHANGE_FOCUSED | WINDOWS_CHANGE_ACTIVE), |
| TIMEOUT_ASYNC_PROCESSING); |
| } |
| |
| @Test |
| public void moveFocusToAnotherDisplay_movesActiveAndFocusWindow() throws Exception { |
| assumeTrue(supportsMultiDisplay(sInstrumentation.getContext())); |
| |
| // Makes sure activityWindow on default display is focused |
| AccessibilityWindowInfo activityWindow = findWindowByTitle(sUiAutomation, mActivityTitle); |
| assertTrue(activityWindow.isActive()); |
| assertTrue(activityWindow.isFocused()); |
| |
| // Creates a virtual display. |
| try (VirtualDisplaySession displaySession = new VirtualDisplaySession()) { |
| final int virtualDisplayId = |
| displaySession.createDisplayWithDefaultDisplayMetricsAndWait( |
| sInstrumentation.getContext(), false).getDisplayId(); |
| // Launchs an activity on virtual display. |
| final Activity activityOnVirtualDisplay = |
| launchActivityOnSpecifiedDisplayAndWaitForItToBeOnscreen(sInstrumentation, |
| sUiAutomation, |
| NonDefaultDisplayActivity.class, |
| virtualDisplayId); |
| |
| final CharSequence activityTitle = getActivityTitle(sInstrumentation, |
| activityOnVirtualDisplay); |
| |
| // Window manager changed the behavior of focused window at a virtual display. A window |
| // at virtual display needs to be touched then it becomes to be focused one. Adding this |
| // touch event on the activity window of the virtual display to pass this test case. |
| sUiAutomation.executeAndWaitForEvent( |
| () -> { |
| final Rect areaOfActivityWindowOnVirtualDisplay = new Rect(); |
| findWindowByTitleAndDisplay(sUiAutomation, activityTitle, virtualDisplayId) |
| .getBoundsInScreen(areaOfActivityWindowOnVirtualDisplay); |
| |
| final int xOnScreen = |
| areaOfActivityWindowOnVirtualDisplay.centerX(); |
| final int yOnScreen = |
| areaOfActivityWindowOnVirtualDisplay.centerY(); |
| final long downEventTime = SystemClock.uptimeMillis(); |
| final MotionEvent downEvent = MotionEvent.obtain(downEventTime, |
| downEventTime, MotionEvent.ACTION_DOWN, xOnScreen, yOnScreen, 0); |
| downEvent.setSource(InputDevice.SOURCE_TOUCHSCREEN); |
| downEvent.setDisplayId(virtualDisplayId); |
| sUiAutomation.injectInputEvent(downEvent, true); |
| |
| final long upEventTime = downEventTime + 10; |
| final MotionEvent upEvent = MotionEvent.obtain(downEventTime, upEventTime, |
| MotionEvent.ACTION_UP, xOnScreen, yOnScreen, 0); |
| upEvent.setSource(InputDevice.SOURCE_TOUCHSCREEN); |
| upEvent.setDisplayId(virtualDisplayId); |
| sUiAutomation.injectInputEvent(upEvent, true); |
| }, |
| filterWindowsChangedWithChangeTypes(WINDOWS_CHANGE_FOCUSED | |
| WINDOWS_CHANGE_ACTIVE), |
| TIMEOUT_ASYNC_PROCESSING); |
| |
| // Make sure activityWindow on virtual display is focused. |
| AccessibilityWindowInfo activityWindowOnVirtualDisplay = |
| findWindowByTitleAndDisplay(sUiAutomation, activityTitle, virtualDisplayId); |
| // Windows may have changed - refresh. |
| activityWindow = findWindowByTitle(sUiAutomation, mActivityTitle); |
| try { |
| if (!perDisplayFocusEnabled()) { |
| assertFalse(activityWindow.isActive()); |
| assertFalse(activityWindow.isFocused()); |
| } else { |
| assertTrue(activityWindow.isActive()); |
| assertTrue(activityWindow.isFocused()); |
| } |
| assertTrue(activityWindowOnVirtualDisplay.isActive()); |
| assertTrue(activityWindowOnVirtualDisplay.isFocused()); |
| } finally { |
| sUiAutomation.executeAndWaitForEvent( |
| () -> { |
| sInstrumentation.runOnMainSync( |
| () -> activityOnVirtualDisplay.finish()); |
| }, |
| filterWindowsChangedWithChangeTypes(WINDOWS_CHANGE_FOCUSED | |
| WINDOWS_CHANGE_ACTIVE), |
| TIMEOUT_ASYNC_PROCESSING); |
| } |
| } |
| // The focused window should be returned to activity at default display after |
| // the activity at virtual display is destroyed. |
| activityWindow = findWindowByTitle(sUiAutomation, mActivityTitle); |
| assertTrue(activityWindow.isActive()); |
| assertTrue(activityWindow.isFocused()); |
| } |
| |
| @Test |
| public void testChangeAccessibilityFocusWindow_getEvent() throws Exception { |
| final AccessibilityServiceInfo info = sUiAutomation.getServiceInfo(); |
| info.flags |= AccessibilityServiceInfo.FLAG_REQUEST_TOUCH_EXPLORATION_MODE; |
| sUiAutomation.setServiceInfo(info); |
| |
| try { |
| showTopWindowAndWaitForItToShowUp(); |
| |
| final AccessibilityWindowInfo activityWindow = |
| findWindowByTitle(sUiAutomation, mActivityTitle); |
| final AccessibilityWindowInfo topWindow = |
| findWindowByTitle(sUiAutomation, TOP_WINDOW_TITLE); |
| final AccessibilityNodeInfo win2Node = |
| topWindow.getRoot().findAccessibilityNodeInfosByText( |
| sInstrumentation.getContext().getString(R.string.button1)).get(0); |
| final AccessibilityNodeInfo win1Node = activityWindow.getRoot() |
| .findAccessibilityNodeInfosByViewId( |
| "android.accessibilityservice.cts:id/autoCompleteLayout") |
| .get(0); |
| |
| sUiAutomation.executeAndWaitForEvent( |
| () -> { |
| win2Node.performAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS); |
| win1Node.performAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS); |
| }, |
| filterWindowsChangedWithChangeTypes(WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED), |
| TIMEOUT_ASYNC_PROCESSING); |
| } finally { |
| info.flags &= ~AccessibilityServiceInfo.FLAG_REQUEST_TOUCH_EXPLORATION_MODE; |
| sUiAutomation.setServiceInfo(info); |
| } |
| } |
| |
| @Test |
| public void testGetAnchorForDropDownForAutoCompleteTextView_returnsTextViewNode() { |
| final AutoCompleteTextView autoCompleteTextView = |
| (AutoCompleteTextView) mActivity.findViewById(R.id.autoCompleteLayout); |
| final AccessibilityNodeInfo autoCompleteTextInfo = sUiAutomation.getRootInActiveWindow() |
| .findAccessibilityNodeInfosByViewId( |
| "android.accessibilityservice.cts:id/autoCompleteLayout") |
| .get(0); |
| |
| // For the drop-down |
| final String[] COUNTRIES = new String[] {"Belgium", "France", "Italy", "Germany", "Spain"}; |
| |
| try { |
| sUiAutomation.executeAndWaitForEvent(() -> sInstrumentation.runOnMainSync( |
| () -> { |
| final ArrayAdapter<String> adapter = new ArrayAdapter<>( |
| mActivity, android.R.layout.simple_dropdown_item_1line, COUNTRIES); |
| autoCompleteTextView.setAdapter(adapter); |
| autoCompleteTextView.showDropDown(); |
| }), |
| filterWindowsChangeTypesAndWindowTitle(sUiAutomation, WINDOWS_CHANGE_CHILDREN, |
| mActivityTitle.toString()), TIMEOUT_ASYNC_PROCESSING); |
| } catch (TimeoutException exception) { |
| throw new RuntimeException( |
| "Failed to get window changed event when showing dropdown", exception); |
| } |
| |
| // Find the pop-up window |
| boolean foundPopup = false; |
| final List<AccessibilityWindowInfo> windows = sUiAutomation.getWindows(); |
| for (int i = 0; i < windows.size(); i++) { |
| final AccessibilityWindowInfo window = windows.get(i); |
| if (window.getAnchor() == null) { |
| continue; |
| } |
| assertEquals(autoCompleteTextInfo, window.getAnchor()); |
| assertFalse("Found multiple pop-ups anchored to one text view", foundPopup); |
| foundPopup = true; |
| } |
| assertTrue("Failed to find accessibility window for auto-complete pop-up", foundPopup); |
| } |
| |
| @AppModeFull |
| @Test |
| public void showNotTouchableWindow_activityWindowIsNotVisible() |
| throws TimeoutException { |
| try { |
| launchNotTouchableWindowTestActivityFromShell(); |
| |
| Intent intent = new Intent(); |
| intent.setAction(NotTouchableWindowTestActivity.ADD_WINDOW); |
| |
| try { |
| // Waits for the not-touchable activity is covered by the untrusted window. |
| sUiAutomation.executeAndWaitForEvent(() -> sInstrumentation.runOnMainSync( |
| () -> sInstrumentation.getContext().sendBroadcast(intent)), |
| (event) -> { |
| final AccessibilityWindowInfo notTouchableWindow = |
| findWindowByTitle(sUiAutomation, |
| NotTouchableWindowTestActivity.TITLE); |
| return notTouchableWindow == null; |
| }, TIMEOUT_ASYNC_PROCESSING); |
| } finally { |
| intent.setAction(NotTouchableWindowTestActivity.REMOVE_WINDOW); |
| sendIntentAndWaitForEvent(intent, |
| filterWindowsChangedWithChangeTypes(WINDOWS_CHANGE_REMOVED)); |
| } |
| } finally { |
| Intent intent = new Intent(); |
| intent.setAction(NotTouchableWindowTestActivity.FINISH_ACTIVITY); |
| sendIntentAndWaitForEvent(intent, |
| filterWindowsChangedWithChangeTypes(WINDOWS_CHANGE_REMOVED)); |
| } |
| } |
| |
| @AppModeFull |
| @Test |
| public void showNotTouchableTrustedWindow_activityWindowIsVisible() |
| throws TimeoutException { |
| try { |
| launchNotTouchableWindowTestActivityFromShell(); |
| |
| Intent intent = new Intent(); |
| intent.setAction(NotTouchableWindowTestActivity.ADD_TRUSTED_WINDOW); |
| |
| try { |
| SystemUtil.runWithShellPermissionIdentity(sUiAutomation, () -> { |
| sendIntentAndWaitForEvent(intent, |
| filterWindowsChangeTypesAndWindowTitle(sUiAutomation, |
| WINDOWS_CHANGE_ADDED, |
| NotTouchableWindowTestActivity.NON_TOUCHABLE_WINDOW_TITLE.toString()) |
| ); |
| }, Manifest.permission.INTERNAL_SYSTEM_WINDOW); |
| |
| List<AccessibilityWindowInfo> windows = sUiAutomation.getWindows(); |
| assertNotNull(windows); |
| |
| assertEquals(1, windows.stream().filter( |
| w -> NotTouchableWindowTestActivity.TITLE.equals(w.getTitle())).count()); |
| } finally { |
| intent.setAction(NotTouchableWindowTestActivity.REMOVE_WINDOW); |
| sendIntentAndWaitForEvent(intent, |
| filterWindowsChangedWithChangeTypes(WINDOWS_CHANGE_REMOVED)); |
| } |
| } finally { |
| Intent intent = new Intent(); |
| intent.setAction(NotTouchableWindowTestActivity.FINISH_ACTIVITY); |
| sendIntentAndWaitForEvent(intent, |
| filterWindowsChangedWithChangeTypes(WINDOWS_CHANGE_REMOVED)); |
| } |
| } |
| |
| // We want to test WindowState#isTrustedOverlay which refers to flag stored in the |
| // Session class and is not updated since the Session is created. |
| // Use shell command instead of ActivityLaunchUtils to get INTERNAL_SYSTEM_WINDOW |
| // permission when the Session is created. |
| private void launchNotTouchableWindowTestActivityFromShell() { |
| SystemUtil.runWithShellPermissionIdentity(sUiAutomation, () -> { |
| sUiAutomation.executeAndWaitForEvent( |
| () -> { |
| final ComponentName componentName = new ComponentName( |
| sInstrumentation.getContext(), NotTouchableWindowTestActivity.class); |
| |
| String command = "am start -n " + componentName.flattenToString(); |
| try { |
| SystemUtil.runShellCommand(sInstrumentation, command); |
| } catch (IOException e) { |
| throw new RuntimeException(e); |
| } |
| }, |
| (event) -> { |
| final AccessibilityWindowInfo window = |
| findWindowByTitleAndDisplay(sUiAutomation, |
| NotTouchableWindowTestActivity.TITLE, 0); |
| return window != null; |
| }, DEFAULT_TIMEOUT_MS); |
| }, Manifest.permission.INTERNAL_SYSTEM_WINDOW); |
| } |
| |
| /** |
| * Test whether we can successfully enable and disable window animations. |
| */ |
| @Test |
| public void testDisableWindowAnimations() { |
| setAndAssertAnimationScale(0.0f); |
| setAndAssertAnimationScale(0.5f); |
| setAndAssertAnimationScale(1.0f); |
| } |
| |
| /** Sets the animation scale to a specified value and asserts that the value has been set. */ |
| private void setAndAssertAnimationScale(float value) { |
| Context context = sInstrumentation.getContext(); |
| sUiAutomation.setAnimationScale(value); |
| assertEquals(value, getGlobalFloat(context, Settings.Global.WINDOW_ANIMATION_SCALE), 0.0f); |
| assertEquals( |
| value, getGlobalFloat(context, Settings.Global.TRANSITION_ANIMATION_SCALE), 0.0f); |
| assertEquals(value, getGlobalFloat(context, Settings.Global.ANIMATOR_DURATION_SCALE), 0.0f); |
| } |
| |
| /** Returns value of constants in Settings.Global. */ |
| private static float getGlobalFloat(Context context, String constantName) { |
| float value = Settings.Global.getFloat(context.getContentResolver(), constantName, -1); |
| return value; |
| } |
| |
| private View showTopWindowAndWaitForItToShowUp() throws TimeoutException { |
| final WindowManager.LayoutParams paramsForTop = layoutParmsForWindowOnTop(); |
| final Button button = new Button(mActivity); |
| button.setText(R.string.button1); |
| addWindowAndWaitForEvent(button, paramsForTop, (event) -> { |
| return (event.getEventType() == TYPE_WINDOWS_CHANGED) |
| && (findWindowByTitle(sUiAutomation, mActivityTitle) != null) |
| && (findWindowByTitle(sUiAutomation, TOP_WINDOW_TITLE) != null); |
| }); |
| return button; |
| } |
| |
| private WindowManager.LayoutParams layoutParmsForWindowOnTop() { |
| final WindowManager.LayoutParams params = layoutParmsForTestWindow(); |
| params.gravity = Gravity.TOP; |
| params.setTitle(TOP_WINDOW_TITLE); |
| sInstrumentation.runOnMainSync(() -> { |
| params.y = getStatusBarHeight(mActivity); |
| }); |
| return params; |
| } |
| |
| private WindowManager.LayoutParams layoutParmsForWindowOnBottom() { |
| final WindowManager.LayoutParams params = layoutParmsForTestWindow(); |
| params.gravity = Gravity.BOTTOM; |
| return params; |
| } |
| |
| private WindowManager.LayoutParams layoutParmsForTestWindow() { |
| final WindowManager.LayoutParams params = new WindowManager.LayoutParams(); |
| params.width = WindowManager.LayoutParams.MATCH_PARENT; |
| params.height = WindowManager.LayoutParams.WRAP_CONTENT; |
| params.flags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN |
| | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL |
| | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR; |
| params.type = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL; |
| sInstrumentation.runOnMainSync(() -> { |
| params.token = mActivity.getWindow().getDecorView().getWindowToken(); |
| }); |
| return params; |
| } |
| |
| private void addWindowAndWaitForEvent(View view, WindowManager.LayoutParams params, |
| UiAutomation.AccessibilityEventFilter filter) |
| throws TimeoutException { |
| sUiAutomation.executeAndWaitForEvent(() -> sInstrumentation.runOnMainSync( |
| () -> mActivity.getWindowManager().addView(view, params)), |
| filter, |
| TIMEOUT_ASYNC_PROCESSING); |
| } |
| |
| private void sendIntentAndWaitForEvent(Intent intent, |
| UiAutomation.AccessibilityEventFilter filter) throws TimeoutException { |
| sUiAutomation.executeAndWaitForEvent(() -> sInstrumentation.runOnMainSync( |
| () -> sInstrumentation.getContext().sendBroadcast(intent)), |
| filter, |
| TIMEOUT_ASYNC_PROCESSING); |
| } |
| |
| } |