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.server.wm.flicker.helpers
18 
19 import android.app.Instrumentation
20 import android.graphics.Rect
21 import android.graphics.Region
22 import android.tools.device.apphelpers.StandardAppHelper
23 import android.tools.helpers.FIND_TIMEOUT
24 import android.tools.helpers.SYSTEMUI_PACKAGE
25 import android.tools.traces.component.ComponentNameMatcher
26 import android.tools.traces.parsers.WindowManagerStateHelper
27 import android.tools.traces.parsers.toFlickerComponent
28 import androidx.test.uiautomator.By
29 import androidx.test.uiautomator.Until
30 import com.android.server.wm.flicker.testapp.ActivityOptions
31 
32 class LetterboxAppHelper
33 @JvmOverloads
34 constructor(
35     instr: Instrumentation,
36     launcherName: String = ActivityOptions.NonResizeablePortraitActivity.LABEL,
37     component: ComponentNameMatcher =
38         ActivityOptions.NonResizeablePortraitActivity.COMPONENT.toFlickerComponent()
39 ) : StandardAppHelper(instr, launcherName, component) {
40 
41     private val gestureHelper: GestureHelper = GestureHelper(instrumentation)
42 
clickRestartnull43     fun clickRestart(wmHelper: WindowManagerStateHelper) {
44         val restartButton =
45             uiDevice.wait(
46                 Until.findObject(By.res(SYSTEMUI_PACKAGE, "size_compat_restart_button")),
47                 FIND_TIMEOUT
48             )
49         restartButton?.run { restartButton.click() } ?: error("Restart button not found")
50 
51         // size compat mode restart confirmation dialog button
52         val restartDialogButton =
53             uiDevice.wait(
54                 Until.findObject(
55                     By.res(SYSTEMUI_PACKAGE, "letterbox_restart_dialog_restart_button")
56                 ),
57                 FIND_TIMEOUT
58             )
59         restartDialogButton?.run { restartDialogButton.click() }
60             ?: error("Restart dialog button not found")
61         wmHelper.StateSyncBuilder().withAppTransitionIdle().waitForAndVerify()
62     }
63 
repositionHorizontallynull64     fun repositionHorizontally(displayBounds: Rect, right: Boolean) {
65         val x = if (right) displayBounds.right - BOUNDS_OFFSET else BOUNDS_OFFSET
66         reposition(x.toFloat(), displayBounds.centerY().toFloat())
67     }
68 
repositionVerticallynull69     fun repositionVertically(displayBounds: Rect, bottom: Boolean) {
70         val y = if (bottom) displayBounds.bottom - BOUNDS_OFFSET else BOUNDS_OFFSET
71         reposition(displayBounds.centerX().toFloat(), y.toFloat())
72     }
73 
repositionnull74     private fun reposition(x: Float, y: Float) {
75         val coords = GestureHelper.Tuple(x, y)
76         require(gestureHelper.tap(coords, 2)) { "Failed to reposition letterbox app" }
77     }
78 
waitForAppToMoveHorizontallyTonull79     fun waitForAppToMoveHorizontallyTo(
80         wmHelper: WindowManagerStateHelper,
81         displayBounds: Rect,
82         right: Boolean
83     ) {
84         wmHelper
85             .StateSyncBuilder()
86             .add("letterboxAppRepositioned") {
87                 val letterboxAppWindow = getWindowRegion(wmHelper)
88                 val appRegionBounds = letterboxAppWindow.bounds
89                 val appWidth = appRegionBounds.width()
90                 return@add if (right)
91                     appRegionBounds.left == displayBounds.right - appWidth &&
92                         appRegionBounds.right == displayBounds.right
93                 else
94                     appRegionBounds.left == displayBounds.left &&
95                         appRegionBounds.right == displayBounds.left + appWidth
96             }
97             .waitForAndVerify()
98     }
99 
waitForAppToMoveVerticallyTonull100     fun waitForAppToMoveVerticallyTo(
101         wmHelper: WindowManagerStateHelper,
102         displayBounds: Rect,
103         navBarHeight: Int,
104         bottom: Boolean
105     ) {
106         wmHelper
107             .StateSyncBuilder()
108             .add("letterboxAppRepositioned") {
109                 val letterboxAppWindow = getWindowRegion(wmHelper)
110                 val appRegionBounds = letterboxAppWindow.bounds
111                 val appHeight = appRegionBounds.height()
112                 return@add if (bottom)
113                     appRegionBounds.bottom == displayBounds.bottom &&
114                         appRegionBounds.top == (displayBounds.bottom - appHeight + navBarHeight)
115                 else
116                     appRegionBounds.top == displayBounds.top &&
117                         appRegionBounds.bottom == displayBounds.top + appHeight
118             }
119             .waitForAndVerify()
120     }
121 
getWindowRegionnull122     private fun getWindowRegion(wmHelper: WindowManagerStateHelper): Region {
123         val windowRegion = wmHelper.getWindowRegion(this)
124         require(!windowRegion.isEmpty) {
125             "Unable to find letterbox app window in the current state"
126         }
127         return windowRegion
128     }
129 
130     companion object {
131         private const val BOUNDS_OFFSET: Int = 100
132     }
133 }
134