1 /* 2 * Copyright (C) 2023 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 com.android.wm.shell.flicker.service.splitscreen.scenarios 18 19 import android.app.Instrumentation 20 import android.graphics.Point 21 import android.tools.common.NavBar 22 import android.tools.common.Rotation 23 import android.tools.device.helpers.WindowUtils 24 import android.tools.device.traces.parsers.WindowManagerStateHelper 25 import androidx.test.platform.app.InstrumentationRegistry 26 import androidx.test.uiautomator.UiDevice 27 import com.android.launcher3.tapl.LauncherInstrumentation 28 import com.android.wm.shell.flicker.service.Utils 29 import com.android.wm.shell.flicker.utils.SplitScreenUtils 30 import org.junit.After 31 import org.junit.Before 32 import org.junit.Ignore 33 import org.junit.Rule 34 import org.junit.Test 35 36 @Ignore("Base Test Class") 37 abstract class SwitchAppByDoubleTapDivider 38 @JvmOverloads 39 constructor(val rotation: Rotation = Rotation.ROTATION_0) { 40 private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() 41 private val tapl = LauncherInstrumentation() 42 private val wmHelper = WindowManagerStateHelper(instrumentation) 43 private val device = UiDevice.getInstance(instrumentation) 44 private val primaryApp = SplitScreenUtils.getPrimary(instrumentation) 45 private val secondaryApp = SplitScreenUtils.getSecondary(instrumentation) 46 47 @Rule @JvmField val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, rotation) 48 49 @Before 50 fun setup() { 51 tapl.setEnableRotation(true) 52 tapl.setExpectedRotation(rotation.value) 53 tapl.workspace.switchToOverview().dismissAllTasks() 54 55 SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp) 56 } 57 58 @Test 59 open fun switchAppByDoubleTapDivider() { 60 SplitScreenUtils.doubleTapDividerToSwitch(device) 61 wmHelper.StateSyncBuilder().withAppTransitionIdle().waitForAndVerify() 62 63 waitForLayersToSwitch(wmHelper) 64 waitForWindowsToSwitch(wmHelper) 65 } 66 67 @After 68 fun teardown() { 69 primaryApp.exit(wmHelper) 70 secondaryApp.exit(wmHelper) 71 } 72 73 private fun waitForWindowsToSwitch(wmHelper: WindowManagerStateHelper) { 74 wmHelper 75 .StateSyncBuilder() 76 .add("appWindowsSwitched") { 77 val primaryAppWindow = 78 it.wmState.visibleWindows.firstOrNull { window -> 79 primaryApp.windowMatchesAnyOf(window) 80 } 81 ?: return@add false 82 val secondaryAppWindow = 83 it.wmState.visibleWindows.firstOrNull { window -> 84 secondaryApp.windowMatchesAnyOf(window) 85 } 86 ?: return@add false 87 88 if (isLandscape(rotation)) { 89 return@add if (isTablet()) { 90 secondaryAppWindow.frame.right <= primaryAppWindow.frame.left 91 } else { 92 primaryAppWindow.frame.right <= secondaryAppWindow.frame.left 93 } 94 } else { 95 return@add if (isTablet()) { 96 primaryAppWindow.frame.bottom <= secondaryAppWindow.frame.top 97 } else { 98 primaryAppWindow.frame.bottom <= secondaryAppWindow.frame.top 99 } 100 } 101 } 102 .waitForAndVerify() 103 } 104 105 private fun waitForLayersToSwitch(wmHelper: WindowManagerStateHelper) { 106 wmHelper 107 .StateSyncBuilder() 108 .add("appLayersSwitched") { 109 val primaryAppLayer = 110 it.layerState.visibleLayers.firstOrNull { window -> 111 primaryApp.layerMatchesAnyOf(window) 112 } 113 ?: return@add false 114 val secondaryAppLayer = 115 it.layerState.visibleLayers.firstOrNull { window -> 116 secondaryApp.layerMatchesAnyOf(window) 117 } 118 ?: return@add false 119 120 val primaryVisibleRegion = primaryAppLayer.visibleRegion?.bounds ?: return@add false 121 val secondaryVisibleRegion = 122 secondaryAppLayer.visibleRegion?.bounds ?: return@add false 123 124 if (isLandscape(rotation)) { 125 return@add if (isTablet()) { 126 secondaryVisibleRegion.right <= primaryVisibleRegion.left 127 } else { 128 primaryVisibleRegion.right <= secondaryVisibleRegion.left 129 } 130 } else { 131 return@add if (isTablet()) { 132 primaryVisibleRegion.bottom <= secondaryVisibleRegion.top 133 } else { 134 primaryVisibleRegion.bottom <= secondaryVisibleRegion.top 135 } 136 } 137 } 138 .waitForAndVerify() 139 } 140 141 private fun isLandscape(rotation: Rotation): Boolean { 142 val displayBounds = WindowUtils.getDisplayBounds(rotation) 143 return displayBounds.width > displayBounds.height 144 } 145 146 private fun isTablet(): Boolean { 147 val sizeDp: Point = device.displaySizeDp 148 val LARGE_SCREEN_DP_THRESHOLD = 600 149 return sizeDp.x >= LARGE_SCREEN_DP_THRESHOLD && sizeDp.y >= LARGE_SCREEN_DP_THRESHOLD 150 } 151 } 152