1 /* 2 * Copyright (C) 2024 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.platform.systemui_tapl.ui 18 19 import android.graphics.Rect 20 import android.platform.systemui_tapl.controller.SysUiFlagController 21 import android.platform.systemui_tapl.controller.SysUiFlagController.SystemUIFlag 22 import android.platform.systemui_tapl.utils.DeviceUtils.SHORT_WAIT 23 import android.platform.systemui_tapl.utils.DeviceUtils.androidResSelector 24 import android.platform.systemui_tapl.utils.DeviceUtils.sysuiResSelector 25 import android.platform.systemui_tapl.utils.UserUtils.runThenWaitUntilSwitchCompleted 26 import android.platform.test.scenario.tapl_common.TaplUiDevice 27 import android.platform.uiautomatorhelpers.DeviceHelpers.assertVisible 28 import android.platform.uiautomatorhelpers.DeviceHelpers.uiDevice 29 import android.platform.uiautomatorhelpers.DeviceHelpers.waitForObj 30 import androidx.test.uiautomator.By 31 import androidx.test.uiautomator.BySelector 32 import androidx.test.uiautomator.Until 33 import java.util.regex.Pattern.compile 34 35 /** 36 * Panel that shows up from the QS multiuser button or user switcher chip. 37 * 38 * Based on the [FULL_SCREEN_USER_SWITCHER] flag, this can be either: 39 * - full screen: http://go/hsv/5243841595572224 40 * - dialog: http://go/hsv/5008296081620992 41 */ 42 class UserSelectionPanel internal constructor() { 43 44 private val fullscreen: Boolean = SystemUIFlag.FULL_SCREEN_USER_SWITCHER.isEnabled 45 46 init { <lambda>null47 userSwitcherSelector().assertVisible { "UserSelectionPanel didn't appear" } 48 } 49 50 /** The number of active users on the current panel. Experimental. */ 51 val usersCount: Int 52 get() { 53 return uiDevice.wait(Until.findObjects(userSelector()), SHORT_WAIT.toMillis()).size 54 } 55 56 /** 57 * The visible bound of the user container. It's mainly for screenshot testing. Experimental. 58 */ 59 val userContainerRect: Rect? 60 get() { 61 if (fullscreen) { 62 return uiDevice 63 .waitForObj(sysuiResSelector(FULLSCREEN_USER_CONTAINER_ID)) 64 .waitForObj(sysuiResSelector("flow")) 65 .visibleBounds 66 } 67 throw NotImplementedError("userContainerRect isn't supported on the current device.") 68 } 69 70 /** Closes user selection. */ closenull71 fun close() { 72 TaplUiDevice.waitForObject(closeSelector(), "Close button").click() 73 } 74 75 /** Opens user settings by clicking the User Settings button. */ openUserSettingsnull76 fun openUserSettings(): MultipleUsersSettings { 77 if (fullscreen) { 78 clickButtonFromAddMenu(MANAGE_USERS_BUTTON_LABEL) 79 } else { 80 // http://go/hsv/5905004487507968?node=28 81 val userSettingsButton = androidResSelector("button3").text("Manage users") 82 TaplUiDevice.waitForObject(userSettingsButton, "User settings button").click() 83 } 84 return MultipleUsersSettings() 85 } 86 87 /** 88 * Unless guest user is initialized in some other way, on every UserSelection type we have 89 * creating always means selecting guest user. 90 */ createAndSwitchToGuestUsernull91 fun createAndSwitchToGuestUser() { 92 runThenWaitUntilSwitchCompleted { 93 if (fullscreen) { 94 clickButtonFromAddMenu(ADD_GUEST_BUTTON_LABEL) 95 } else { 96 selectUser(GUEST_NAME) 97 } 98 } 99 } 100 101 /** Opens add user prompt. */ openAddUserPromptnull102 fun openAddUserPrompt(): AddUserPrompt { 103 if (fullscreen) { 104 clickButtonFromAddMenu(ADD_USER_BUTTON_LABEL) 105 } else { 106 selectUser(ADD_USER_BUTTON_LABEL) 107 } 108 return AddUserPrompt() 109 } 110 111 /** Selects switching back to an already added guest user. */ switchBackToGuestnull112 fun switchBackToGuest(): WelcomeBackGuestDialog { 113 runThenWaitUntilSwitchCompleted { selectUser(GUEST_NAME) } 114 return WelcomeBackGuestDialog() 115 } 116 117 /** Selects switching to an existing user or a new guest user. */ switchToUsernull118 fun switchToUser(userName: String) { 119 runThenWaitUntilSwitchCompleted { selectUser(userName) } 120 } 121 122 /** 123 * Selects an entry from the "Add" menu, available only in fullscreen. 124 * 125 * https://hsv.googleplex.com/5243841595572224?node=10 126 */ clickButtonFromAddMenunull127 private fun clickButtonFromAddMenu(label: String) { 128 fun clickButtonWithText(label: String) { 129 // TODO(b/260815442): The clickable attribute of these items in the menu list is false. 130 waitForObj(By.text(label)).click() 131 } 132 clickButtonWithText("Add") 133 clickButtonWithText(label) 134 } 135 136 /** Selects the user with [userName]. */ selectUsernull137 private fun selectUser(userName: String) { 138 val userItemSelector: BySelector = 139 if (fullscreen) By.clazz("android.view.ViewGroup") else sysuiResSelector("user_item") 140 141 waitForObj(userItemSelector.hasDescendant(userSelector().text(userName))).click() 142 } 143 144 /** Selects exiting the guest user. */ exitGuestnull145 fun exitGuest(): ExitGuestPrompt { 146 selectUser(EXIT_GUEST_ITEM) 147 return ExitGuestPrompt() 148 } 149 userSwitcherSelectornull150 private fun userSwitcherSelector(): BySelector = 151 if (fullscreen) { 152 sysuiResSelector("user_switcher_root") 153 } else { 154 androidResSelector("alertTitle").text("Select user") 155 } 156 closeSelectornull157 private fun closeSelector(): BySelector = 158 if (fullscreen) { 159 // http://go/hsv/5243841595572224?node=9 160 sysuiResSelector(FULLSCREEN_CANCEL_ID) 161 } else { 162 // http://go/hsv/5905004487507968?node=29 163 androidResSelector("button1").text(compile("Close|Done")) 164 } 165 166 // http://go/hsv/5008296081620992?node=10# userSelectornull167 private fun userSelector(): BySelector = 168 sysuiResSelector(if (fullscreen) "user_switcher_text" else "user_name") 169 170 companion object { 171 private const val GUEST_NAME = "Guest" 172 private const val MANAGE_USERS_BUTTON_LABEL = "Manage users" 173 private const val ADD_GUEST_BUTTON_LABEL = "Add guest" 174 private const val ADD_USER_BUTTON_LABEL = "Add user" 175 private const val FULLSCREEN_CANCEL_ID = "cancel" 176 private const val EXIT_GUEST_ITEM = "Exit guest" 177 private const val FULLSCREEN_USER_CONTAINER_ID = "user_switcher_grid_container" 178 } 179 180 private val SystemUIFlag.isEnabled: Boolean 181 get() = SysUiFlagController.get().isEnabled(this) 182 } 183