Loading packages/SystemUI/multivalentTests/src/com/android/systemui/flashlight/ui/viewmodel/FlashlightSliderViewModelTest.kt +28 −1 Original line number Diff line number Diff line Loading @@ -31,6 +31,7 @@ import com.android.systemui.kosmos.runCurrent import com.android.systemui.kosmos.runTest import com.android.systemui.kosmos.testScope import com.android.systemui.lifecycle.activateIn import com.android.systemui.res.R import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import org.junit.Before Loading Loading @@ -160,7 +161,7 @@ class FlashlightSliderViewModelTest : SysuiTestCase() { } @Test fun updateInteractor_updatesState() = fun updateInteractor_updatesLevel() = kosmos.runTest { flashlightInteractor.setEnabled(true) runCurrent() Loading Loading @@ -196,6 +197,32 @@ class FlashlightSliderViewModelTest : SysuiTestCase() { assertThat(underTest.currentFlashlightLevel!!.level).isEqualTo(MAX_LEVEL) } @Test fun flashlightIsAdjustable_turnsTrueAfterInitialization() = kosmos.runTest { assertThat(underTest.isFlashlightAdjustable).isFalse() runCurrent() assertThat(underTest.isFlashlightAdjustable).isTrue() } @Test fun testCorrectFlashlightIconForDifferentPercentages() = kosmos.runTest { assertThat(FlashlightSliderViewModel.getIconForPercentage(0f)) .isEqualTo(R.drawable.vd_flashlight_off) assertThat(FlashlightSliderViewModel.getIconForPercentage(1f)) .isEqualTo(R.drawable.vd_flashlight_on) assertThat(FlashlightSliderViewModel.getIconForPercentage(99f)) .isEqualTo(R.drawable.vd_flashlight_on) assertThat(FlashlightSliderViewModel.getIconForPercentage(100f)) .isEqualTo(R.drawable.vd_flashlight_on) } private companion object { const val MAX_LEVEL = 45 const val DEFAULT_LEVEL = 21 Loading packages/SystemUI/res/drawable/vd_flashlight_off.xml 0 → 100644 +40 −0 Original line number Diff line number Diff line <?xml version="1.0" encoding="utf-8"?><!-- ~ 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. --> <vector xmlns:android="http://schemas.android.com/apk/res/android" android:name="root_vector" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24"> <group android:name="root_group" android:pivotX="32" android:pivotY="32" android:scaleX="0.7" android:scaleY="0.7" android:translateX="-20" android:translateY="-20"> <path android:name="top" android:fillColor="#ffffff" android:pathData="M24 21.7182V23.1202H40.0533V21.7182C40.0533 21.0061 39.7969 20.4053 39.2841 19.9157C38.7936 19.4039 38.1916 19.1479 37.4781 19.1479H26.5752C25.8617 19.1479 25.2486 19.4039 24.7358 19.9157C24.2453 20.4053 24 21.0061 24 21.7182Z" /> <path android:name="body" android:fillColor="#ffffff" android:fillType="evenOdd" android:pathData="@string/path_flashlight_off" /> </group> </vector> No newline at end of file packages/SystemUI/res/drawable/vd_flashlight_on.xml 0 → 100644 +40 −0 Original line number Diff line number Diff line <?xml version="1.0" encoding="utf-8"?><!-- ~ 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. --> <vector xmlns:android="http://schemas.android.com/apk/res/android" android:name="root_vector" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24"> <group android:name="root_group" android:pivotX="32" android:pivotY="32" android:scaleX="0.7" android:scaleY="0.7" android:translateX="-20" android:translateY="-20"> <path android:name="top" android:fillColor="#ffffff" android:pathData="M24 21.7182V23.1202H40.0533V21.7182C40.0533 21.0061 39.7969 20.4053 39.2841 19.9157C38.7936 19.4039 38.1916 19.1479 37.4781 19.1479H26.5752C25.8617 19.1479 25.2486 19.4039 24.7358 19.9157C24.2453 20.4053 24 21.0061 24 21.7182Z" /> <path android:name="body" android:fillColor="#ffffff" android:fillType="evenOdd" android:pathData="@string/path_flashlight_on" /> </group> </vector> No newline at end of file packages/SystemUI/src/com/android/systemui/flashlight/ui/composable/FlashlightSlider.kt +8 −92 Original line number Diff line number Diff line Loading @@ -16,88 +16,13 @@ package com.android.systemui.flashlight.ui.composable import androidx.compose.animation.core.animateFloatAsState import androidx.compose.foundation.gestures.Orientation import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi import androidx.compose.material3.Slider import androidx.compose.material3.SliderDefaults import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.unit.DpSize import androidx.compose.ui.unit.dp import com.android.systemui.compose.modifiers.sysuiResTag import com.android.systemui.flashlight.ui.viewmodel.FlashlightSliderViewModel import com.android.systemui.haptics.slider.SeekableSliderTrackerConfig import com.android.systemui.haptics.slider.SliderHapticFeedbackConfig import com.android.systemui.haptics.slider.compose.ui.SliderHapticsViewModel import com.android.systemui.lifecycle.rememberViewModel @OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class) @Composable fun FlashlightSlider( levelValue: Int, valueRange: IntRange, hapticsViewModelFactory: SliderHapticsViewModel.Factory, onLevelChanged: (Int) -> Unit, ) { var value by remember(levelValue) { mutableIntStateOf(levelValue) } val animatedValue by animateFloatAsState(targetValue = value.toFloat(), label = "FlashlightSliderAnimatedValue") val interactionSource = remember { MutableInteractionSource() } val floatValueRange = valueRange.first.toFloat()..valueRange.last.toFloat() val hapticsViewModel: SliderHapticsViewModel = rememberViewModel(traceName = "SliderHapticsViewModel") { hapticsViewModelFactory.create( interactionSource, floatValueRange, Orientation.Horizontal, SliderHapticFeedbackConfig( maxVelocityToScale = 1f /* slider progress(from 0 to 1) per sec */ ), SeekableSliderTrackerConfig(), ) } Slider( value = animatedValue, valueRange = floatValueRange, onValueChange = { hapticsViewModel.onValueChange(it) value = it.toInt() onLevelChanged(it.toInt()) }, onValueChangeFinished = { hapticsViewModel.onValueChangeEnded() }, interactionSource = interactionSource, thumb = { SliderDefaults.Thumb( interactionSource = interactionSource, thumbSize = DpSize(Constants.ThumbWidth, Constants.ThumbHeight), ) }, track = { sliderState -> SliderDefaults.Track( sliderState = sliderState, modifier = Modifier.height(40.dp), trackCornerSize = Constants.SliderTrackRoundedCorner, trackInsideCornerSize = Constants.TrackInsideCornerSize, drawStopIndicator = null, thumbTrackGapSize = Constants.ThumbTrackGapSize, ) }, ) } import com.android.systemui.util.ui.compose.DualIconSlider @Composable fun FlashlightSliderContainer(viewModel: FlashlightSliderViewModel, modifier: Modifier = Modifier) { Loading @@ -106,27 +31,18 @@ fun FlashlightSliderContainer(viewModel: FlashlightSliderViewModel, modifier: Mo if (currentState.enabled) { currentState.level } else { // flashlight off Constants .POSITION_ZERO // even if the "level" has been reset to "default" on the backend 0 // even if the "level" has been reset to "default" on the backend } val maxLevel = currentState.max Box(modifier = modifier.fillMaxWidth().sysuiResTag("flashlight_slider")) { FlashlightSlider( DualIconSlider( levelValue = levelValue, valueRange = Constants.POSITION_ZERO..maxLevel, valueRange = 0..currentState.max, iconResProvider = FlashlightSliderViewModel::getIconForPercentage, imageLoader = viewModel::loadImage, hapticsViewModelFactory = viewModel.hapticsViewModelFactory, onLevelChanged = { viewModel.setFlashlightLevel(it) }, onDrag = viewModel::setFlashlightLevel, isEnabled = viewModel.isFlashlightAdjustable, ) } } private object Constants { val SliderTrackRoundedCorner = 12.dp val ThumbTrackGapSize = 6.dp val ThumbHeight = 52.dp val ThumbWidth = 4.dp val TrackInsideCornerSize = 2.dp const val POSITION_ZERO = 0 } packages/SystemUI/src/com/android/systemui/flashlight/ui/viewmodel/FlashlightSliderViewModel.kt +29 −2 Original line number Diff line number Diff line Loading @@ -16,15 +16,22 @@ package com.android.systemui.flashlight.ui.viewmodel import android.content.Context import androidx.annotation.DrawableRes import androidx.annotation.FloatRange import androidx.compose.runtime.getValue import com.android.internal.logging.UiEventLogger import com.android.systemui.common.shared.model.Icon import com.android.systemui.common.shared.model.asIcon import com.android.systemui.flashlight.domain.interactor.FlashlightInteractor import com.android.systemui.flashlight.shared.logger.FlashlightLogger import com.android.systemui.flashlight.shared.logger.FlashlightUiEvent import com.android.systemui.flashlight.shared.model.FlashlightModel import com.android.systemui.graphics.ImageLoader import com.android.systemui.haptics.slider.compose.ui.SliderHapticsViewModel import com.android.systemui.lifecycle.ExclusiveActivatable import com.android.systemui.lifecycle.Hydrator import com.android.systemui.res.R import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import kotlinx.coroutines.flow.filterIsInstance Loading @@ -38,6 +45,7 @@ constructor( private val flashlightInteractor: FlashlightInteractor, private val logger: FlashlightLogger, private val uiEventLogger: UiEventLogger, private val imageLoader: ImageLoader, ) : ExclusiveActivatable() { private val hydrator = Hydrator("FlashlightSliderViewModel.hydrator") Loading @@ -45,11 +53,10 @@ constructor( hydrator.hydratedStateOf( "currentFlashlightLevel", flashlightInteractor.state.value as? FlashlightModel.Available.Level, // TODO (b/413736768): disable slider if flashlight becomes un-adjustable mid-slide! flashlightInteractor.state.filterIsInstance(FlashlightModel.Available.Level::class), ) private val isFlashlightAdjustable: Boolean by val isFlashlightAdjustable: Boolean by hydrator.hydratedStateOf( "isFlashlightAdjustable", flashlightInteractor.state.value is FlashlightModel.Available.Level, Loading Loading @@ -82,8 +89,28 @@ constructor( } } suspend fun loadImage(@DrawableRes resId: Int, context: Context): Icon.Loaded { return imageLoader .loadDrawable( android.graphics.drawable.Icon.createWithResource(context, resId), maxHeight = 200, maxWidth = 200, )!! .asIcon(null, resId) } @AssistedFactory interface Factory { fun create(): FlashlightSliderViewModel } companion object { @DrawableRes fun getIconForPercentage(@FloatRange(0.0, 100.0) percentage: Float): Int { return when { percentage == 0f -> R.drawable.vd_flashlight_off else -> R.drawable.vd_flashlight_on } } } } Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/flashlight/ui/viewmodel/FlashlightSliderViewModelTest.kt +28 −1 Original line number Diff line number Diff line Loading @@ -31,6 +31,7 @@ import com.android.systemui.kosmos.runCurrent import com.android.systemui.kosmos.runTest import com.android.systemui.kosmos.testScope import com.android.systemui.lifecycle.activateIn import com.android.systemui.res.R import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import org.junit.Before Loading Loading @@ -160,7 +161,7 @@ class FlashlightSliderViewModelTest : SysuiTestCase() { } @Test fun updateInteractor_updatesState() = fun updateInteractor_updatesLevel() = kosmos.runTest { flashlightInteractor.setEnabled(true) runCurrent() Loading Loading @@ -196,6 +197,32 @@ class FlashlightSliderViewModelTest : SysuiTestCase() { assertThat(underTest.currentFlashlightLevel!!.level).isEqualTo(MAX_LEVEL) } @Test fun flashlightIsAdjustable_turnsTrueAfterInitialization() = kosmos.runTest { assertThat(underTest.isFlashlightAdjustable).isFalse() runCurrent() assertThat(underTest.isFlashlightAdjustable).isTrue() } @Test fun testCorrectFlashlightIconForDifferentPercentages() = kosmos.runTest { assertThat(FlashlightSliderViewModel.getIconForPercentage(0f)) .isEqualTo(R.drawable.vd_flashlight_off) assertThat(FlashlightSliderViewModel.getIconForPercentage(1f)) .isEqualTo(R.drawable.vd_flashlight_on) assertThat(FlashlightSliderViewModel.getIconForPercentage(99f)) .isEqualTo(R.drawable.vd_flashlight_on) assertThat(FlashlightSliderViewModel.getIconForPercentage(100f)) .isEqualTo(R.drawable.vd_flashlight_on) } private companion object { const val MAX_LEVEL = 45 const val DEFAULT_LEVEL = 21 Loading
packages/SystemUI/res/drawable/vd_flashlight_off.xml 0 → 100644 +40 −0 Original line number Diff line number Diff line <?xml version="1.0" encoding="utf-8"?><!-- ~ 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. --> <vector xmlns:android="http://schemas.android.com/apk/res/android" android:name="root_vector" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24"> <group android:name="root_group" android:pivotX="32" android:pivotY="32" android:scaleX="0.7" android:scaleY="0.7" android:translateX="-20" android:translateY="-20"> <path android:name="top" android:fillColor="#ffffff" android:pathData="M24 21.7182V23.1202H40.0533V21.7182C40.0533 21.0061 39.7969 20.4053 39.2841 19.9157C38.7936 19.4039 38.1916 19.1479 37.4781 19.1479H26.5752C25.8617 19.1479 25.2486 19.4039 24.7358 19.9157C24.2453 20.4053 24 21.0061 24 21.7182Z" /> <path android:name="body" android:fillColor="#ffffff" android:fillType="evenOdd" android:pathData="@string/path_flashlight_off" /> </group> </vector> No newline at end of file
packages/SystemUI/res/drawable/vd_flashlight_on.xml 0 → 100644 +40 −0 Original line number Diff line number Diff line <?xml version="1.0" encoding="utf-8"?><!-- ~ 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. --> <vector xmlns:android="http://schemas.android.com/apk/res/android" android:name="root_vector" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24"> <group android:name="root_group" android:pivotX="32" android:pivotY="32" android:scaleX="0.7" android:scaleY="0.7" android:translateX="-20" android:translateY="-20"> <path android:name="top" android:fillColor="#ffffff" android:pathData="M24 21.7182V23.1202H40.0533V21.7182C40.0533 21.0061 39.7969 20.4053 39.2841 19.9157C38.7936 19.4039 38.1916 19.1479 37.4781 19.1479H26.5752C25.8617 19.1479 25.2486 19.4039 24.7358 19.9157C24.2453 20.4053 24 21.0061 24 21.7182Z" /> <path android:name="body" android:fillColor="#ffffff" android:fillType="evenOdd" android:pathData="@string/path_flashlight_on" /> </group> </vector> No newline at end of file
packages/SystemUI/src/com/android/systemui/flashlight/ui/composable/FlashlightSlider.kt +8 −92 Original line number Diff line number Diff line Loading @@ -16,88 +16,13 @@ package com.android.systemui.flashlight.ui.composable import androidx.compose.animation.core.animateFloatAsState import androidx.compose.foundation.gestures.Orientation import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi import androidx.compose.material3.Slider import androidx.compose.material3.SliderDefaults import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.unit.DpSize import androidx.compose.ui.unit.dp import com.android.systemui.compose.modifiers.sysuiResTag import com.android.systemui.flashlight.ui.viewmodel.FlashlightSliderViewModel import com.android.systemui.haptics.slider.SeekableSliderTrackerConfig import com.android.systemui.haptics.slider.SliderHapticFeedbackConfig import com.android.systemui.haptics.slider.compose.ui.SliderHapticsViewModel import com.android.systemui.lifecycle.rememberViewModel @OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class) @Composable fun FlashlightSlider( levelValue: Int, valueRange: IntRange, hapticsViewModelFactory: SliderHapticsViewModel.Factory, onLevelChanged: (Int) -> Unit, ) { var value by remember(levelValue) { mutableIntStateOf(levelValue) } val animatedValue by animateFloatAsState(targetValue = value.toFloat(), label = "FlashlightSliderAnimatedValue") val interactionSource = remember { MutableInteractionSource() } val floatValueRange = valueRange.first.toFloat()..valueRange.last.toFloat() val hapticsViewModel: SliderHapticsViewModel = rememberViewModel(traceName = "SliderHapticsViewModel") { hapticsViewModelFactory.create( interactionSource, floatValueRange, Orientation.Horizontal, SliderHapticFeedbackConfig( maxVelocityToScale = 1f /* slider progress(from 0 to 1) per sec */ ), SeekableSliderTrackerConfig(), ) } Slider( value = animatedValue, valueRange = floatValueRange, onValueChange = { hapticsViewModel.onValueChange(it) value = it.toInt() onLevelChanged(it.toInt()) }, onValueChangeFinished = { hapticsViewModel.onValueChangeEnded() }, interactionSource = interactionSource, thumb = { SliderDefaults.Thumb( interactionSource = interactionSource, thumbSize = DpSize(Constants.ThumbWidth, Constants.ThumbHeight), ) }, track = { sliderState -> SliderDefaults.Track( sliderState = sliderState, modifier = Modifier.height(40.dp), trackCornerSize = Constants.SliderTrackRoundedCorner, trackInsideCornerSize = Constants.TrackInsideCornerSize, drawStopIndicator = null, thumbTrackGapSize = Constants.ThumbTrackGapSize, ) }, ) } import com.android.systemui.util.ui.compose.DualIconSlider @Composable fun FlashlightSliderContainer(viewModel: FlashlightSliderViewModel, modifier: Modifier = Modifier) { Loading @@ -106,27 +31,18 @@ fun FlashlightSliderContainer(viewModel: FlashlightSliderViewModel, modifier: Mo if (currentState.enabled) { currentState.level } else { // flashlight off Constants .POSITION_ZERO // even if the "level" has been reset to "default" on the backend 0 // even if the "level" has been reset to "default" on the backend } val maxLevel = currentState.max Box(modifier = modifier.fillMaxWidth().sysuiResTag("flashlight_slider")) { FlashlightSlider( DualIconSlider( levelValue = levelValue, valueRange = Constants.POSITION_ZERO..maxLevel, valueRange = 0..currentState.max, iconResProvider = FlashlightSliderViewModel::getIconForPercentage, imageLoader = viewModel::loadImage, hapticsViewModelFactory = viewModel.hapticsViewModelFactory, onLevelChanged = { viewModel.setFlashlightLevel(it) }, onDrag = viewModel::setFlashlightLevel, isEnabled = viewModel.isFlashlightAdjustable, ) } } private object Constants { val SliderTrackRoundedCorner = 12.dp val ThumbTrackGapSize = 6.dp val ThumbHeight = 52.dp val ThumbWidth = 4.dp val TrackInsideCornerSize = 2.dp const val POSITION_ZERO = 0 }
packages/SystemUI/src/com/android/systemui/flashlight/ui/viewmodel/FlashlightSliderViewModel.kt +29 −2 Original line number Diff line number Diff line Loading @@ -16,15 +16,22 @@ package com.android.systemui.flashlight.ui.viewmodel import android.content.Context import androidx.annotation.DrawableRes import androidx.annotation.FloatRange import androidx.compose.runtime.getValue import com.android.internal.logging.UiEventLogger import com.android.systemui.common.shared.model.Icon import com.android.systemui.common.shared.model.asIcon import com.android.systemui.flashlight.domain.interactor.FlashlightInteractor import com.android.systemui.flashlight.shared.logger.FlashlightLogger import com.android.systemui.flashlight.shared.logger.FlashlightUiEvent import com.android.systemui.flashlight.shared.model.FlashlightModel import com.android.systemui.graphics.ImageLoader import com.android.systemui.haptics.slider.compose.ui.SliderHapticsViewModel import com.android.systemui.lifecycle.ExclusiveActivatable import com.android.systemui.lifecycle.Hydrator import com.android.systemui.res.R import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import kotlinx.coroutines.flow.filterIsInstance Loading @@ -38,6 +45,7 @@ constructor( private val flashlightInteractor: FlashlightInteractor, private val logger: FlashlightLogger, private val uiEventLogger: UiEventLogger, private val imageLoader: ImageLoader, ) : ExclusiveActivatable() { private val hydrator = Hydrator("FlashlightSliderViewModel.hydrator") Loading @@ -45,11 +53,10 @@ constructor( hydrator.hydratedStateOf( "currentFlashlightLevel", flashlightInteractor.state.value as? FlashlightModel.Available.Level, // TODO (b/413736768): disable slider if flashlight becomes un-adjustable mid-slide! flashlightInteractor.state.filterIsInstance(FlashlightModel.Available.Level::class), ) private val isFlashlightAdjustable: Boolean by val isFlashlightAdjustable: Boolean by hydrator.hydratedStateOf( "isFlashlightAdjustable", flashlightInteractor.state.value is FlashlightModel.Available.Level, Loading Loading @@ -82,8 +89,28 @@ constructor( } } suspend fun loadImage(@DrawableRes resId: Int, context: Context): Icon.Loaded { return imageLoader .loadDrawable( android.graphics.drawable.Icon.createWithResource(context, resId), maxHeight = 200, maxWidth = 200, )!! .asIcon(null, resId) } @AssistedFactory interface Factory { fun create(): FlashlightSliderViewModel } companion object { @DrawableRes fun getIconForPercentage(@FloatRange(0.0, 100.0) percentage: Float): Int { return when { percentage == 0f -> R.drawable.vd_flashlight_off else -> R.drawable.vd_flashlight_on } } } }