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

Commit 8dbd069c authored by amehfooz's avatar amehfooz
Browse files

[SB][ComposeIcons] Add Vibrate icon

Bug: 417522622
Test: VibrateIconViewModelTest
Flag: com.android.systemui.status_bar_system_status_icons_in_compose
Change-Id: Ie179e39655831965c8dcdab567e01c0f5b157082
parent abd37deb
Loading
Loading
Loading
Loading
+79 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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.statusbar.systemstatusicons.ringer.ui.viewmodel

import android.media.AudioManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.settingslib.volume.shared.model.RingerMode
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.testScope
import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.lifecycle.activateIn
import com.android.systemui.res.R
import com.android.systemui.testKosmos
import com.android.systemui.volume.data.repository.fakeAudioRepository
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith

@SmallTest
@RunWith(AndroidJUnit4::class)
class VibrateIconViewModelTest : SysuiTestCase() {

    private val kosmos = testKosmos().useUnconfinedTestDispatcher()
    private val underTest = kosmos.vibrateIconViewModelFactory.create()

    @Before
    fun setUp() {
        underTest.activateIn(kosmos.testScope)
    }

    @Test
    fun icon_ringerModeNormal_null() =
        kosmos.runTest {
            fakeAudioRepository.setRingerMode(RingerMode(AudioManager.RINGER_MODE_NORMAL))

            assertThat(underTest.icon).isNull()
        }

    @Test
    fun icon_ringerModeSilent_null() =
        kosmos.runTest {
            fakeAudioRepository.setRingerMode(RingerMode(AudioManager.RINGER_MODE_SILENT))

            assertThat(underTest.icon).isNull()
        }

    @Test
    fun icon_ringerModeVibrate_isShown() =
        kosmos.runTest {
            fakeAudioRepository.setRingerMode(RingerMode(AudioManager.RINGER_MODE_VIBRATE))

            val expected =
                Icon.Resource(
                    R.drawable.ic_volume_ringer_vibrate,
                    ContentDescription.Resource(R.string.accessibility_ringer_vibrate),
                )

            assertThat(underTest.icon).isEqualTo(expected)
        }
}
+73 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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.statusbar.systemstatusicons.ringer.ui.viewmodel

import android.media.AudioManager
import androidx.compose.runtime.getValue
import com.android.settingslib.volume.domain.interactor.AudioVolumeInteractor
import com.android.settingslib.volume.shared.model.RingerMode
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.lifecycle.ExclusiveActivatable
import com.android.systemui.lifecycle.Hydrator
import com.android.systemui.res.R
import com.android.systemui.statusbar.systemstatusicons.SystemStatusIconsInCompose
import com.android.systemui.statusbar.systemstatusicons.ui.viewmodel.SystemStatusIconViewModel
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.flow.map

/**
 * View model for the vibrate system status icon. Emits an icon when the ringer is set to vibrate.
 * Null icon otherwise.
 */
class VibrateIconViewModel @AssistedInject constructor(interactor: AudioVolumeInteractor) :
    SystemStatusIconViewModel, ExclusiveActivatable() {

    init {
        SystemStatusIconsInCompose.expectInNewMode()
    }

    private val hydrator = Hydrator("VibrateIconViewModel.hydrator")

    override val icon: Icon? by
        hydrator.hydratedStateOf(
            traceName = "SystemStatus.vibrateIcon",
            initialValue = null,
            source = interactor.ringerMode.map { it.toUiState() },
        )

    override suspend fun onActivated(): Nothing {
        hydrator.activate()
    }

    fun RingerMode.toUiState(): Icon? =
        if (this.value == AudioManager.RINGER_MODE_VIBRATE) {
            Icon.Resource(
                res = R.drawable.ic_volume_ringer_vibrate,
                contentDescription =
                    ContentDescription.Resource(R.string.accessibility_ringer_vibrate),
            )
        } else {
            null
        }

    @AssistedFactory
    interface Factory {
        fun create(): VibrateIconViewModel
    }
}
+5 −1
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import com.android.systemui.statusbar.systemstatusicons.airplane.ui.viewmodel.Ai
import com.android.systemui.statusbar.systemstatusicons.bluetooth.ui.viewmodel.BluetoothIconViewModel
import com.android.systemui.statusbar.systemstatusicons.ethernet.ui.viewmodel.EthernetIconViewModel
import com.android.systemui.statusbar.systemstatusicons.ringer.ui.viewmodel.MuteIconViewModel
import com.android.systemui.statusbar.systemstatusicons.ringer.ui.viewmodel.VibrateIconViewModel
import com.android.systemui.statusbar.systemstatusicons.zenmode.ui.viewmodel.ZenModeIconViewModel
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
@@ -44,6 +45,7 @@ constructor(
    ethernetIconViewModelFactory: EthernetIconViewModel.Factory,
    zenModeIconViewModelFactory: ZenModeIconViewModel.Factory,
    muteIconViewModelFactory: MuteIconViewModel.Factory,
    vibrateIconViewModelFactory: VibrateIconViewModel.Factory,
) : ExclusiveActivatable() {

    init {
@@ -55,9 +57,10 @@ constructor(
    private val bluetoothIcon by lazy { bluetoothIconViewModelFactory.create() }
    private val zenModeIcon by lazy { zenModeIconViewModelFactory.create() }
    private val muteIcon by lazy { muteIconViewModelFactory.create() }
    private val vibrateIcon by lazy { vibrateIconViewModelFactory.create() }

    private val iconViewModels: List<SystemStatusIconViewModel> by lazy {
        listOf(bluetoothIcon, zenModeIcon, muteIcon, ethernetIcon, airplaneModeIcon)
        listOf(bluetoothIcon, zenModeIcon, vibrateIcon, muteIcon, ethernetIcon, airplaneModeIcon)
    }

    val icons: List<Icon>
@@ -70,6 +73,7 @@ constructor(
            launch { bluetoothIcon.activate() }
            launch { airplaneModeIcon.activate() }
            launch { muteIcon.activate() }
            launch { vibrateIcon.activate() }
        }
        awaitCancellation()
    }
+30 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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.statusbar.systemstatusicons.ringer.ui.viewmodel

import com.android.systemui.kosmos.Kosmos
import com.android.systemui.volume.domain.interactor.audioVolumeInteractor

private val Kosmos.vibrateIconViewModel: VibrateIconViewModel by
    Kosmos.Fixture { VibrateIconViewModel(audioVolumeInteractor) }

val Kosmos.vibrateIconViewModelFactory: VibrateIconViewModel.Factory by
    Kosmos.Fixture {
        object : VibrateIconViewModel.Factory {
            override fun create(): VibrateIconViewModel = vibrateIconViewModel
        }
    }
+2 −0
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import com.android.systemui.statusbar.systemstatusicons.airplane.ui.viewmodel.ai
import com.android.systemui.statusbar.systemstatusicons.bluetooth.ui.viewmodel.bluetoothIconViewModelFactory
import com.android.systemui.statusbar.systemstatusicons.ethernet.ui.viewmodel.ethernetIconViewModelFactory
import com.android.systemui.statusbar.systemstatusicons.ringer.ui.viewmodel.muteIconViewModelFactory
import com.android.systemui.statusbar.systemstatusicons.ringer.ui.viewmodel.vibrateIconViewModelFactory
import com.android.systemui.statusbar.systemstatusicons.zenmode.ui.viewmodel.zenModeIconViewModelFactory

val Kosmos.systemStatusIconsViewModel by
@@ -31,6 +32,7 @@ val Kosmos.systemStatusIconsViewModel by
            ethernetIconViewModelFactory = ethernetIconViewModelFactory,
            zenModeIconViewModelFactory = zenModeIconViewModelFactory,
            muteIconViewModelFactory = muteIconViewModelFactory,
            vibrateIconViewModelFactory = vibrateIconViewModelFactory,
        )
    }