1 /*
<lambda>null2  * 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.PointF
20 import android.platform.helpers.CommonUtils
21 import android.platform.systemui_tapl.ui.Bubble.Companion.bubbleViews
22 import android.platform.systemui_tapl.utils.DeviceUtils.launcherResSelector
23 import android.platform.systemui_tapl.utils.DeviceUtils.sysuiResSelector
24 import android.platform.uiautomatorhelpers.BetterSwipe
25 import android.platform.uiautomatorhelpers.DeviceHelpers.assertVisible
26 import android.platform.uiautomatorhelpers.DeviceHelpers.context
27 import android.platform.uiautomatorhelpers.DeviceHelpers.hasObject
28 import android.platform.uiautomatorhelpers.DeviceHelpers.uiDevice
29 import android.platform.uiautomatorhelpers.DeviceHelpers.waitForNullableObj
30 import android.platform.uiautomatorhelpers.DeviceHelpers.waitForObj
31 import android.platform.uiautomatorhelpers.FLING_GESTURE_INTERPOLATOR
32 import android.view.WindowInsets
33 import android.view.WindowManager
34 import android.view.WindowMetrics
35 import androidx.test.uiautomator.UiObject2
36 import com.android.launcher3.tapl.LauncherInstrumentation
37 import com.google.common.truth.Truth.assertWithMessage
38 import java.time.Duration
39 import java.time.temporal.ChronoUnit.MILLIS
40 
41 /** System UI test automation object representing the expanded bubble stack. */
42 class ExpandedBubbleStack internal constructor() {
43     init {
44         BUBBLE_EXPANDED_VIEW.assertVisible(timeout = FIND_OBJECT_TIMEOUT) {
45             "Bubbles expanded view should be visible"
46         }
47     }
48 
49     /** Returns all bubbles. */
50     val bubbles: List<Bubble>
51         get() = bubbleViews.map { bubbleView: UiObject2 -> Bubble(bubbleView) }
52 
53     /** Clicks the overflow button and returns the overflow panel that appears. */
54     fun openOverflow(): BubbleOverflow {
55         bubbleOverflow.click()
56         return BubbleOverflow()
57     }
58 
59     /** Closes the stack by swiping up. */
60     fun closeBySwiping() {
61         val windowBounds = windowMetrics.bounds
62         val x = windowBounds.width() / 2f
63         // Move up by 1 pixel to ensure tap begins on screen.
64         val startY = windowBounds.bottom - 1f
65         // From bottom middle of screen
66         val from = PointF(x, startY)
67         // To middle of screen
68         val to = PointF(x, startY / 2)
69         // Use a custom duration for bubble swipe to reduce flakiness on slow device.
70         BetterSwipe.from(from)
71             .to(to, duration = Duration.of(700, MILLIS), interpolator = FLING_GESTURE_INTERPOLATOR)
72             .release()
73         Root.get().verifyNoExpandedBubbleStackIsVisible()
74     }
75 
76     /** Closes the stack by the "back" gesture. */
77     fun closeByBackGesture() {
78         LauncherInstrumentation().pressBack()
79         Root.get().verifyNoExpandedBubbleStackIsVisible()
80     }
81 
82     /** Closes the stack by clicking outside. */
83     fun closeByClickingOutside() {
84         val gestureInsets =
85             windowMetrics.windowInsets.getInsetsIgnoringVisibility(
86                 WindowInsets.Type.mandatorySystemGestures() or WindowInsets.Type.displayCutout()
87             )
88         val clickX = gestureInsets.left
89         val clickY = gestureInsets.top
90         uiDevice.click(clickX, clickY)
91         Root.get().verifyNoExpandedBubbleStackIsVisible()
92     }
93 
94     /** Dismiss Manage education to proceed with expanded bubbles */
95     fun dismissManageEducation() {
96         if (hasObject(BUBBLE_MANAGE_EDUCATION)) {
97             waitForObj(BUBBLE_GOT_IT_BUTTON).click()
98             uiDevice.waitForIdle()
99         }
100     }
101 
102     companion object {
103         val FIND_OBJECT_TIMEOUT = Duration.ofSeconds(20)
104         val BUBBLE_EXPANDED_VIEW = sysuiResSelector("bubble_expanded_view")
105         private val BUBBLE_OVERFLOW_BUTTON = sysuiResSelector("bubble_overflow_button")
106         private val BUBBLE_BAR_OVERFLOW = launcherResSelector("bubble_overflow_button")
107         private val BUBBLE_MANAGE_EDUCATION = sysuiResSelector("manage_education_view")
108         private val BUBBLE_GOT_IT_BUTTON = sysuiResSelector("got_it")
109 
110         private val windowMetrics: WindowMetrics
111             get() = context.getSystemService(WindowManager::class.java)!!.currentWindowMetrics
112 
113         @JvmStatic
114         internal val bubbleOverflow: UiObject2
115             get() {
116                 val bubbleOverflow =
117                     if (CommonUtils.isLargeScreen()) {
118                         // Check bubble bar first if we're large screen
119                         waitForNullableObj(BUBBLE_BAR_OVERFLOW, timeout = FIND_OBJECT_TIMEOUT)
120                             ?:
121                             // Check floating in case bubble bar wasn't active
122                             waitForNullableObj(
123                                 BUBBLE_OVERFLOW_BUTTON,
124                                 timeout = FIND_OBJECT_TIMEOUT,
125                             )
126                     } else {
127                         waitForNullableObj(BUBBLE_OVERFLOW_BUTTON, timeout = FIND_OBJECT_TIMEOUT)
128                     }
129                 assertWithMessage("Bubble overflow not visible").that(bubbleOverflow).isNotNull()
130                 return bubbleOverflow!!
131             }
132     }
133 }
134