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.graphics.Point
19 import android.graphics.PointF
20 import android.platform.systemui_tapl.utils.DeviceUtils.sysuiResSelector
21 import android.platform.systemui_tapl.volume.panel.ui.VolumePanel
22 import android.platform.uiautomatorhelpers.BetterSwipe
23 import android.platform.uiautomatorhelpers.DeviceHelpers.assertVisible
24 import android.platform.uiautomatorhelpers.DeviceHelpers.click
25 import android.platform.uiautomatorhelpers.DeviceHelpers.uiDevice
26 import android.platform.uiautomatorhelpers.DeviceHelpers.waitForObj
27 import android.platform.uiautomatorhelpers.PRECISE_GESTURE_INTERPOLATOR
28 import com.google.common.truth.Truth
29 
30 /** System UI test automation object representing the dialog for adjusting the device volume. */
31 class VolumeDialog internal constructor() {
32     init {
33         assertVolumeDialogVisible()
34     }
35 
36     /**
37      * Changes the volume by dragging the volume slider.
38      *
39      * Note: Volume value cannot be more than 25 and less than 0.
40      *
41      * @param volume new volume value
42      */
setVolumeByDraggingnull43     fun setVolumeByDragging(volume: Int) {
44         assertVolumeDialogVisible()
45         dragAndChangeVolume(volume)
46         assertVolumeDialogVisible()
47     }
48 
49     /** Open the ringer drawer by clicking the ringer mode icon on the volume dialog. */
openRingerDrawernull50     fun openRingerDrawer(): VolumeRingerDrawer {
51         val ringerIconSel = sysuiResSelector("volume_new_ringer_active_icon_container")
52         waitForObj(ringerIconSel).click()
53         return VolumeRingerDrawer()
54     }
55 
56     /** Open the volume setting panel by clicking the setting icon on the volume dialog. */
57     @Deprecated(
58         "This new volume panel is rolled out. Use openNewVolumePanel instead",
59         replaceWith = ReplaceWith("openNewVolumePanel"),
60     )
openVolumePanelnull61     fun openVolumePanel(): VolumePanelDialog {
62         sysuiResSelector("settings").click()
63         return VolumePanelDialog()
64     }
65 
66     /** Open the volume setting panel by clicking the setting icon on the volume dialog. */
openNewVolumePanelnull67     fun openNewVolumePanel(): VolumePanel {
68         waitForObj(sysuiResSelector("settings")).click()
69         return VolumePanel()
70     }
71 
72     /**
73      * Click the live caption button on the volume dialog.
74      *
75      * https://hsv.googleplex.com/4767031439130624
76      *
77      * @return this
78      */
toggleLiveCaptionsnull79     fun toggleLiveCaptions(): VolumeDialog {
80         waitForObj(sysuiResSelector("odi_captions_icon")).click()
81         return this
82     }
83 
84     companion object {
85         private val SLIDER = sysuiResSelector("volume_row_slider")
86         val PAGE_TITLE_SELECTOR = sysuiResSelector("volume_dialog")
87         private const val MAX_VOLUME = 26
88         private const val MIN_VOLUME = -1
89 
90         /**
91          * Method used for dragging and changing the volume. Note: Volume value cannot be more than
92          * 25 and less than 0.
93          *
94          * @param volume value for volume to changed
95          */
dragAndChangeVolumenull96         private fun dragAndChangeVolume(volume: Int) {
97             val coordinates = getDragCoordinates(volume)
98             assertVolumeDialogVisible()
99             BetterSwipe.from(waitForObj(SLIDER).visibleCenter)
100                 .to(PointF(coordinates), interpolator = PRECISE_GESTURE_INTERPOLATOR)
101                 .release()
102         }
103 
104         /** Asserts that the volume dialog is visible. */
assertVolumeDialogVisiblenull105         private fun assertVolumeDialogVisible() {
106             PAGE_TITLE_SELECTOR.assertVisible()
107         }
108 
109         /**
110          * This will get the co-ordinate of the for volume slider based on the volume value
111          * provided.
112          *
113          * Note: Volume value cannot be more than 25 and less than 0.
114          *
115          * Formula's used: Suppose volume slider length is 100 and volume provided is 15, therefore
116          * slider should move to:
117          *
118          * FORMULA: (Volume Provided / Max Volume) * Slider Length Hence in current scenario its:
119          * (15/25)*100
120          *
121          * Since the Android device coordinate works from top to down
122          * [https://screenshot.googleplex .com/Zm3s1rqJ2Es], therefore formula used for coordinate
123          * calculation is:
124          *
125          * X-Coordinate: TOP + ((MAX VOLUME - GIVEN VOLUME)/MAX VOLUME ) * (SLIDER BOTTOM Y
126          * COORDINATE - SLIDER TOP Y COORDINATE)
127          *
128          * Y-Coordinate: SLIDER LEFT X COORDINATE + (SLIDER RIGHT X COORDINATE - SLIDER LEFT X
129          * COORDINATE) / 2
130          *
131          * @param volume value for volume to changed
132          * @return an of Point which is the coordinate of the slider to be moved too.
133          */
getDragCoordinatesnull134         private fun getDragCoordinates(volume: Int): Point {
135             Truth.assertThat(volume).isLessThan(MAX_VOLUME)
136             Truth.assertThat(volume).isGreaterThan(MIN_VOLUME)
137             val dimension = uiDevice.waitForObj(SLIDER).visibleBounds
138             val top = dimension.top
139             val left = dimension.left
140             val right = dimension.right
141             val bottom = dimension.bottom
142             val y = (top + (25 - volume).toFloat() / 25 * (bottom - top)).toInt()
143             val x = left + (right - left) / 2
144             return Point(x, y)
145         }
146     }
147 }
148