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 com.android.permissioncontroller.permission.ui.television
18
19 import androidx.test.uiautomator.By
20 import androidx.test.uiautomator.StaleObjectException
21 import androidx.test.uiautomator.UiDevice
22 import androidx.test.uiautomator.UiObject2
23 import androidx.test.uiautomator.Until
24
25 private val SELECTOR_FOCUSED = By.focused(true)
26 private val SELECTOR_RES_ID_PC_DECOR_TITLE =
27 By.res("com.android.permissioncontroller", "decor_title")
28 private val SELECTOR_RES_ID_ANDROID_TITLE = By.res("android", "title")
29
30 private const val WAIT_DELAY = 3_000L
31 private const val RETRIES = 5
32
33 val UiDevice.fragmentDecorTitle: String?
34 get() = wait(Until.findObject(SELECTOR_RES_ID_PC_DECOR_TITLE), WAIT_DELAY)?.text
35
36 val UiDevice.focusedElement: UiObject2
37 get() = wait(Until.findObject(SELECTOR_FOCUSED), WAIT_DELAY)
38 ?: error("Focused item is not found")
39
40 private val UiObject2.titleElement: UiObject2
41 get() = wait(Until.findObject(SELECTOR_RES_ID_ANDROID_TITLE), WAIT_DELAY)
42 ?: error("Could not retrieve title")
43
44 val UiDevice.focusedElementTitle: String?
45 get() {
<lambda>null46 repeat(RETRIES) {
47 try { return focusedElement.titleElement.text } catch (e: StaleObjectException) {}
48 }
49 error("Could not get title text")
50 }
51
<lambda>null52 fun UiDevice.navigateDown() = navigate { pressDPadDown() }
53
<lambda>null54 fun UiDevice.navigateUp() = navigate { pressDPadUp() }
55
56 @Suppress("ControlFlowWithEmptyBody")
UiDevicenull57 fun UiDevice.navigateToTheBottom() {
58 while (navigateDown()) {}
59 }
60
61 @Suppress("ControlFlowWithEmptyBody")
navigateToTheTopnull62 fun UiDevice.navigateToTheTop() {
63 while (navigateUp()) {}
64 }
65
UiDevicenull66 fun UiDevice.focusOnElementWithTitle(title: CharSequence): Boolean =
67 checkAllItemsIfNeeded { focusedElementTitle == title }
68
hasElementWithTitlenull69 fun UiDevice.hasElementWithTitle(title: CharSequence): Boolean =
70 checkAllItemsIfNeeded {
71 hasObject(By.copy(SELECTOR_RES_ID_ANDROID_TITLE).text(title.toString()))
72 }
73
UiDevicenull74 private fun UiDevice.checkAllItemsIfNeeded(predicate: () -> Boolean): Boolean {
75 // Let's do one quick check first, right where we are. If it does not work - we'll do the walk.
76 if (predicate()) return true
77
78 // That didn't work, so we'll go over all the items in the list (if needed) top to bottom, but
79 // let's make sure we start from the very top.
80 navigateToTheTop()
81
82 do {
83 if (predicate()) return true
84 } while (navigateDown())
85
86 return false
87 }
88
navigatenull89 private fun UiDevice.navigate(action: () -> Unit): Boolean {
90 val prevFocusedTitle = focusedElementTitle
91
92 action()
93 waitForIdle()
94
95 return prevFocusedTitle != focusedElementTitle
96 }