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