Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0ae02176 authored by Angela Wang's avatar Angela Wang
Browse files

Migrate HearingDevicesTile

Bug: 363023752
Flag: com.android.systemui.hearing_aids_qs_tile_dialog
Flag: com.android.systemui.qs_new_tiles_future
Test: atest HearingDevicesTileMapperTest HearingDevicesTileDataInteractorTest HearingDevicesTileUserActionInteractorTest
Change-Id: I6ffd9785615f1eebbeba59471827982dbac38192
parent 832e8eda
Loading
Loading
Loading
Loading
+118 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.android.systemui.qs.tiles.impl.hearingdevices.domain

import android.graphics.drawable.TestStubDrawable
import android.widget.Switch
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
import com.android.systemui.qs.tiles.impl.hearingdevices.domain.model.HearingDevicesTileModel
import com.android.systemui.qs.tiles.impl.hearingdevices.qsHearingDevicesTileConfig
import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
import org.junit.Test
import org.junit.runner.RunWith

@SmallTest
@RunWith(AndroidJUnit4::class)
class HearingDevicesTileMapperTest : SysuiTestCase() {
    private val kosmos = Kosmos()
    private val qsTileConfig = kosmos.qsHearingDevicesTileConfig
    private val mapper by lazy {
        HearingDevicesTileMapper(
            context.orCreateTestableResources
                .apply { addOverride(R.drawable.qs_hearing_devices_icon, TestStubDrawable()) }
                .resources,
            context.theme,
        )
    }

    @Test
    fun map_anyActiveHearingDevice_anyPairedHearingDevice_activeState() {
        val tileState: QSTileState =
            mapper.map(
                qsTileConfig,
                HearingDevicesTileModel(
                    isAnyActiveHearingDevice = true,
                    isAnyPairedHearingDevice = true,
                ),
            )
        val expectedState =
            createHearingDevicesTileState(
                QSTileState.ActivationState.ACTIVE,
                context.getString(R.string.quick_settings_hearing_devices_connected),
            )
        QSTileStateSubject.assertThat(tileState).isEqualTo(expectedState)
    }

    @Test
    fun map_noActiveHearingDevice_anyPairedHearingDevice_inactiveState() {
        val tileState: QSTileState =
            mapper.map(
                qsTileConfig,
                HearingDevicesTileModel(
                    isAnyActiveHearingDevice = false,
                    isAnyPairedHearingDevice = true,
                ),
            )
        val expectedState =
            createHearingDevicesTileState(
                QSTileState.ActivationState.INACTIVE,
                context.getString(R.string.quick_settings_hearing_devices_disconnected),
            )
        QSTileStateSubject.assertThat(tileState).isEqualTo(expectedState)
    }

    @Test
    fun map_noActiveHearingDevice_noPairedHearingDevice_inactiveState() {
        val tileState: QSTileState =
            mapper.map(
                qsTileConfig,
                HearingDevicesTileModel(
                    isAnyActiveHearingDevice = false,
                    isAnyPairedHearingDevice = false,
                ),
            )
        val expectedState =
            createHearingDevicesTileState(QSTileState.ActivationState.INACTIVE, secondaryLabel = "")
        QSTileStateSubject.assertThat(tileState).isEqualTo(expectedState)
    }

    private fun createHearingDevicesTileState(
        activationState: QSTileState.ActivationState,
        secondaryLabel: String,
    ): QSTileState {
        val label = context.getString(R.string.quick_settings_hearing_devices_label)
        val iconRes = R.drawable.qs_hearing_devices_icon
        return QSTileState(
            { Icon.Loaded(context.getDrawable(iconRes)!!, null) },
            iconRes,
            label,
            activationState,
            secondaryLabel,
            setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK),
            label,
            null,
            QSTileState.SideViewIcon.Chevron,
            QSTileState.EnabledState.ENABLED,
            Switch::class.qualifiedName,
        )
    }
}
+158 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.systemui.qs.tiles.impl.hearingdevices.domain.interactor

import android.os.UserHandle
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.platform.test.annotations.EnabledOnRavenwood
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.accessibility.hearingaid.HearingDevicesChecker
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
import com.android.systemui.qs.tiles.impl.hearingdevices.domain.model.HearingDevicesTileModel
import com.android.systemui.statusbar.policy.fakeBluetoothController
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.junit.MockitoJUnit
import org.mockito.junit.MockitoRule
import org.mockito.kotlin.whenever

@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@EnabledOnRavenwood
@RunWith(AndroidJUnit4::class)
class HearingDevicesTileDataInteractorTest : SysuiTestCase() {
    private val kosmos = Kosmos()
    private val testScope = kosmos.testScope
    private val testUser = UserHandle.of(1)

    private val controller = kosmos.fakeBluetoothController
    private lateinit var underTest: HearingDevicesTileDataInteractor

    @Rule @JvmField val mockitoRule: MockitoRule = MockitoJUnit.rule()
    @Mock private lateinit var checker: HearingDevicesChecker

    @Before
    fun setup() {
        underTest = HearingDevicesTileDataInteractor(testScope.testScheduler, controller, checker)
    }

    @EnableFlags(Flags.FLAG_HEARING_AIDS_QS_TILE_DIALOG)
    @Test
    fun availability_flagEnabled_returnTrue() =
        testScope.runTest {
            val availability by collectLastValue(underTest.availability(testUser))

            assertThat(availability).isTrue()
        }

    @DisableFlags(Flags.FLAG_HEARING_AIDS_QS_TILE_DIALOG)
    @Test
    fun availability_flagDisabled_returnFalse() =
        testScope.runTest {
            val availability by collectLastValue(underTest.availability(testUser))

            assertThat(availability).isFalse()
        }

    @Test
    fun tileData_bluetoothStateChanged_dataMatchesChecker() =
        testScope.runTest {
            val flowValues: List<HearingDevicesTileModel> by
                collectValues(
                    underTest.tileData(testUser, flowOf(DataUpdateTrigger.InitialRequest))
                )
            runCurrent()
            assertThat(flowValues.size).isEqualTo(1) // from addCallback in setup()

            whenever(checker.isAnyPairedHearingDevice).thenReturn(false)
            whenever(checker.isAnyActiveHearingDevice).thenReturn(false)
            controller.isBluetoothEnabled = false
            runCurrent()
            assertThat(flowValues.size).isEqualTo(1) // model unchanged, no new flow value

            whenever(checker.isAnyPairedHearingDevice).thenReturn(true)
            whenever(checker.isAnyActiveHearingDevice).thenReturn(false)
            controller.isBluetoothEnabled = true
            runCurrent()
            assertThat(flowValues.size).isEqualTo(2)

            whenever(checker.isAnyPairedHearingDevice).thenReturn(true)
            whenever(checker.isAnyActiveHearingDevice).thenReturn(true)
            controller.isBluetoothEnabled = true
            runCurrent()
            assertThat(flowValues.size).isEqualTo(3)

            assertThat(flowValues.map { it.isAnyPairedHearingDevice })
                .containsExactly(false, true, true)
                .inOrder()
            assertThat(flowValues.map { it.isAnyActiveHearingDevice })
                .containsExactly(false, false, true)
                .inOrder()
        }

    @Test
    fun tileData_bluetoothDeviceChanged_dataMatchesChecker() =
        testScope.runTest {
            val flowValues: List<HearingDevicesTileModel> by
                collectValues(
                    underTest.tileData(testUser, flowOf(DataUpdateTrigger.InitialRequest))
                )
            runCurrent()
            assertThat(flowValues.size).isEqualTo(1) // from addCallback in setup()

            whenever(checker.isAnyPairedHearingDevice).thenReturn(false)
            whenever(checker.isAnyActiveHearingDevice).thenReturn(false)
            controller.onBluetoothDevicesChanged()
            runCurrent()
            assertThat(flowValues.size).isEqualTo(1) // model unchanged, no new flow value

            whenever(checker.isAnyPairedHearingDevice).thenReturn(true)
            whenever(checker.isAnyActiveHearingDevice).thenReturn(false)
            controller.onBluetoothDevicesChanged()
            runCurrent()
            assertThat(flowValues.size).isEqualTo(2)

            whenever(checker.isAnyPairedHearingDevice).thenReturn(true)
            whenever(checker.isAnyActiveHearingDevice).thenReturn(true)
            controller.onBluetoothDevicesChanged()
            runCurrent()
            assertThat(flowValues.size).isEqualTo(3)

            assertThat(flowValues.map { it.isAnyPairedHearingDevice })
                .containsExactly(false, true, true)
                .inOrder()
            assertThat(flowValues.map { it.isAnyActiveHearingDevice })
                .containsExactly(false, false, true)
                .inOrder()
        }
}
+96 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.android.systemui.qs.tiles.impl.hearingdevices.domain.interactor

import android.platform.test.annotations.EnabledOnRavenwood
import android.provider.Settings
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.accessibility.hearingaid.HearingDevicesDialogManager
import com.android.systemui.accessibility.hearingaid.HearingDevicesUiEventLogger.Companion.LAUNCH_SOURCE_QS_TILE
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject
import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
import com.android.systemui.qs.tiles.impl.hearingdevices.domain.model.HearingDevicesTileModel
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.eq
import org.mockito.Mock
import org.mockito.junit.MockitoJUnit
import org.mockito.junit.MockitoRule
import org.mockito.kotlin.anyOrNull
import org.mockito.kotlin.verify

@SmallTest
@EnabledOnRavenwood
@RunWith(AndroidJUnit4::class)
class HearingDevicesTileUserActionInteractorTest : SysuiTestCase() {
    private val kosmos = Kosmos()
    private val testScope = kosmos.testScope
    private val inputHandler = FakeQSTileIntentUserInputHandler()

    private lateinit var underTest: HearingDevicesTileUserActionInteractor

    @Rule @JvmField val mockitoRule: MockitoRule = MockitoJUnit.rule()
    @Mock private lateinit var dialogManager: HearingDevicesDialogManager

    @Before
    fun setUp() {
        underTest =
            HearingDevicesTileUserActionInteractor(
                testScope.coroutineContext,
                inputHandler,
                dialogManager,
            )
    }

    @Test
    fun handleClick_launchDialog() =
        testScope.runTest {
            val input =
                HearingDevicesTileModel(
                    isAnyActiveHearingDevice = true,
                    isAnyPairedHearingDevice = true,
                )

            underTest.handleInput(QSTileInputTestKtx.click(input))

            verify(dialogManager).showDialog(anyOrNull(), eq(LAUNCH_SOURCE_QS_TILE))
        }

    @Test
    fun handleLongClick_launchSettings() =
        testScope.runTest {
            val input =
                HearingDevicesTileModel(
                    isAnyActiveHearingDevice = true,
                    isAnyPairedHearingDevice = true,
                )

            underTest.handleInput(QSTileInputTestKtx.longClick(input))

            QSTileIntentUserInputHandlerSubject.assertThat(inputHandler).handledOneIntentInput {
                assertThat(it.intent.action).isEqualTo(Settings.ACTION_HEARING_DEVICES_SETTINGS)
            }
        }
}
+55 −21
Original line number Diff line number Diff line
@@ -39,6 +39,10 @@ import com.android.systemui.qs.tiles.impl.fontscaling.domain.FontScalingTileMapp
import com.android.systemui.qs.tiles.impl.fontscaling.domain.interactor.FontScalingTileDataInteractor
import com.android.systemui.qs.tiles.impl.fontscaling.domain.interactor.FontScalingTileUserActionInteractor
import com.android.systemui.qs.tiles.impl.fontscaling.domain.model.FontScalingTileModel
import com.android.systemui.qs.tiles.impl.hearingdevices.domain.HearingDevicesTileMapper
import com.android.systemui.qs.tiles.impl.hearingdevices.domain.interactor.HearingDevicesTileDataInteractor
import com.android.systemui.qs.tiles.impl.hearingdevices.domain.interactor.HearingDevicesTileUserActionInteractor
import com.android.systemui.qs.tiles.impl.hearingdevices.domain.model.HearingDevicesTileModel
import com.android.systemui.qs.tiles.impl.inversion.domain.ColorInversionTileMapper
import com.android.systemui.qs.tiles.impl.inversion.domain.interactor.ColorInversionTileDataInteractor
import com.android.systemui.qs.tiles.impl.inversion.domain.interactor.ColorInversionUserActionInteractor
@@ -159,6 +163,13 @@ interface QSAccessibilityModule {
        impl: NightDisplayTileDataInteractor
    ): QSTileAvailabilityInteractor

    @Binds
    @IntoMap
    @StringKey(HEARING_DEVICES_TILE_SPEC)
    fun provideHearingDevicesAvailabilityInteractor(
        impl: HearingDevicesTileDataInteractor
    ): QSTileAvailabilityInteractor

    companion object {
        const val COLOR_CORRECTION_TILE_SPEC = "color_correction"
        const val COLOR_INVERSION_TILE_SPEC = "inversion"
@@ -191,7 +202,7 @@ interface QSAccessibilityModule {
            factory: QSTileViewModelFactory.Static<ColorCorrectionTileModel>,
            mapper: ColorCorrectionTileMapper,
            stateInteractor: ColorCorrectionTileDataInteractor,
            userActionInteractor: ColorCorrectionUserActionInteractor
            userActionInteractor: ColorCorrectionUserActionInteractor,
        ): QSTileViewModel =
            factory.create(
                TileSpec.create(COLOR_CORRECTION_TILE_SPEC),
@@ -223,7 +234,7 @@ interface QSAccessibilityModule {
            factory: QSTileViewModelFactory.Static<ColorInversionTileModel>,
            mapper: ColorInversionTileMapper,
            stateInteractor: ColorInversionTileDataInteractor,
            userActionInteractor: ColorInversionUserActionInteractor
            userActionInteractor: ColorInversionUserActionInteractor,
        ): QSTileViewModel =
            factory.create(
                TileSpec.create(COLOR_INVERSION_TILE_SPEC),
@@ -255,7 +266,7 @@ interface QSAccessibilityModule {
            factory: QSTileViewModelFactory.Static<FontScalingTileModel>,
            mapper: FontScalingTileMapper,
            stateInteractor: FontScalingTileDataInteractor,
            userActionInteractor: FontScalingTileUserActionInteractor
            userActionInteractor: FontScalingTileUserActionInteractor,
        ): QSTileViewModel =
            factory.create(
                TileSpec.create(FONT_SCALING_TILE_SPEC),
@@ -279,21 +290,6 @@ interface QSAccessibilityModule {
                category = TileCategory.DISPLAY,
            )

        @Provides
        @IntoMap
        @StringKey(HEARING_DEVICES_TILE_SPEC)
        fun provideHearingDevicesTileConfig(uiEventLogger: QsEventLogger): QSTileConfig =
            QSTileConfig(
                tileSpec = TileSpec.create(HEARING_DEVICES_TILE_SPEC),
                uiConfig =
                    QSTileUIConfig.Resource(
                        iconRes = R.drawable.qs_hearing_devices_icon,
                        labelRes = R.string.quick_settings_hearing_devices_label,
                    ),
                instanceId = uiEventLogger.getNewInstanceId(),
                category = TileCategory.ACCESSIBILITY,
            )

        /**
         * Inject Reduce Bright Colors Tile into tileViewModelMap in QSModule. The tile is hidden
         * behind a flag.
@@ -305,7 +301,7 @@ interface QSAccessibilityModule {
            factory: QSTileViewModelFactory.Static<ReduceBrightColorsTileModel>,
            mapper: ReduceBrightColorsTileMapper,
            stateInteractor: ReduceBrightColorsTileDataInteractor,
            userActionInteractor: ReduceBrightColorsTileUserActionInteractor
            userActionInteractor: ReduceBrightColorsTileUserActionInteractor,
        ): QSTileViewModel =
            if (Flags.qsNewTilesFuture())
                factory.create(
@@ -339,7 +335,7 @@ interface QSAccessibilityModule {
            factory: QSTileViewModelFactory.Static<OneHandedModeTileModel>,
            mapper: OneHandedModeTileMapper,
            stateInteractor: OneHandedModeTileDataInteractor,
            userActionInteractor: OneHandedModeTileUserActionInteractor
            userActionInteractor: OneHandedModeTileUserActionInteractor,
        ): QSTileViewModel =
            if (Flags.qsNewTilesFuture())
                factory.create(
@@ -376,7 +372,7 @@ interface QSAccessibilityModule {
            factory: QSTileViewModelFactory.Static<NightDisplayTileModel>,
            mapper: NightDisplayTileMapper,
            stateInteractor: NightDisplayTileDataInteractor,
            userActionInteractor: NightDisplayTileUserActionInteractor
            userActionInteractor: NightDisplayTileUserActionInteractor,
        ): QSTileViewModel =
            if (Flags.qsNewTilesFuture())
                factory.create(
@@ -386,5 +382,43 @@ interface QSAccessibilityModule {
                    mapper,
                )
            else StubQSTileViewModel

        @Provides
        @IntoMap
        @StringKey(HEARING_DEVICES_TILE_SPEC)
        fun provideHearingDevicesTileConfig(uiEventLogger: QsEventLogger): QSTileConfig =
            QSTileConfig(
                tileSpec = TileSpec.create(HEARING_DEVICES_TILE_SPEC),
                uiConfig =
                    QSTileUIConfig.Resource(
                        iconRes = R.drawable.qs_hearing_devices_icon,
                        labelRes = R.string.quick_settings_hearing_devices_label,
                    ),
                instanceId = uiEventLogger.getNewInstanceId(),
                category = TileCategory.ACCESSIBILITY,
            )

        /**
         * Inject HearingDevices Tile into tileViewModelMap in QSModule. The tile is hidden behind a
         * flag.
         */
        @Provides
        @IntoMap
        @StringKey(HEARING_DEVICES_TILE_SPEC)
        fun provideHearingDevicesTileViewModel(
            factory: QSTileViewModelFactory.Static<HearingDevicesTileModel>,
            mapper: HearingDevicesTileMapper,
            stateInteractor: HearingDevicesTileDataInteractor,
            userActionInteractor: HearingDevicesTileUserActionInteractor,
        ): QSTileViewModel {
            return if (Flags.hearingAidsQsTileDialog() && Flags.qsNewTilesFuture()) {
                factory.create(
                    TileSpec.create(HEARING_DEVICES_TILE_SPEC),
                    userActionInteractor,
                    stateInteractor,
                    mapper,
                )
            } else StubQSTileViewModel
        }
    }
}
+59 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.systemui.qs.tiles.impl.hearingdevices.domain

import android.content.res.Resources
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
import com.android.systemui.qs.tiles.impl.hearingdevices.domain.model.HearingDevicesTileModel
import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
import javax.inject.Inject

/** Maps [HearingDevicesTileModel] to [QSTileState]. */
class HearingDevicesTileMapper
@Inject
constructor(@Main private val resources: Resources, private val theme: Resources.Theme) :
    QSTileDataToStateMapper<HearingDevicesTileModel> {

    override fun map(config: QSTileConfig, data: HearingDevicesTileModel): QSTileState =
        QSTileState.build(resources, theme, config.uiConfig) {
            label = resources.getString(R.string.quick_settings_hearing_devices_label)
            iconRes = R.drawable.qs_hearing_devices_icon
            val loadedIcon =
                Icon.Loaded(resources.getDrawable(iconRes!!, theme), contentDescription = null)
            icon = { loadedIcon }
            sideViewIcon = QSTileState.SideViewIcon.Chevron
            contentDescription = label
            if (data.isAnyActiveHearingDevice) {
                activationState = QSTileState.ActivationState.ACTIVE
                secondaryLabel =
                    resources.getString(R.string.quick_settings_hearing_devices_connected)
            } else if (data.isAnyPairedHearingDevice) {
                activationState = QSTileState.ActivationState.INACTIVE
                secondaryLabel =
                    resources.getString(R.string.quick_settings_hearing_devices_disconnected)
            } else {
                activationState = QSTileState.ActivationState.INACTIVE
                secondaryLabel = ""
            }
            supportedActions =
                setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK)
        }
}
Loading