xref: /aosp_15_r20/frameworks/base/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt (revision d57664e9bc4670b3ecf6748a746a57c557b6bc9e)
1 /*
2  * Copyright (C) 2021 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.tileimpl
18 
19 import android.content.Context
20 import android.graphics.Rect
21 import android.graphics.drawable.Drawable
22 import android.service.quicksettings.Tile
23 import android.testing.TestableLooper
24 import android.text.TextUtils
25 import android.view.ContextThemeWrapper
26 import android.view.View
27 import android.view.accessibility.AccessibilityNodeInfo
28 import android.widget.Button
29 import android.widget.TextView
30 import androidx.test.ext.junit.runners.AndroidJUnit4
31 import androidx.test.filters.SmallTest
32 import com.android.systemui.SysuiTestCase
33 import com.android.systemui.haptics.qs.QSLongPressEffect
34 import com.android.systemui.haptics.qs.qsLongPressEffect
35 import com.android.systemui.plugins.qs.QSTile
36 import com.android.systemui.qs.qsTileFactory
37 import com.android.systemui.res.R
38 import com.android.systemui.testKosmos
39 import com.google.common.truth.Truth.assertThat
40 import org.junit.Before
41 import org.junit.Test
42 import org.junit.runner.RunWith
43 import org.mockito.Mock
44 import org.mockito.MockitoAnnotations
45 
46 @RunWith(AndroidJUnit4::class)
47 @SmallTest
48 @TestableLooper.RunWithLooper(setAsMainLooper = true)
49 class QSTileViewImplTest : SysuiTestCase() {
50 
51     @Mock private lateinit var customDrawable: Drawable
52 
53     private lateinit var tileView: FakeTileView
54     private lateinit var customDrawableView: View
55     private lateinit var chevronView: View
56     private val kosmos = testKosmos()
57 
58     @Before
setUpnull59     fun setUp() {
60         MockitoAnnotations.initMocks(this)
61         context.ensureTestableResources()
62 
63         tileView = FakeTileView(context, false, kosmos.qsLongPressEffect)
64         customDrawableView = tileView.requireViewById(R.id.customDrawable)
65         chevronView = tileView.requireViewById(R.id.chevron)
66     }
67 
68     @Test
testSecondaryLabelNotModified_unavailablenull69     fun testSecondaryLabelNotModified_unavailable() {
70         val state = QSTile.State()
71         val testString = "TEST STRING"
72         state.state = Tile.STATE_UNAVAILABLE
73         state.secondaryLabel = testString
74 
75         tileView.changeState(state)
76 
77         assertThat(state.secondaryLabel as CharSequence).isEqualTo(testString)
78     }
79 
80     @Test
testSecondaryLabelNotModified_booleanInactivenull81     fun testSecondaryLabelNotModified_booleanInactive() {
82         val state = QSTile.BooleanState()
83         val testString = "TEST STRING"
84         state.state = Tile.STATE_INACTIVE
85         state.secondaryLabel = testString
86 
87         tileView.changeState(state)
88 
89         assertThat(state.secondaryLabel as CharSequence).isEqualTo(testString)
90     }
91 
92     @Test
testSecondaryLabelNotModified_booleanActivenull93     fun testSecondaryLabelNotModified_booleanActive() {
94         val state = QSTile.BooleanState()
95         val testString = "TEST STRING"
96         state.state = Tile.STATE_ACTIVE
97         state.secondaryLabel = testString
98 
99         tileView.changeState(state)
100 
101         assertThat(state.secondaryLabel as CharSequence).isEqualTo(testString)
102     }
103 
104     @Test
testSecondaryLabelNotModified_availableNotBoolean_inactivenull105     fun testSecondaryLabelNotModified_availableNotBoolean_inactive() {
106         val state = QSTile.State()
107         state.state = Tile.STATE_INACTIVE
108         state.secondaryLabel = ""
109 
110         tileView.changeState(state)
111 
112         assertThat(TextUtils.isEmpty(state.secondaryLabel)).isTrue()
113     }
114 
115     @Test
testSecondaryLabelNotModified_availableNotBoolean_activenull116     fun testSecondaryLabelNotModified_availableNotBoolean_active() {
117         val state = QSTile.State()
118         state.state = Tile.STATE_ACTIVE
119         state.secondaryLabel = ""
120 
121         tileView.changeState(state)
122 
123         assertThat(TextUtils.isEmpty(state.secondaryLabel)).isTrue()
124     }
125 
126     @Test
testSecondaryLabelDescription_unavailable_defaultnull127     fun testSecondaryLabelDescription_unavailable_default() {
128         val state = QSTile.State()
129         state.state = Tile.STATE_UNAVAILABLE
130         state.secondaryLabel = ""
131 
132         tileView.changeState(state)
133 
134         assertThat(state.secondaryLabel as CharSequence)
135             .isEqualTo(context.getString(R.string.tile_unavailable))
136     }
137 
138     @Test
testSecondaryLabelDescription_booleanInactive_defaultnull139     fun testSecondaryLabelDescription_booleanInactive_default() {
140         val state = QSTile.BooleanState()
141         state.state = Tile.STATE_INACTIVE
142         state.secondaryLabel = ""
143 
144         tileView.changeState(state)
145 
146         assertThat(state.secondaryLabel as CharSequence)
147             .isEqualTo(context.getString(R.string.switch_bar_off))
148     }
149 
150     @Test
testSecondaryLabelDescription_booleanActive_defaultnull151     fun testSecondaryLabelDescription_booleanActive_default() {
152         val state = QSTile.BooleanState()
153         state.state = Tile.STATE_ACTIVE
154         state.secondaryLabel = ""
155 
156         tileView.changeState(state)
157 
158         assertThat(state.secondaryLabel as CharSequence)
159             .isEqualTo(context.getString(R.string.switch_bar_on))
160     }
161 
162     @Test
testShowCustomDrawableViewBooleanStatenull163     fun testShowCustomDrawableViewBooleanState() {
164         val state = QSTile.BooleanState()
165         state.sideViewCustomDrawable = customDrawable
166 
167         tileView.changeState(state)
168 
169         assertThat(customDrawableView.visibility).isEqualTo(View.VISIBLE)
170         assertThat(chevronView.visibility).isEqualTo(View.GONE)
171     }
172 
173     @Test
testShowCustomDrawableViewNonBooleanStatenull174     fun testShowCustomDrawableViewNonBooleanState() {
175         val state = QSTile.State()
176         state.sideViewCustomDrawable = customDrawable
177 
178         tileView.changeState(state)
179 
180         assertThat(customDrawableView.visibility).isEqualTo(View.VISIBLE)
181         assertThat(chevronView.visibility).isEqualTo(View.GONE)
182     }
183 
184     @Test
testShowCustomDrawableViewBooleanStateForceChevronnull185     fun testShowCustomDrawableViewBooleanStateForceChevron() {
186         val state = QSTile.BooleanState()
187         state.sideViewCustomDrawable = customDrawable
188         state.forceExpandIcon = true
189 
190         tileView.changeState(state)
191 
192         assertThat(customDrawableView.visibility).isEqualTo(View.VISIBLE)
193         assertThat(chevronView.visibility).isEqualTo(View.GONE)
194     }
195 
196     @Test
testShowChevronNonBooleanStatenull197     fun testShowChevronNonBooleanState() {
198         val state = QSTile.State()
199 
200         tileView.changeState(state)
201 
202         assertThat(customDrawableView.visibility).isEqualTo(View.GONE)
203         assertThat(chevronView.visibility).isEqualTo(View.VISIBLE)
204     }
205 
206     @Test
testShowChevronBooleanStateForcheShownull207     fun testShowChevronBooleanStateForcheShow() {
208         val state = QSTile.BooleanState()
209         state.forceExpandIcon = true
210 
211         tileView.changeState(state)
212 
213         assertThat(customDrawableView.visibility).isEqualTo(View.GONE)
214         assertThat(chevronView.visibility).isEqualTo(View.VISIBLE)
215     }
216 
217     @Test
testNoImageShownnull218     fun testNoImageShown() {
219         val state = QSTile.BooleanState()
220 
221         tileView.changeState(state)
222 
223         assertThat(customDrawableView.visibility).isEqualTo(View.GONE)
224         assertThat(chevronView.visibility).isEqualTo(View.GONE)
225     }
226 
227     @Test
testUseStateStringsForKnownSpec_Booleannull228     fun testUseStateStringsForKnownSpec_Boolean() {
229         val state = QSTile.BooleanState()
230         val spec = "internet"
231         state.spec = spec
232 
233         val unavailableString = "${spec}_unavailable"
234         val offString = "${spec}_off"
235         val onString = "${spec}_on"
236 
237         context.orCreateTestableResources.addOverride(
238             R.array.tile_states_internet,
239             arrayOf(unavailableString, offString, onString)
240         )
241 
242         // State UNAVAILABLE
243         state.secondaryLabel = ""
244         state.state = Tile.STATE_UNAVAILABLE
245         tileView.changeState(state)
246         assertThat((tileView.secondaryLabel as TextView).text).isEqualTo(unavailableString)
247 
248         // State INACTIVE
249         state.secondaryLabel = ""
250         state.state = Tile.STATE_INACTIVE
251         tileView.changeState(state)
252         assertThat((tileView.secondaryLabel as TextView).text).isEqualTo(offString)
253 
254         // State ACTIVE
255         state.secondaryLabel = ""
256         state.state = Tile.STATE_ACTIVE
257         tileView.changeState(state)
258         assertThat((tileView.secondaryLabel as TextView).text).isEqualTo(onString)
259     }
260 
261     @Test
testCollectionItemInfoHasPositionnull262     fun testCollectionItemInfoHasPosition() {
263         val position = 5
264         tileView.setPosition(position)
265 
266         val info = AccessibilityNodeInfo(tileView)
267         tileView.onInitializeAccessibilityNodeInfo(info)
268 
269         assertThat(info.collectionItemInfo.rowIndex).isEqualTo(position)
270         assertThat(info.collectionItemInfo.rowSpan).isEqualTo(1)
271         assertThat(info.collectionItemInfo.columnIndex).isEqualTo(0)
272         assertThat(info.collectionItemInfo.columnSpan).isEqualTo(1)
273     }
274 
275     @Test
testCollectionItemInfoNoPositionnull276     fun testCollectionItemInfoNoPosition() {
277         val info = AccessibilityNodeInfo(tileView)
278         tileView.onInitializeAccessibilityNodeInfo(info)
279 
280         assertThat(info.collectionItemInfo).isNull()
281     }
282 
283     @Test
testDisabledByPolicyInactive_usesUnavailableColorsnull284     fun testDisabledByPolicyInactive_usesUnavailableColors() {
285         val stateDisabledByPolicy = QSTile.State()
286         stateDisabledByPolicy.state = Tile.STATE_INACTIVE
287         stateDisabledByPolicy.disabledByPolicy = true
288 
289         val stateUnavailable = QSTile.State()
290         stateUnavailable.state = Tile.STATE_UNAVAILABLE
291 
292         tileView.changeState(stateDisabledByPolicy)
293         val colorsDisabledByPolicy = tileView.getCurrentColors()
294 
295         tileView.changeState(stateUnavailable)
296         val colorsUnavailable = tileView.getCurrentColors()
297 
298         assertThat(colorsDisabledByPolicy).containsExactlyElementsIn(colorsUnavailable)
299     }
300 
301     @Test
testDisabledByPolicyActive_usesUnavailableColorsnull302     fun testDisabledByPolicyActive_usesUnavailableColors() {
303         val stateDisabledByPolicy = QSTile.State()
304         stateDisabledByPolicy.state = Tile.STATE_ACTIVE
305         stateDisabledByPolicy.disabledByPolicy = true
306 
307         val stateUnavailable = QSTile.State()
308         stateUnavailable.state = Tile.STATE_UNAVAILABLE
309 
310         tileView.changeState(stateDisabledByPolicy)
311         val colorsDisabledByPolicy = tileView.getCurrentColors()
312 
313         tileView.changeState(stateUnavailable)
314         val colorsUnavailable = tileView.getCurrentColors()
315 
316         assertThat(colorsDisabledByPolicy).containsExactlyElementsIn(colorsUnavailable)
317     }
318 
319     @Test
testDisableByPolicyThenRemoved_changesColornull320     fun testDisableByPolicyThenRemoved_changesColor() {
321         val stateActive = QSTile.State()
322         stateActive.state = Tile.STATE_ACTIVE
323 
324         val stateDisabledByPolicy = stateActive.copy()
325         stateDisabledByPolicy.disabledByPolicy = true
326 
327         tileView.changeState(stateActive)
328         val activeColors = tileView.getCurrentColors()
329 
330         tileView.changeState(stateDisabledByPolicy)
331         // It has unavailable colors
332         assertThat(tileView.getCurrentColors()).isNotEqualTo(activeColors)
333 
334         // When we get back to not disabled by policy tile, it should go back to active colors
335         tileView.changeState(stateActive)
336         assertThat(tileView.getCurrentColors()).containsExactlyElementsIn(activeColors)
337     }
338 
339     @Test
testDisabledByPolicy_secondaryLabelTextnull340     fun testDisabledByPolicy_secondaryLabelText() {
341         val testA11yLabel = "TEST_LABEL"
342         context.orCreateTestableResources.addOverride(
343             R.string.accessibility_tile_disabled_by_policy_action_description,
344             testA11yLabel
345         )
346 
347         val stateDisabledByPolicy = QSTile.State()
348         stateDisabledByPolicy.state = Tile.STATE_INACTIVE
349         stateDisabledByPolicy.disabledByPolicy = true
350 
351         tileView.changeState(stateDisabledByPolicy)
352 
353         val info = AccessibilityNodeInfo(tileView)
354         tileView.onInitializeAccessibilityNodeInfo(info)
355         assertThat(
356                 info.actionList
357                     .find { it.id == AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK.id }
358                     ?.label
359             )
360             .isEqualTo(testA11yLabel)
361     }
362 
363     @Test
testDisabledByPolicy_unavailableInStateDescriptionnull364     fun testDisabledByPolicy_unavailableInStateDescription() {
365         val state = QSTile.BooleanState()
366         val spec = "internet"
367         state.spec = spec
368         state.disabledByPolicy = true
369         state.state = Tile.STATE_INACTIVE
370 
371         val unavailableString = "${spec}_unavailable"
372         val offString = "${spec}_off"
373         val onString = "${spec}_on"
374 
375         context.orCreateTestableResources.addOverride(
376             R.array.tile_states_internet,
377             arrayOf(unavailableString, offString, onString)
378         )
379 
380         tileView.changeState(state)
381         assertThat(tileView.stateDescription?.contains(unavailableString)).isTrue()
382     }
383 
384     @Test
testNonSwitchA11yClass_longClickActionHasCorrectLabelnull385     fun testNonSwitchA11yClass_longClickActionHasCorrectLabel() {
386         val state =
387             QSTile.State().apply {
388                 expandedAccessibilityClassName = Button::class.java.name
389                 handlesLongClick = true
390             }
391         tileView.changeState(state)
392         val info = AccessibilityNodeInfo(tileView)
393         tileView.onInitializeAccessibilityNodeInfo(info)
394 
395         assertThat(
396                 info.actionList
397                     .find {
398                         it.id == AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK.id
399                     }
400                     ?.label
401             )
402             .isEqualTo(context.getString(R.string.accessibility_long_click_tile))
403     }
404 
405     @Test
testNonSwitchA11yClass_disabledByPolicy_noLongClickActionnull406     fun testNonSwitchA11yClass_disabledByPolicy_noLongClickAction() {
407         val state =
408             QSTile.State().apply {
409                 expandedAccessibilityClassName = Button::class.java.name
410                 handlesLongClick = true
411                 disabledByPolicy = true
412             }
413         tileView.changeState(state)
414         val info = AccessibilityNodeInfo(tileView)
415         tileView.onInitializeAccessibilityNodeInfo(info)
416 
417         assertThat(
418                 info.actionList.find {
419                     it.id == AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK.id
420                 }
421             )
422             .isNull()
423     }
424 
425     @Test
onStateChange_longPressEffectActive_withInvalidDuration_doesNotInitializeEffectnull426     fun onStateChange_longPressEffectActive_withInvalidDuration_doesNotInitializeEffect() {
427         val state = QSTile.State() // A state that handles longPress
428 
429         // GIVEN an invalid long-press effect duration
430         tileView.constantLongPressEffectDuration = -1
431 
432         // WHEN the state changes
433         tileView.changeState(state)
434 
435         // THEN the long-press effect is not initialized
436         assertThat(tileView.isLongPressEffectInitialized).isFalse()
437     }
438 
439     @Test
onStateChange_longPressEffectActive_withValidDuration_initializesEffectnull440     fun onStateChange_longPressEffectActive_withValidDuration_initializesEffect() {
441         // GIVEN a test state that handles long-press and a valid long-press effect duration
442         val state = QSTile.State()
443 
444         // WHEN the state changes
445         tileView.changeState(state)
446 
447         // THEN the long-press effect is initialized
448         assertThat(tileView.isLongPressEffectInitialized).isTrue()
449     }
450 
451     @Test
onStateChange_fromLongPress_to_noLongPress_clearsResourcesnull452     fun onStateChange_fromLongPress_to_noLongPress_clearsResources() {
453         // GIVEN a state that no longer handles long-press
454         val state = QSTile.State()
455         state.handlesLongClick = false
456 
457         // WHEN the state changes
458         tileView.changeState(state)
459 
460         // THEN the long-press effect resources are not set
461         assertThat(tileView.areLongPressEffectPropertiesSet).isFalse()
462     }
463 
464     @Test
onStateChange_fromNoLongPress_to_longPress_setsPropertiesnull465     fun onStateChange_fromNoLongPress_to_longPress_setsProperties() {
466         // GIVEN that the tile has changed to a state that does not handle long-press
467         val state = QSTile.State()
468         state.handlesLongClick = false
469         tileView.changeState(state)
470 
471         // WHEN the state changes back to handling long-press
472         state.handlesLongClick = true
473         tileView.changeState(state)
474 
475         // THEN the long-press effect resources are set
476         assertThat(tileView.areLongPressEffectPropertiesSet).isTrue()
477     }
478 
479     @Test
onStateChange_withoutLongPressEffect_fromLongPress_to_noLongPress_neverSetsPropertiesnull480     fun onStateChange_withoutLongPressEffect_fromLongPress_to_noLongPress_neverSetsProperties() {
481         // GIVEN a tile where the long-press effect is null
482         tileView = FakeTileView(context, false, null)
483 
484         // GIVEN a state that no longer handles long-press
485         val state = QSTile.State()
486         state.handlesLongClick = false
487 
488         // WHEN the state changes
489         tileView.changeState(state)
490 
491         // THEN the effect properties are not set and the effect is not initialized
492         assertThat(tileView.areLongPressEffectPropertiesSet).isFalse()
493         assertThat(tileView.isLongPressEffectInitialized).isFalse()
494     }
495 
496     @Test
onStateChange_withoutLongPressEffect_fromNoLongPress_to_longPress_neverSetsPropertiesnull497     fun onStateChange_withoutLongPressEffect_fromNoLongPress_to_longPress_neverSetsProperties() {
498         // GIVEN a tile where the long-press effect is null
499         tileView = FakeTileView(context, false, null)
500 
501         // GIVEN that the tile has changed to a state that does not handle long-press
502         val state = QSTile.State()
503         state.handlesLongClick = false
504         tileView.changeState(state)
505 
506         // WHEN the state changes back to handling long-press
507         state.handlesLongClick = true
508         tileView.changeState(state)
509 
510         // THEN the effect properties are not set and the effect is not initialized
511         assertThat(tileView.areLongPressEffectPropertiesSet).isFalse()
512         assertThat(tileView.isLongPressEffectInitialized).isFalse()
513     }
514 
515     @Test
getPaddingForLaunchAnimation_onLongClickedState_paddingForLaunchAnimationIsConfigurednull516     fun getPaddingForLaunchAnimation_onLongClickedState_paddingForLaunchAnimationIsConfigured() {
517         val startingWidth = 100
518         val startingHeight = 50
519         val deltaWidth = (QSTileViewImpl.LONG_PRESS_EFFECT_WIDTH_SCALE - 1f) * startingWidth
520         val deltaHeight = (QSTileViewImpl.LONG_PRESS_EFFECT_HEIGHT_SCALE - 1f) * startingHeight
521 
522         // GIVEN that long-press effect properties are initialized
523         tileView.initializeLongPressProperties(startingHeight, startingWidth)
524 
525         // WHEN the long-press effect has ended in the long-click state
526         kosmos.qsLongPressEffect.setState(QSLongPressEffect.State.LONG_CLICKED)
527 
528         // THE animation padding corresponds to the tile's growth due to the effect
529         val padding = tileView.getPaddingForLaunchAnimation()
530         assertThat(padding)
531             .isEqualTo(
532                 Rect(
533                     -deltaWidth.toInt() / 2,
534                     -deltaHeight.toInt() / 2,
535                     deltaWidth.toInt() / 2,
536                     deltaHeight.toInt() / 2,
537                 )
538             )
539     }
540 
541     @Test
getPaddingForLaunchAnimation_notInLongClickState_paddingForLaunchAnimationIsEmptynull542     fun getPaddingForLaunchAnimation_notInLongClickState_paddingForLaunchAnimationIsEmpty() {
543         val startingWidth = 100
544         val startingHeight = 50
545 
546         // GIVEN that long-press effect properties are initialized
547         tileView.initializeLongPressProperties(startingHeight, startingWidth)
548 
549         // WHEN the long-press effect has ended in the click state
550         kosmos.qsLongPressEffect.setState(QSLongPressEffect.State.CLICKED)
551 
552         // THE animation padding is empty
553         val padding = tileView.getPaddingForLaunchAnimation()
554         assertThat(padding.isEmpty).isTrue()
555     }
556 
557     @Test
onActivityLaunchAnimationEnd_onFreshTile_longPressPropertiesAreResetnull558     fun onActivityLaunchAnimationEnd_onFreshTile_longPressPropertiesAreReset() {
559         // WHEN an activity launch animation ends on a fresh tile
560         tileView.onActivityLaunchAnimationEnd()
561 
562         // THEN the tile's long-press effect properties are reset by default
563         assertThat(tileView.haveLongPressPropertiesBeenReset).isTrue()
564     }
565 
566     @Test
onUpdateLongPressEffectProperties_duringLongPressEffect_propertiesAreNotResetnull567     fun onUpdateLongPressEffectProperties_duringLongPressEffect_propertiesAreNotReset() {
568         // GIVEN a state that supports long-press
569         val state = QSTile.State()
570         tileView.changeState(state)
571 
572         // WHEN the long-press effect is updating the properties
573         tileView.updateLongPressEffectProperties(1f)
574 
575         // THEN the tile's long-press effect properties haven't reset
576         assertThat(tileView.haveLongPressPropertiesBeenReset).isFalse()
577     }
578 
579     @Test
onActivityLaunchAnimationEnd_afterLongPressEffect_longPressPropertiesAreResetnull580     fun onActivityLaunchAnimationEnd_afterLongPressEffect_longPressPropertiesAreReset() {
581         // GIVEN a state that supports long-press and the long-press effect updating
582         val state = QSTile.State()
583         tileView.changeState(state)
584         tileView.updateLongPressEffectProperties(1f)
585 
586         // WHEN an activity launch animation ends on a fresh tile
587         tileView.onActivityLaunchAnimationEnd()
588 
589         // THEN the tile's long-press effect properties are reset
590         assertThat(tileView.haveLongPressPropertiesBeenReset).isTrue()
591     }
592 
593     @Test
onInit_withLongPressEffect_longPressEffectHasTileAndExpandablenull594     fun onInit_withLongPressEffect_longPressEffectHasTileAndExpandable() {
595         val tile = kosmos.qsTileFactory.createTile("Test Tile")
596         tileView.init(tile)
597 
598         assertThat(tileView.isTileAddedToLongPress).isTrue()
599         assertThat(tileView.isExpandableAddedToLongPress).isTrue()
600     }
601 
602     @Test
onInit_withoutLongPressEffect_longPressEffectDoesNotHaveTileAndExpandablenull603     fun onInit_withoutLongPressEffect_longPressEffectDoesNotHaveTileAndExpandable() {
604         tileView = FakeTileView(context, false, null)
605         val tile = kosmos.qsTileFactory.createTile("Test Tile")
606         tileView.init(tile)
607 
608         assertThat(tileView.isTileAddedToLongPress).isFalse()
609         assertThat(tileView.isExpandableAddedToLongPress).isFalse()
610     }
611 
612     class FakeTileView(
613         context: Context,
614         collapsed: Boolean,
615         private val longPressEffect: QSLongPressEffect?,
616     ) :
617         QSTileViewImpl(
618             ContextThemeWrapper(context, R.style.Theme_SystemUI_QuickSettings),
619             collapsed,
620             longPressEffect,
621         ) {
622         var constantLongPressEffectDuration = 500
623         val isTileAddedToLongPress: Boolean
624             get() = longPressEffect?.qsTile != null
625 
626         val isExpandableAddedToLongPress: Boolean
627             get() = longPressEffect?.expandable != null
628 
getLongPressEffectDurationnull629         override fun getLongPressEffectDuration(): Int = constantLongPressEffectDuration
630 
631         fun changeState(state: QSTile.State) {
632             handleStateChanged(state)
633         }
634     }
635 }
636