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.night.ui 18 19 import android.graphics.drawable.TestStubDrawable 20 import android.service.quicksettings.Tile 21 import android.text.TextUtils 22 import android.widget.Switch 23 import androidx.test.ext.junit.runners.AndroidJUnit4 24 import androidx.test.filters.SmallTest 25 import com.android.systemui.SysuiTestCase 26 import com.android.systemui.common.shared.model.Icon 27 import com.android.systemui.kosmos.Kosmos 28 import com.android.systemui.qs.tiles.base.logging.QSTileLogger 29 import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject 30 import com.android.systemui.qs.tiles.impl.night.domain.model.NightDisplayTileModel 31 import com.android.systemui.qs.tiles.impl.night.qsNightDisplayTileConfig 32 import com.android.systemui.qs.tiles.viewmodel.QSTileState 33 import com.android.systemui.res.R 34 import com.android.systemui.util.mockito.mock 35 import java.time.LocalTime 36 import java.time.format.DateTimeFormatter 37 import org.junit.Before 38 import org.junit.Test 39 import org.junit.runner.RunWith 40 41 @SmallTest 42 @RunWith(AndroidJUnit4::class) 43 class NightDisplayTileMapperTest : SysuiTestCase() { 44 private val kosmos = Kosmos() 45 private val config = kosmos.qsNightDisplayTileConfig 46 47 private val testStartTime = LocalTime.MIDNIGHT 48 private val testEndTime = LocalTime.NOON 49 50 private lateinit var mapper: NightDisplayTileMapper 51 52 @Before setupnull53 fun setup() { 54 mapper = 55 NightDisplayTileMapper( 56 context.orCreateTestableResources 57 .apply { 58 addOverride(R.drawable.qs_nightlight_icon_on, TestStubDrawable()) 59 addOverride(R.drawable.qs_nightlight_icon_off, TestStubDrawable()) 60 } 61 .resources, 62 context.theme, 63 mock<QSTileLogger>(), 64 ) 65 } 66 67 @Test disabledModel_whenAutoModeOffnull68 fun disabledModel_whenAutoModeOff() { 69 val inputModel = NightDisplayTileModel.AutoModeOff(false, false) 70 71 val outputState = mapper.map(config, inputModel) 72 73 val expectedState = 74 createNightDisplayTileState( 75 QSTileState.ActivationState.INACTIVE, 76 context.resources.getStringArray(R.array.tile_states_night)[Tile.STATE_INACTIVE], 77 ) 78 QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState) 79 } 80 81 /** Force enable does not change the mode by itself. */ 82 @Test disabledModel_whenAutoModeOff_whenForceEnablenull83 fun disabledModel_whenAutoModeOff_whenForceEnable() { 84 val inputModel = NightDisplayTileModel.AutoModeOff(false, true) 85 86 val outputState = mapper.map(config, inputModel) 87 88 val expectedState = 89 createNightDisplayTileState( 90 QSTileState.ActivationState.INACTIVE, 91 context.resources.getStringArray(R.array.tile_states_night)[Tile.STATE_INACTIVE], 92 ) 93 QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState) 94 } 95 96 @Test enabledModel_whenAutoModeOffnull97 fun enabledModel_whenAutoModeOff() { 98 val inputModel = NightDisplayTileModel.AutoModeOff(true, false) 99 100 val outputState = mapper.map(config, inputModel) 101 102 val expectedState = 103 createNightDisplayTileState( 104 QSTileState.ActivationState.ACTIVE, 105 context.resources.getStringArray(R.array.tile_states_night)[Tile.STATE_ACTIVE], 106 ) 107 QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState) 108 } 109 110 @Test enabledModel_forceAutoMode_whenAutoModeOffnull111 fun enabledModel_forceAutoMode_whenAutoModeOff() { 112 val inputModel = NightDisplayTileModel.AutoModeOff(true, true) 113 114 val outputState = mapper.map(config, inputModel) 115 116 val expectedState = 117 createNightDisplayTileState( 118 QSTileState.ActivationState.ACTIVE, 119 context.resources.getStringArray(R.array.tile_states_night)[Tile.STATE_ACTIVE], 120 ) 121 QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState) 122 } 123 124 @Test enabledModel_autoModeTwilight_locationOffnull125 fun enabledModel_autoModeTwilight_locationOff() { 126 val inputModel = NightDisplayTileModel.AutoModeTwilight(true, false, false) 127 128 val outputState = mapper.map(config, inputModel) 129 130 val expectedState = createNightDisplayTileState(QSTileState.ActivationState.ACTIVE, null) 131 QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState) 132 } 133 134 @Test enabledModel_autoModeTwilight_locationOnnull135 fun enabledModel_autoModeTwilight_locationOn() { 136 val inputModel = NightDisplayTileModel.AutoModeTwilight(true, false, true) 137 138 val outputState = mapper.map(config, inputModel) 139 140 val expectedState = 141 createNightDisplayTileState( 142 QSTileState.ActivationState.ACTIVE, 143 context.getString(R.string.quick_settings_night_secondary_label_until_sunrise), 144 ) 145 QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState) 146 } 147 148 @Test disabledModel_autoModeTwilight_locationOnnull149 fun disabledModel_autoModeTwilight_locationOn() { 150 val inputModel = NightDisplayTileModel.AutoModeTwilight(false, false, true) 151 152 val outputState = mapper.map(config, inputModel) 153 154 val expectedState = 155 createNightDisplayTileState( 156 QSTileState.ActivationState.INACTIVE, 157 context.getString(R.string.quick_settings_night_secondary_label_on_at_sunset), 158 ) 159 QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState) 160 } 161 162 @Test disabledModel_autoModeTwilight_locationOffnull163 fun disabledModel_autoModeTwilight_locationOff() { 164 val inputModel = NightDisplayTileModel.AutoModeTwilight(false, false, false) 165 166 val outputState = mapper.map(config, inputModel) 167 168 val expectedState = createNightDisplayTileState(QSTileState.ActivationState.INACTIVE, null) 169 QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState) 170 } 171 172 @Test disabledModel_autoModeCustom_24Hournull173 fun disabledModel_autoModeCustom_24Hour() { 174 val inputModel = 175 NightDisplayTileModel.AutoModeCustom(false, false, testStartTime, null, true) 176 177 val outputState = mapper.map(config, inputModel) 178 179 val expectedState = 180 createNightDisplayTileState( 181 QSTileState.ActivationState.INACTIVE, 182 context.getString( 183 R.string.quick_settings_night_secondary_label_on_at, 184 formatter24Hour.format(testStartTime), 185 ), 186 ) 187 QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState) 188 } 189 190 @Test disabledModel_autoModeCustom_12Hournull191 fun disabledModel_autoModeCustom_12Hour() { 192 val inputModel = 193 NightDisplayTileModel.AutoModeCustom(false, false, testStartTime, null, false) 194 195 val outputState = mapper.map(config, inputModel) 196 197 val expectedState = 198 createNightDisplayTileState( 199 QSTileState.ActivationState.INACTIVE, 200 context.getString( 201 R.string.quick_settings_night_secondary_label_on_at, 202 formatter12Hour.format(testStartTime), 203 ), 204 ) 205 QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState) 206 } 207 208 /** Should have the same outcome as [disabledModel_autoModeCustom_12Hour] */ 209 @Test disabledModel_autoModeCustom_12Hour_isEnrolledForcedAutoModenull210 fun disabledModel_autoModeCustom_12Hour_isEnrolledForcedAutoMode() { 211 val inputModel = 212 NightDisplayTileModel.AutoModeCustom(false, true, testStartTime, null, false) 213 214 val outputState = mapper.map(config, inputModel) 215 216 val expectedState = 217 createNightDisplayTileState( 218 QSTileState.ActivationState.INACTIVE, 219 context.getString( 220 R.string.quick_settings_night_secondary_label_on_at, 221 formatter12Hour.format(testStartTime), 222 ), 223 ) 224 QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState) 225 } 226 227 @Test enabledModel_autoModeCustom_24Hournull228 fun enabledModel_autoModeCustom_24Hour() { 229 val inputModel = NightDisplayTileModel.AutoModeCustom(true, false, null, testEndTime, true) 230 231 val outputState = mapper.map(config, inputModel) 232 233 val expectedState = 234 createNightDisplayTileState( 235 QSTileState.ActivationState.ACTIVE, 236 context.getString( 237 R.string.quick_settings_secondary_label_until, 238 formatter24Hour.format(testEndTime), 239 ), 240 ) 241 QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState) 242 } 243 244 @Test enabledModel_autoModeCustom_12Hournull245 fun enabledModel_autoModeCustom_12Hour() { 246 val inputModel = NightDisplayTileModel.AutoModeCustom(true, false, null, testEndTime, false) 247 248 val outputState = mapper.map(config, inputModel) 249 250 val expectedState = 251 createNightDisplayTileState( 252 QSTileState.ActivationState.ACTIVE, 253 context.getString( 254 R.string.quick_settings_secondary_label_until, 255 formatter12Hour.format(testEndTime), 256 ), 257 ) 258 QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState) 259 } 260 261 /** Should have the same state as [enabledModel_autoModeCustom_24Hour] */ 262 @Test enabledModel_autoModeCustom_24Hour_forceEnablednull263 fun enabledModel_autoModeCustom_24Hour_forceEnabled() { 264 val inputModel = NightDisplayTileModel.AutoModeCustom(true, true, null, testEndTime, true) 265 266 val outputState = mapper.map(config, inputModel) 267 268 val expectedState = 269 createNightDisplayTileState( 270 QSTileState.ActivationState.ACTIVE, 271 context.getString( 272 R.string.quick_settings_secondary_label_until, 273 formatter24Hour.format(testEndTime), 274 ), 275 ) 276 QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState) 277 } 278 createNightDisplayTileStatenull279 private fun createNightDisplayTileState( 280 activationState: QSTileState.ActivationState, 281 secondaryLabel: String?, 282 ): QSTileState { 283 val label = context.getString(R.string.quick_settings_night_display_label) 284 val iconRes = 285 if (activationState == QSTileState.ActivationState.ACTIVE) 286 R.drawable.qs_nightlight_icon_on 287 else R.drawable.qs_nightlight_icon_off 288 val contentDescription = 289 if (TextUtils.isEmpty(secondaryLabel)) label 290 else TextUtils.concat(label, ", ", secondaryLabel) 291 return QSTileState( 292 Icon.Loaded(context.getDrawable(iconRes)!!, null), 293 iconRes, 294 label, 295 activationState, 296 secondaryLabel, 297 setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK), 298 contentDescription, 299 null, 300 QSTileState.SideViewIcon.None, 301 QSTileState.EnabledState.ENABLED, 302 Switch::class.qualifiedName, 303 ) 304 } 305 306 private companion object { 307 val formatter12Hour: DateTimeFormatter = DateTimeFormatter.ofPattern("hh:mm a") 308 val formatter24Hour: DateTimeFormatter = DateTimeFormatter.ofPattern("HH:mm") 309 } 310 } 311