1 /*
2  * Copyright (C) 2022 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 package android.platform.systemui_tapl.ui
17 
18 import android.os.SystemClock
19 import android.platform.systemui_tapl.utils.DeviceUtils.sysuiResSelector
20 import android.platform.systemui_tapl.utils.SYSUI_PACKAGE
21 import android.platform.uiautomatorhelpers.DeviceHelpers.assertInvisible
22 import android.platform.uiautomatorhelpers.DeviceHelpers.assertVisibility
23 import android.platform.uiautomatorhelpers.DeviceHelpers.assertVisible
24 import android.platform.uiautomatorhelpers.DeviceHelpers.click
25 import android.platform.uiautomatorhelpers.DeviceHelpers.waitForObj
26 import android.platform.uiautomatorhelpers.WaitUtils.ensureThat
27 import android.platform.uiautomatorhelpers.scrollUntilFound
28 import androidx.test.uiautomator.By
29 import com.android.launcher3.tapl.LauncherInstrumentation
30 import com.android.systemui.shared.system.ActivityManagerWrapper
31 import java.time.Duration
32 import java.util.regex.Pattern
33 
34 /**
35  * System UI test automation object representing a media projection permission dialog for sharing
36  * the user's screen to another app.
37  *
38  * https://screenshot.googleplex.com/9YU3VqEh2uiTQ34 (single app) and
39  * https://screenshot.googleplex.com/AE5gqPJ3DE3jo7g (entire screen).
40  */
41 class MediaProjectionPermissionDialog internal constructor() {
42 
43     init {
44         assertSpinnerVisibility(visible = true)
45     }
46 
startSingleTaskSharenull47     fun startSingleTaskShare() {
48         waitForObj(SPINNER_SELECTOR).click()
49 
50         // Make sure clicking one the pop-up option, not the spinner with the same text
51         waitForObj(POP_UP).waitForObj(SINGLE_APP_SELECTOR) { "Single app option not found" }.click()
52         waitForObj(DIALOG).scrollUntilFound(SINGLE_APP_START_SELECTOR)
53             ?: error("Start button not found")
54 
55         // TODO(b/333510487): Sleep is temporarily needed after scroll, and should be remove later
56         SystemClock.sleep(SHORT_TIMEOUT.toMillis())
57         SINGLE_APP_START_SELECTOR.click()
58         ensureThat("App selector is launched") { isAppSelectorRunning() }
59     }
60 
isAppSelectorRunningnull61     private fun isAppSelectorRunning(): Boolean {
62         val tasks = ActivityManagerWrapper.getInstance().getRunningTasks(false)
63         return tasks.any { it.topActivity?.className?.contains(APP_SELECTOR_ACTIVITY) ?: false }
64     }
65 
cancelnull66     fun cancel() {
67         waitForObj(CANCEL_SELECTOR) { "Cancel button not found" }.click()
68     }
69 
70     /* Dismisses the dialog by the Back gesture */
dismissnull71     fun dismiss() {
72         LauncherInstrumentation().pressBack()
73         assertSpinnerVisibility(visible = false)
74     }
75 
assertSingleAppOptionIsDefaultnull76     fun assertSingleAppOptionIsDefault() {
77         // Single App option should be visible without clicking on the spinner
78         SINGLE_APP_SELECTOR.assertVisible()
79     }
80 
assertEntireScreenOptionIsDefaultnull81     fun assertEntireScreenOptionIsDefault() {
82         // Entire screen option should be visible without clicking on the spinner
83         ENTIRE_SCREEN_SELECTOR.assertVisible()
84     }
85 
assertSingleAppOptionDisablednull86     fun assertSingleAppOptionDisabled() {
87         waitForObj(SPINNER_SELECTOR).click()
88         SINGLE_APP_DISABLED_SELECTOR.assertVisible()
89         ensureThat("Single app is disabled") { !waitForObj(SINGLE_APP_SELECTOR).isEnabled }
90     }
91 
assertSingleAppOptionEnablednull92     fun assertSingleAppOptionEnabled() {
93         waitForObj(SPINNER_SELECTOR).click()
94         SINGLE_APP_DISABLED_SELECTOR.assertInvisible()
95         ensureThat("Single app is enabled") { waitForObj(SINGLE_APP_SELECTOR).isEnabled }
96     }
97 
assertSecondaryDisplayVisiblenull98     fun assertSecondaryDisplayVisible() {
99         waitForObj(SPINNER_SELECTOR).click()
100         ENTIRE_SCREEN_RECORD_SECONDARY_SELECTOR.assertVisible()
101     }
102 
103     companion object {
104         private val DIALOG = sysuiResSelector("screen_share_permission_dialog")
105 
106         // Builds from 24Q3 and earlier will have screen_share_mode_spinner, while builds from
107         // 24Q4 onwards will have screen_share_mode_options, so need to check both options here
108         private val SPINNER_SELECTOR =
109             By.res(Pattern.compile("$SYSUI_PACKAGE:id/screen_share_mode_(options|spinner)"))
110 
111         private val POP_UP = By.clazz("android.widget.ListView")
112         private val SINGLE_APP_SELECTOR = By.text("Share one app")
113         private val ENTIRE_SCREEN_SELECTOR = By.text("Share entire screen")
114         private val ENTIRE_SCREEN_RECORD_SECONDARY_SELECTOR =
115             By.text("Record entire screen: Overlay #1")
116         private val SINGLE_APP_START_SELECTOR = By.text("Next")
117         private val CANCEL_SELECTOR = By.text("Cancel")
118         private val SINGLE_APP_DISABLED_SELECTOR = By.textEndsWith("has disabled this option")
119         private const val APP_SELECTOR_ACTIVITY = "MediaProjectionAppSelectorActivity"
120         private val SHORT_TIMEOUT = Duration.ofMillis(500)
121 
122         // Public as it is used in Root.java
123         @JvmStatic
assertSpinnerVisibilitynull124         fun assertSpinnerVisibility(visible: Boolean) {
125             assertVisibility(SPINNER_SELECTOR, visible)
126         }
127     }
128 }
129