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.haptics.msdl.qs
18 
19 import android.service.quicksettings.Tile
20 import androidx.test.ext.junit.runners.AndroidJUnit4
21 import androidx.test.filters.SmallTest
22 import com.android.systemui.SysuiTestCase
23 import com.android.systemui.haptics.msdl.fakeMSDLPlayer
24 import com.android.systemui.haptics.msdl.tileHapticsViewModelFactory
25 import com.android.systemui.kosmos.testScope
26 import com.android.systemui.lifecycle.activateIn
27 import com.android.systemui.plugins.qs.QSTile
28 import com.android.systemui.qs.panels.ui.viewmodel.fakeQsTile
29 import com.android.systemui.qs.panels.ui.viewmodel.tileViewModel
30 import com.android.systemui.testKosmos
31 import com.google.android.msdl.data.model.MSDLToken
32 import com.google.common.truth.Truth.assertThat
33 import kotlinx.coroutines.ExperimentalCoroutinesApi
34 import kotlinx.coroutines.test.TestScope
35 import kotlinx.coroutines.test.runCurrent
36 import kotlinx.coroutines.test.runTest
37 import org.junit.Before
38 import org.junit.Test
39 import org.junit.runner.RunWith
40 
41 @SmallTest
42 @OptIn(ExperimentalCoroutinesApi::class)
43 @RunWith(AndroidJUnit4::class)
44 class TileHapticsViewModelTest : SysuiTestCase() {
45 
46     private val kosmos = testKosmos()
47     private val testScope = kosmos.testScope
48     private val qsTile = kosmos.fakeQsTile
49     private val msdlPlayer = kosmos.fakeMSDLPlayer
50     private val tileViewModel = kosmos.tileViewModel
51 
52     private val underTest = kosmos.tileHapticsViewModelFactory.create(tileViewModel)
53 
54     @Before
setUpnull55     fun setUp() {
56         underTest.activateIn(testScope)
57     }
58 
59     @Test
whenTileTogglesOnFromClick_playsSwitchOnHapticsnull60     fun whenTileTogglesOnFromClick_playsSwitchOnHaptics() =
61         testScope.runTest {
62             // WHEN the tile toggles on after being clicked
63             underTest.setTileInteractionState(TileHapticsViewModel.TileInteractionState.CLICKED)
64             toggleOn()
65 
66             // THEN the switch on token plays
67             assertThat(msdlPlayer.latestTokenPlayed).isEqualTo(MSDLToken.SWITCH_ON)
68             assertThat(msdlPlayer.latestPropertiesPlayed).isNull()
69         }
70 
71     @Test
whenTileTogglesOffFromClick_playsSwitchOffHapticsnull72     fun whenTileTogglesOffFromClick_playsSwitchOffHaptics() =
73         testScope.runTest {
74             // WHEN the tile toggles off after being clicked
75             underTest.setTileInteractionState(TileHapticsViewModel.TileInteractionState.CLICKED)
76             toggleOff()
77 
78             // THEN the switch off token plays
79             assertThat(msdlPlayer.latestTokenPlayed).isEqualTo(MSDLToken.SWITCH_OFF)
80             assertThat(msdlPlayer.latestPropertiesPlayed).isNull()
81         }
82 
83     @Test
whenTileTogglesOnWhileIdle_doesNotPlaySwitchOnHapticsnull84     fun whenTileTogglesOnWhileIdle_doesNotPlaySwitchOnHaptics() =
85         testScope.runTest {
86             // WHEN the tile toggles on without being clicked
87             toggleOn()
88 
89             // THEN no token plays
90             assertThat(msdlPlayer.latestTokenPlayed).isNull()
91             assertThat(msdlPlayer.latestPropertiesPlayed).isNull()
92         }
93 
94     @Test
whenTileTogglesOffWhileIdle_doesNotPlaySwitchOffHapticsnull95     fun whenTileTogglesOffWhileIdle_doesNotPlaySwitchOffHaptics() =
96         testScope.runTest {
97             // WHEN the tile toggles off without being clicked
98             toggleOff()
99 
100             // THEN no token plays
101             assertThat(msdlPlayer.latestTokenPlayed).isNull()
102             assertThat(msdlPlayer.latestPropertiesPlayed).isNull()
103         }
104 
105     @Test
whenLaunchingFromLongClick_playsLongPressHapticsnull106     fun whenLaunchingFromLongClick_playsLongPressHaptics() =
107         testScope.runTest {
108             // WHEN the tile is long-clicked and its action state changes accordingly
109             underTest.setTileInteractionState(
110                 TileHapticsViewModel.TileInteractionState.LONG_CLICKED
111             )
112             // WHEN the activity transition (from the long-click) starts
113             underTest.onActivityLaunchTransitionStart()
114             runCurrent()
115 
116             // THEN the long-press token plays
117             assertThat(msdlPlayer.latestTokenPlayed).isEqualTo(MSDLToken.LONG_PRESS)
118             assertThat(msdlPlayer.latestPropertiesPlayed).isNull()
119         }
120 
121     @Test
whenLaunchingFromClick_doesNotPlayHapticsnull122     fun whenLaunchingFromClick_doesNotPlayHaptics() =
123         testScope.runTest {
124             // WHEN the tile is clicked and its action state changes accordingly
125             underTest.setTileInteractionState(TileHapticsViewModel.TileInteractionState.CLICKED)
126             // WHEN an activity transition starts (from clicking)
127             underTest.onActivityLaunchTransitionStart()
128             runCurrent()
129 
130             // THEN no haptics play
131             assertThat(msdlPlayer.latestTokenPlayed).isNull()
132             assertThat(msdlPlayer.latestPropertiesPlayed).isNull()
133         }
134 
TestScopenull135     private fun TestScope.toggleOn() {
136         qsTile.changeState(QSTile.State().apply { state = Tile.STATE_INACTIVE })
137         runCurrent()
138 
139         qsTile.changeState(QSTile.State().apply { state = Tile.STATE_ACTIVE })
140         runCurrent()
141     }
142 
toggleOffnull143     private fun TestScope.toggleOff() {
144         qsTile.changeState(QSTile.State().apply { state = Tile.STATE_ACTIVE })
145         runCurrent()
146 
147         qsTile.changeState(QSTile.State().apply { state = Tile.STATE_INACTIVE })
148         runCurrent()
149     }
150 }
151