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.permissionui.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() =
38         wait(Until.findObject(SELECTOR_FOCUSED), WAIT_DELAY) ?: error("Focused item is not found")
39 
40 private val UiObject2.titleElement: UiObject2
41     get() =
42         wait(Until.findObject(SELECTOR_RES_ID_ANDROID_TITLE), WAIT_DELAY)
43             ?: error("Could not retrieve title")
44 
45 val UiDevice.focusedElementTitle: String?
46     get() {
<lambda>null47         repeat(RETRIES) {
48             try {
49                 return focusedElement.titleElement.text
50             } catch (e: StaleObjectException) {}
51         }
52         error("Could not get title text")
53     }
54 
<lambda>null55 fun UiDevice.navigateDown() = navigate { pressDPadDown() }
56 
<lambda>null57 fun UiDevice.navigateUp() = navigate { pressDPadUp() }
58 
59 @Suppress("ControlFlowWithEmptyBody")
UiDevicenull60 fun UiDevice.navigateToTheBottom() {
61     while (navigateDown()) {}
62 }
63 
64 @Suppress("ControlFlowWithEmptyBody")
navigateToTheTopnull65 fun UiDevice.navigateToTheTop() {
66     while (navigateUp()) {}
67 }
68 
<lambda>null69 fun UiDevice.focusOnElementWithTitle(title: CharSequence): Boolean = checkAllItemsIfNeeded {
70     focusedElementTitle == title
71 }
72 
<lambda>null73 fun UiDevice.hasElementWithTitle(title: CharSequence): Boolean = checkAllItemsIfNeeded {
74     hasObject(By.copy(SELECTOR_RES_ID_ANDROID_TITLE).text(title.toString()))
75 }
76 
UiDevicenull77 private fun UiDevice.checkAllItemsIfNeeded(predicate: () -> Boolean): Boolean {
78     // Let's do one quick check first, right where we are. If it does not work - we'll do the walk.
79     if (predicate()) return true
80 
81     // That didn't work, so we'll go over all the items in the list (if needed) top to bottom, but
82     // let's make sure we start from the very top.
83     navigateToTheTop()
84 
85     do {
86         if (predicate()) return true
87     } while (navigateDown())
88 
89     return false
90 }
91 
navigatenull92 private fun UiDevice.navigate(action: () -> Unit): Boolean {
93     val prevFocusedTitle = focusedElementTitle
94 
95     action()
96     waitForIdle()
97 
98     return prevFocusedTitle != focusedElementTitle
99 }
100