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 com.android.systemui.qs.tiles.impl.rotation.ui.mapper
18 
19 import android.graphics.drawable.TestStubDrawable
20 import android.widget.Switch
21 import androidx.test.ext.junit.runners.AndroidJUnit4
22 import androidx.test.filters.SmallTest
23 import com.android.systemui.SysuiTestCase
24 import com.android.systemui.common.shared.model.Icon
25 import com.android.systemui.defaultDeviceState
26 import com.android.systemui.deviceStateManager
27 import com.android.systemui.foldedDeviceStateList
28 import com.android.systemui.kosmos.Kosmos
29 import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
30 import com.android.systemui.qs.tiles.impl.rotation.domain.model.RotationLockTileModel
31 import com.android.systemui.qs.tiles.impl.rotation.qsRotationLockTileConfig
32 import com.android.systemui.qs.tiles.viewmodel.QSTileState
33 import com.android.systemui.res.R
34 import com.android.systemui.statusbar.policy.DevicePostureController
35 import com.android.systemui.statusbar.policy.devicePostureController
36 import com.google.common.truth.Truth.assertThat
37 import org.junit.Before
38 import org.junit.Test
39 import org.junit.runner.RunWith
40 import org.mockito.kotlin.whenever
41 
42 @SmallTest
43 @RunWith(AndroidJUnit4::class)
44 class RotationLockTileMapperTest : SysuiTestCase() {
45     private val kosmos = Kosmos()
46     private val rotationLockTileConfig = kosmos.qsRotationLockTileConfig
47     private val devicePostureController = kosmos.devicePostureController
48     private val deviceStateManager = kosmos.deviceStateManager
49 
50     private lateinit var mapper: RotationLockTileMapper
51 
52     @Before
setupnull53     fun setup() {
54         deviceStateManager
55         whenever(devicePostureController.devicePosture)
56             .thenReturn(DevicePostureController.DEVICE_POSTURE_CLOSED)
57         whenever(deviceStateManager.supportedDeviceStates)
58             .thenReturn(listOf(kosmos.defaultDeviceState))
59 
60         mapper =
61             RotationLockTileMapper(
62                 context.orCreateTestableResources
63                     .apply {
64                         addOverride(R.drawable.qs_auto_rotate_icon_off, TestStubDrawable())
65                         addOverride(R.drawable.qs_auto_rotate_icon_on, TestStubDrawable())
66                         addOverride(com.android.internal.R.bool.config_allowRotationResolver, true)
67                         addOverride(
68                             com.android.internal.R.array.config_foldedDeviceStates,
69                             intArrayOf(), // empty array <=> device is not foldable
70                         )
71                     }
72                     .resources,
73                 context.theme,
74                 devicePostureController,
75                 deviceStateManager,
76             )
77     }
78 
79     @Test
rotationNotLocked_cameraRotationDisablednull80     fun rotationNotLocked_cameraRotationDisabled() {
81         val inputModel = RotationLockTileModel(false, false)
82 
83         val outputState = mapper.map(rotationLockTileConfig, inputModel)
84 
85         val expectedState =
86             createRotationLockTileState(
87                 QSTileState.ActivationState.ACTIVE,
88                 EMPTY_SECONDARY_STRING,
89                 R.drawable.qs_auto_rotate_icon_on,
90             )
91         QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
92     }
93 
94     @Test
rotationNotLocked_cameraRotationEnablednull95     fun rotationNotLocked_cameraRotationEnabled() {
96         val inputModel = RotationLockTileModel(false, true)
97 
98         val outputState = mapper.map(rotationLockTileConfig, inputModel)
99 
100         val expectedState =
101             createRotationLockTileState(
102                 QSTileState.ActivationState.ACTIVE,
103                 context.getString(R.string.rotation_lock_camera_rotation_on),
104                 R.drawable.qs_auto_rotate_icon_on,
105             )
106         QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
107     }
108 
109     @Test
rotationLocked_cameraRotationNotEnablednull110     fun rotationLocked_cameraRotationNotEnabled() {
111         val inputModel = RotationLockTileModel(true, false)
112 
113         val outputState = mapper.map(rotationLockTileConfig, inputModel)
114 
115         val expectedState =
116             createRotationLockTileState(
117                 QSTileState.ActivationState.INACTIVE,
118                 EMPTY_SECONDARY_STRING,
119                 R.drawable.qs_auto_rotate_icon_off,
120             )
121         QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
122     }
123 
124     @Test
deviceFoldableAndClosed_secondaryLabelIsFoldableSpecificnull125     fun deviceFoldableAndClosed_secondaryLabelIsFoldableSpecific() {
126         setDeviceFoldable()
127         val inputModel = RotationLockTileModel(false, false)
128 
129         val outputState = mapper.map(rotationLockTileConfig, inputModel)
130 
131         val expectedSecondaryLabelEnding =
132             context.getString(R.string.quick_settings_rotation_posture_folded)
133         assertThat(
134                 context.resources.getIntArray(
135                     com.android.internal.R.array.config_foldedDeviceStates
136                 )
137             )
138             .isNotEmpty()
139         val actualSecondaryLabel = outputState.secondaryLabel
140         assertThat(actualSecondaryLabel).isNotNull()
141         assertThat(actualSecondaryLabel!!.endsWith(expectedSecondaryLabelEnding)).isTrue()
142     }
143 
144     @Test
deviceFoldableAndNotClosed_secondaryLabelIsFoldableSpecificnull145     fun deviceFoldableAndNotClosed_secondaryLabelIsFoldableSpecific() {
146         setDeviceFoldable()
147         whenever(devicePostureController.devicePosture)
148             .thenReturn(DevicePostureController.DEVICE_POSTURE_OPENED)
149         val inputModel = RotationLockTileModel(false, false)
150 
151         val outputState = mapper.map(rotationLockTileConfig, inputModel)
152 
153         val expectedSecondaryLabelEnding =
154             context.getString(R.string.quick_settings_rotation_posture_unfolded)
155         assertThat(
156                 context.orCreateTestableResources.resources.getIntArray(
157                     com.android.internal.R.array.config_foldedDeviceStates
158                 )
159             )
160             .isNotEmpty()
161         val actualSecondaryLabel = outputState.secondaryLabel
162         assertThat(actualSecondaryLabel).isNotNull()
163         assertThat(actualSecondaryLabel!!.endsWith(expectedSecondaryLabelEnding)).isTrue()
164     }
165 
setDeviceFoldablenull166     private fun setDeviceFoldable() {
167         mapper.apply {
168             overrideResource(
169                 com.android.internal.R.array.config_foldedDeviceStates,
170                 intArrayOf(1, 2, 3),
171             )
172         }
173         whenever(deviceStateManager.supportedDeviceStates).thenReturn(kosmos.foldedDeviceStateList)
174     }
175 
createRotationLockTileStatenull176     private fun createRotationLockTileState(
177         activationState: QSTileState.ActivationState,
178         secondaryLabel: String,
179         iconRes: Int,
180     ): QSTileState {
181         val label = context.getString(R.string.quick_settings_rotation_unlocked_label)
182         return QSTileState(
183             Icon.Loaded(context.getDrawable(iconRes)!!, null),
184             iconRes,
185             label,
186             activationState,
187             secondaryLabel,
188             setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK),
189             context.getString(R.string.accessibility_quick_settings_rotation),
190             secondaryLabel,
191             QSTileState.SideViewIcon.None,
192             QSTileState.EnabledState.ENABLED,
193             Switch::class.qualifiedName,
194         )
195     }
196 
197     private companion object {
198         private const val EMPTY_SECONDARY_STRING = ""
199     }
200 }
201