• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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.companion.cts.uicommon
18 
19 import android.os.SystemClock
20 import android.os.SystemClock.sleep
21 import androidx.test.uiautomator.By
22 import androidx.test.uiautomator.BySelector
23 import androidx.test.uiautomator.Direction
24 import androidx.test.uiautomator.SearchCondition
25 import androidx.test.uiautomator.UiDevice
26 import androidx.test.uiautomator.UiObject2
27 import androidx.test.uiautomator.UiScrollable
28 import androidx.test.uiautomator.UiSelector
29 import androidx.test.uiautomator.Until
30 import kotlin.time.Duration
31 import kotlin.time.Duration.Companion.seconds
32 
33 open class CompanionDeviceManagerUi(private val ui: UiDevice) {
34     val isVisible: Boolean
35         get() = ui.hasObject(CONFIRMATION_UI)
36 
dismissnull37     fun dismiss() {
38         if (!isVisible) return
39         // Pressing back button should close (cancel) confirmation UI.
40         ui.pressBack()
41         waitUntilGone()
42     }
43 
waitUntilVisiblenull44     fun waitUntilVisible(timeout: Duration = 3.seconds) = ui.wait(
45         Until.hasObject(CONFIRMATION_UI), "CDM UI has not appeared.", timeout)
46 
47     fun waitUntilNotificationVisible(isAuto: Boolean = false) = ui.wait(
48         if (isAuto) Until.hasObject(NOTIFICATION_UI_AUTO) else Until.hasObject(NOTIFICATION_UI),
49         "NOTIFICATION UI has not appeared.")
50 
51     fun waitUntilGone() = ui.waitShort(Until.gone(CONFIRMATION_UI), "CDM UI has not disappeared")
52 
53     fun waitAndClickOnFirstFoundDevice() {
54         val firstDevice = ui.waitLongAndFind(
55             Until.findObject(
56                 DEVICE_LIST_WITH_ITEMS), "The item in the Device List not found or empty")
57             .children[0]
58 
59         val startTime = SystemClock.uptimeMillis()
60         var elapsedTime = 0L
61         // Keep trying to click the first item in the list until the device_list is disappeared
62         // or it times out after 5s.
63         while (ui.hasObject(DEVICE_LIST) && elapsedTime < 5.seconds.inWholeMilliseconds) {
64             firstDevice.click()
65             SystemClock.sleep(0.2.seconds.inWholeMilliseconds)
66             elapsedTime = SystemClock.uptimeMillis() - startTime
67         }
68     }
69 
waitUntilPositiveButtonIsEnabledAndClicknull70     fun waitUntilPositiveButtonIsEnabledAndClick() = ui.waitLongAndFind(
71         Until.findObject(POSITIVE_BUTTON), "Positive button not found or not clickable")
72             .click()
73 
74     fun waitUntilSystemDataTransferConfirmationVisible() = ui.wait(
75             Until.hasObject(SYSTEM_DATA_TRANSFER_CONFIRMATION_UI),
76             "System data transfer dialog has not appeared.")
77 
78     fun clickPositiveButton() = click(POSITIVE_BUTTON, "Positive button")
79 
80     fun clickNegativeButton() = click(NEGATIVE_BUTTON, "Negative button")
81 
82     fun clickNegativeButtonMultipleDevices() {
83         ui.wait(Until.findObject(CONFIRMATION_UI), 2.seconds.inWholeMilliseconds)?.let {
84             // swipe up (or scroll down) until cancel button is enabled
85             val startTime = SystemClock.uptimeMillis()
86             var elapsedTime = 0L
87             // UiDevice.hasObject() takes a long time for some reason so wait at least 10 seconds
88             while (!ui.hasObject(NEGATIVE_BUTTON_MULTIPLE_DEVICES)
89                     && elapsedTime < 10.seconds.inWholeMilliseconds) {
90                 it.swipe(Direction.UP, 1.0F)
91                 elapsedTime = SystemClock.uptimeMillis() - startTime
92             }
93         }
94         click(NEGATIVE_BUTTON_MULTIPLE_DEVICES, "Negative button for multiple devices")
95     }
96 
waitUntilAppAppearednull97     fun waitUntilAppAppeared() = ui.wait(Until.hasObject(ASSOCIATION_REVOKE_APP_UI),
98         "The test app has not appeared.")
99 
100     fun waitUntilPositiveButtonAppeared() = ui.waitLongAndFind(
101         Until.findObject(POSITIVE_BUTTON), "Positive button")
102 
103     fun scrollToBottom() {
104         if (SCROLLABLE_PERMISSION_LIST.waitForExists(2.seconds.inWholeMilliseconds)) {
105             SCROLLABLE_PERMISSION_LIST.scrollToEnd(MAX_SWIPE)
106             val positiveButton = waitUntilPositiveButtonAppeared()
107             val isEnabled = positiveButton.wait(
108                 Until.enabled(positiveButton.isEnabled), 5.seconds.inWholeMilliseconds)
109             if (!isEnabled) {
110                 error("Positive button is not enabled")
111             }
112         }
113     }
114 
isCdmDialogExistsnull115     fun isCdmDialogExists(): Boolean {
116         return ui.wait(Until.hasObject(CONFIRMATION_UI), 2.seconds.inWholeMilliseconds)
117     }
118 
clicknull119     protected fun click(selector: BySelector, description: String) = ui.waitShortAndFind(
120             Until.findObject(selector), "$description is not found")
121             .click()
122 
123     companion object {
124         private const val PACKAGE_NAME = "com.android.companiondevicemanager"
125         private const val NOTIFICATION_PACKAGE_NAME = "com.android.settings"
126         private const val NOTIFICATION_PACKAGE_NAME_AUTO = "com.android.car.settings"
127 
128         private const val MAX_SWIPE = 10
129 
130         private val CONFIRMATION_UI = By.pkg(PACKAGE_NAME)
131                 .res(PACKAGE_NAME, "activity_confirmation")
132         private val ASSOCIATION_REVOKE_APP_UI = By.pkg(ASSOCIATION_REVOKE_APP_NAME).depth(0)
133 
134         private val NOTIFICATION_UI = By.pkg(NOTIFICATION_PACKAGE_NAME).depth(0)
135 
136         private val NOTIFICATION_UI_AUTO = By.pkg(NOTIFICATION_PACKAGE_NAME_AUTO).depth(0)
137 
138         private val CLICKABLE_BUTTON =
139                 By.pkg(PACKAGE_NAME).clazz(".Button").clickable(true)
140         private val POSITIVE_BUTTON = By.copy(CLICKABLE_BUTTON).res(PACKAGE_NAME, "btn_positive")
141         private val NEGATIVE_BUTTON = By.copy(CLICKABLE_BUTTON).res(PACKAGE_NAME, "btn_negative")
142         private val NEGATIVE_BUTTON_MULTIPLE_DEVICES = By.pkg(PACKAGE_NAME)
143                 .res(PACKAGE_NAME, "negative_multiple_devices_layout")
144 
145         private val DEVICE_LIST = By.res(PACKAGE_NAME, "device_list")
146         private val DEVICE_LIST_ITEM = By.res(PACKAGE_NAME, "list_item_device")
147         private val DEVICE_LIST_WITH_ITEMS = By.copy(DEVICE_LIST)
148                 .hasDescendant(DEVICE_LIST_ITEM)
149 
150         private val SCROLLABLE_PERMISSION_LIST = UiScrollable(
151             UiSelector().resourceId("$PACKAGE_NAME:id/permission_list"))
152 
153         private val SYSTEM_DATA_TRANSFER_CONFIRMATION_UI = By.pkg(PACKAGE_NAME)
154                 .res(PACKAGE_NAME, "data_transfer_confirmation")
155     }
156 
waitnull157     protected fun UiDevice.wait(
158         condition: SearchCondition<Boolean>,
159         message: String,
160         timeout: Duration = 3.seconds
161     ) {
162         if (!wait(condition, timeout.inWholeMilliseconds)) error(message)
163     }
164 
waitShortnull165     protected fun UiDevice.waitShort(condition: SearchCondition<Boolean>, message: String) =
166             wait(condition, message, 1.seconds)
167 
168     protected fun UiDevice.waitAndFind(
169         condition: SearchCondition<UiObject2>,
170         message: String,
171         timeout: Duration = 3.seconds
172     ): UiObject2 =
173             wait(condition, timeout.inWholeMilliseconds) ?: error(message)
174 
175     protected fun UiDevice.waitShortAndFind(
176         condition: SearchCondition<UiObject2>,
177         message: String
178     ): UiObject2 = waitAndFind(condition, message, 1.seconds)
179 
180     protected fun UiDevice.waitLongAndFind(
181         condition: SearchCondition<UiObject2>,
182         message: String
183     ): UiObject2 = waitAndFind(condition, message, 10.seconds)
184 }
185