Loading packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfig.kt +61 −59 Original line number Diff line number Diff line Loading @@ -21,19 +21,21 @@ import android.content.Context import com.android.systemui.R import com.android.systemui.animation.Expandable import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow import com.android.systemui.common.shared.model.ContentDescription import com.android.systemui.common.shared.model.Icon import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.keyguard.shared.quickaffordance.ActivationState import com.android.systemui.statusbar.policy.FlashlightController import javax.inject.Inject import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow import javax.inject.Inject @SysUISingleton class FlashlightQuickAffordanceConfig @Inject constructor( class FlashlightQuickAffordanceConfig @Inject constructor( @Application private val context: Context, private val flashlightController: FlashlightController, ) : KeyguardQuickAffordanceConfig { Loading @@ -46,7 +48,7 @@ class FlashlightQuickAffordanceConfig @Inject constructor( override fun toLockScreenState(): KeyguardQuickAffordanceConfig.LockScreenState = KeyguardQuickAffordanceConfig.LockScreenState.Visible( Icon.Resource( R.drawable.ic_flashlight_on, R.drawable.qs_flashlight_icon_on, ContentDescription.Resource(R.string.quick_settings_flashlight_label) ), ActivationState.Active Loading @@ -57,7 +59,7 @@ class FlashlightQuickAffordanceConfig @Inject constructor( override fun toLockScreenState(): KeyguardQuickAffordanceConfig.LockScreenState = KeyguardQuickAffordanceConfig.LockScreenState.Visible( Icon.Resource( R.drawable.ic_flashlight_off, R.drawable.qs_flashlight_icon_off, ContentDescription.Resource(R.string.quick_settings_flashlight_label) ), ActivationState.Inactive Loading @@ -77,15 +79,12 @@ class FlashlightQuickAffordanceConfig @Inject constructor( get() = context.getString(R.string.quick_settings_flashlight_label) override val pickerIconResourceId: Int get() = if (flashlightController.isEnabled) { R.drawable.ic_flashlight_on } else { R.drawable.ic_flashlight_off } get() = R.drawable.ic_flashlight_off override val lockScreenState: Flow<KeyguardQuickAffordanceConfig.LockScreenState> = conflatedCallbackFlow { val flashlightCallback = object : FlashlightController.FlashlightListener { val flashlightCallback = object : FlashlightController.FlashlightListener { override fun onFlashlightChanged(enabled: Boolean) { trySendWithFailureLogging( if (enabled) { Loading @@ -98,7 +97,10 @@ class FlashlightQuickAffordanceConfig @Inject constructor( } override fun onFlashlightError() { trySendWithFailureLogging(FlashlightState.OffAvailable.toLockScreenState(), TAG) trySendWithFailureLogging( FlashlightState.OffAvailable.toLockScreenState(), TAG ) } override fun onFlashlightAvailabilityChanged(available: Boolean) { Loading @@ -119,15 +121,15 @@ class FlashlightQuickAffordanceConfig @Inject constructor( flashlightController.addCallback(flashlightCallback) awaitClose { flashlightController.removeCallback(flashlightCallback) } awaitClose { flashlightController.removeCallback(flashlightCallback) } } override fun onTriggered(expandable: Expandable?): KeyguardQuickAffordanceConfig.OnTriggeredResult { flashlightController .setFlashlight(flashlightController.isAvailable && !flashlightController.isEnabled) override fun onTriggered( expandable: Expandable? ): KeyguardQuickAffordanceConfig.OnTriggeredResult { flashlightController.setFlashlight( flashlightController.isAvailable && !flashlightController.isEnabled ) return KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled } Loading packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt +26 −4 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.systemui.keyguard.ui.binder import android.graphics.drawable.Animatable2 import android.util.Size import android.util.TypedValue import android.view.View Loading @@ -27,12 +28,11 @@ import androidx.core.view.isVisible import androidx.core.view.updateLayoutParams import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle import com.android.keyguard.KeyguardUpdateMonitor import com.android.keyguard.LockIconViewController import com.android.settingslib.Utils import com.android.systemui.R import com.android.systemui.animation.Expandable import com.android.systemui.animation.Interpolators import com.android.systemui.common.shared.model.Icon import com.android.systemui.common.ui.binder.IconViewBinder import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordanceViewModel Loading Loading @@ -73,7 +73,8 @@ object KeyguardBottomAreaViewBinder { fun onConfigurationChanged() /** * Returns whether the keyguard bottom area should be constrained to the top of the lock icon * Returns whether the keyguard bottom area should be constrained to the top of the lock * icon */ fun shouldConstrainToTopOfLockIcon(): Boolean } Loading Loading @@ -248,6 +249,27 @@ object KeyguardBottomAreaViewBinder { IconViewBinder.bind(viewModel.icon, view) (view.drawable as? Animatable2)?.let { animatable -> (viewModel.icon as? Icon.Resource)?.res?.let { iconResourceId -> // Always start the animation (we do call stop() below, if we need to skip it). animatable.start() if (view.tag != iconResourceId) { // Here when we haven't run the animation on a previous update. // // Save the resource ID for next time, so we know not to re-animate the same // animation again. view.tag = iconResourceId } else { // Here when we've already done this animation on a previous update and want to // skip directly to the final frame of the animation to avoid running it. // // By calling stop after start, we go to the final frame of the animation. animatable.stop() } } } view.isActivated = viewModel.isActivated view.drawable.setTint( Utils.getColorAttrDefaultColor( Loading packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfigTest.kt +65 −43 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ import com.android.systemui.keyguard.shared.quickaffordance.ActivationState import com.android.systemui.statusbar.policy.FlashlightController import com.android.systemui.utils.leaks.FakeFlashlightController import com.android.systemui.utils.leaks.LeakCheckedTest import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.toList import kotlinx.coroutines.launch import kotlinx.coroutines.test.UnconfinedTestDispatcher Loading @@ -38,6 +39,7 @@ import org.junit.runners.JUnit4 import org.mockito.Mock import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest @RunWith(JUnit4::class) class FlashlightQuickAffordanceConfigTest : LeakCheckedTest() { Loading @@ -51,7 +53,9 @@ class FlashlightQuickAffordanceConfigTest : LeakCheckedTest() { injectLeakCheckedDependency(FlashlightController::class.java) MockitoAnnotations.initMocks(this) flashlightController = SysuiLeakCheck().getLeakChecker(FlashlightController::class.java) as FakeFlashlightController flashlightController = SysuiLeakCheck().getLeakChecker(FlashlightController::class.java) as FakeFlashlightController underTest = FlashlightQuickAffordanceConfig(context, flashlightController) } Loading @@ -69,8 +73,12 @@ class FlashlightQuickAffordanceConfigTest : LeakCheckedTest() { // then assertTrue(lastValue is KeyguardQuickAffordanceConfig.LockScreenState.Visible) assertEquals(R.drawable.ic_flashlight_on, ((lastValue as KeyguardQuickAffordanceConfig.LockScreenState.Visible).icon as? Icon.Resource)?.res) assertEquals( R.drawable.qs_flashlight_icon_on, ((lastValue as KeyguardQuickAffordanceConfig.LockScreenState.Visible).icon as? Icon.Resource) ?.res ) job.cancel() } Loading @@ -88,8 +96,12 @@ class FlashlightQuickAffordanceConfigTest : LeakCheckedTest() { // then assertTrue(lastValue is KeyguardQuickAffordanceConfig.LockScreenState.Visible) assertEquals(R.drawable.ic_flashlight_off, ((lastValue as KeyguardQuickAffordanceConfig.LockScreenState.Visible).icon as? Icon.Resource)?.res) assertEquals( R.drawable.qs_flashlight_icon_off, ((lastValue as KeyguardQuickAffordanceConfig.LockScreenState.Visible).icon as? Icon.Resource) ?.res ) job.cancel() } Loading @@ -107,8 +119,12 @@ class FlashlightQuickAffordanceConfigTest : LeakCheckedTest() { // then assertTrue(lastValue is KeyguardQuickAffordanceConfig.LockScreenState.Visible) assertEquals(R.drawable.ic_flashlight_off, ((lastValue as KeyguardQuickAffordanceConfig.LockScreenState.Visible).icon as? Icon.Resource)?.res) assertEquals( R.drawable.qs_flashlight_icon_off, ((lastValue as KeyguardQuickAffordanceConfig.LockScreenState.Visible).icon as? Icon.Resource) ?.res ) job.cancel() } Loading Loading @@ -143,8 +159,11 @@ class FlashlightQuickAffordanceConfigTest : LeakCheckedTest() { // then assertTrue(lastValue is KeyguardQuickAffordanceConfig.LockScreenState.Visible) assertTrue((lastValue as KeyguardQuickAffordanceConfig.LockScreenState.Visible).activationState is ActivationState.Active) assertEquals(R.drawable.ic_flashlight_on, (lastValue.icon as? Icon.Resource)?.res) assertTrue( (lastValue as KeyguardQuickAffordanceConfig.LockScreenState.Visible).activationState is ActivationState.Active ) assertEquals(R.drawable.qs_flashlight_icon_on, (lastValue.icon as? Icon.Resource)?.res) job.cancel() } Loading @@ -162,8 +181,11 @@ class FlashlightQuickAffordanceConfigTest : LeakCheckedTest() { // then assertTrue(lastValue is KeyguardQuickAffordanceConfig.LockScreenState.Visible) assertTrue((lastValue as KeyguardQuickAffordanceConfig.LockScreenState.Visible).activationState is ActivationState.Inactive) assertEquals(R.drawable.ic_flashlight_off, (lastValue.icon as? Icon.Resource)?.res) assertTrue( (lastValue as KeyguardQuickAffordanceConfig.LockScreenState.Visible).activationState is ActivationState.Inactive ) assertEquals(R.drawable.qs_flashlight_icon_off, (lastValue.icon as? Icon.Resource)?.res) job.cancel() } Loading Loading
packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfig.kt +61 −59 Original line number Diff line number Diff line Loading @@ -21,19 +21,21 @@ import android.content.Context import com.android.systemui.R import com.android.systemui.animation.Expandable import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow import com.android.systemui.common.shared.model.ContentDescription import com.android.systemui.common.shared.model.Icon import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.keyguard.shared.quickaffordance.ActivationState import com.android.systemui.statusbar.policy.FlashlightController import javax.inject.Inject import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow import javax.inject.Inject @SysUISingleton class FlashlightQuickAffordanceConfig @Inject constructor( class FlashlightQuickAffordanceConfig @Inject constructor( @Application private val context: Context, private val flashlightController: FlashlightController, ) : KeyguardQuickAffordanceConfig { Loading @@ -46,7 +48,7 @@ class FlashlightQuickAffordanceConfig @Inject constructor( override fun toLockScreenState(): KeyguardQuickAffordanceConfig.LockScreenState = KeyguardQuickAffordanceConfig.LockScreenState.Visible( Icon.Resource( R.drawable.ic_flashlight_on, R.drawable.qs_flashlight_icon_on, ContentDescription.Resource(R.string.quick_settings_flashlight_label) ), ActivationState.Active Loading @@ -57,7 +59,7 @@ class FlashlightQuickAffordanceConfig @Inject constructor( override fun toLockScreenState(): KeyguardQuickAffordanceConfig.LockScreenState = KeyguardQuickAffordanceConfig.LockScreenState.Visible( Icon.Resource( R.drawable.ic_flashlight_off, R.drawable.qs_flashlight_icon_off, ContentDescription.Resource(R.string.quick_settings_flashlight_label) ), ActivationState.Inactive Loading @@ -77,15 +79,12 @@ class FlashlightQuickAffordanceConfig @Inject constructor( get() = context.getString(R.string.quick_settings_flashlight_label) override val pickerIconResourceId: Int get() = if (flashlightController.isEnabled) { R.drawable.ic_flashlight_on } else { R.drawable.ic_flashlight_off } get() = R.drawable.ic_flashlight_off override val lockScreenState: Flow<KeyguardQuickAffordanceConfig.LockScreenState> = conflatedCallbackFlow { val flashlightCallback = object : FlashlightController.FlashlightListener { val flashlightCallback = object : FlashlightController.FlashlightListener { override fun onFlashlightChanged(enabled: Boolean) { trySendWithFailureLogging( if (enabled) { Loading @@ -98,7 +97,10 @@ class FlashlightQuickAffordanceConfig @Inject constructor( } override fun onFlashlightError() { trySendWithFailureLogging(FlashlightState.OffAvailable.toLockScreenState(), TAG) trySendWithFailureLogging( FlashlightState.OffAvailable.toLockScreenState(), TAG ) } override fun onFlashlightAvailabilityChanged(available: Boolean) { Loading @@ -119,15 +121,15 @@ class FlashlightQuickAffordanceConfig @Inject constructor( flashlightController.addCallback(flashlightCallback) awaitClose { flashlightController.removeCallback(flashlightCallback) } awaitClose { flashlightController.removeCallback(flashlightCallback) } } override fun onTriggered(expandable: Expandable?): KeyguardQuickAffordanceConfig.OnTriggeredResult { flashlightController .setFlashlight(flashlightController.isAvailable && !flashlightController.isEnabled) override fun onTriggered( expandable: Expandable? ): KeyguardQuickAffordanceConfig.OnTriggeredResult { flashlightController.setFlashlight( flashlightController.isAvailable && !flashlightController.isEnabled ) return KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled } Loading
packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt +26 −4 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.systemui.keyguard.ui.binder import android.graphics.drawable.Animatable2 import android.util.Size import android.util.TypedValue import android.view.View Loading @@ -27,12 +28,11 @@ import androidx.core.view.isVisible import androidx.core.view.updateLayoutParams import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle import com.android.keyguard.KeyguardUpdateMonitor import com.android.keyguard.LockIconViewController import com.android.settingslib.Utils import com.android.systemui.R import com.android.systemui.animation.Expandable import com.android.systemui.animation.Interpolators import com.android.systemui.common.shared.model.Icon import com.android.systemui.common.ui.binder.IconViewBinder import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordanceViewModel Loading Loading @@ -73,7 +73,8 @@ object KeyguardBottomAreaViewBinder { fun onConfigurationChanged() /** * Returns whether the keyguard bottom area should be constrained to the top of the lock icon * Returns whether the keyguard bottom area should be constrained to the top of the lock * icon */ fun shouldConstrainToTopOfLockIcon(): Boolean } Loading Loading @@ -248,6 +249,27 @@ object KeyguardBottomAreaViewBinder { IconViewBinder.bind(viewModel.icon, view) (view.drawable as? Animatable2)?.let { animatable -> (viewModel.icon as? Icon.Resource)?.res?.let { iconResourceId -> // Always start the animation (we do call stop() below, if we need to skip it). animatable.start() if (view.tag != iconResourceId) { // Here when we haven't run the animation on a previous update. // // Save the resource ID for next time, so we know not to re-animate the same // animation again. view.tag = iconResourceId } else { // Here when we've already done this animation on a previous update and want to // skip directly to the final frame of the animation to avoid running it. // // By calling stop after start, we go to the final frame of the animation. animatable.stop() } } } view.isActivated = viewModel.isActivated view.drawable.setTint( Utils.getColorAttrDefaultColor( Loading
packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FlashlightQuickAffordanceConfigTest.kt +65 −43 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ import com.android.systemui.keyguard.shared.quickaffordance.ActivationState import com.android.systemui.statusbar.policy.FlashlightController import com.android.systemui.utils.leaks.FakeFlashlightController import com.android.systemui.utils.leaks.LeakCheckedTest import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.toList import kotlinx.coroutines.launch import kotlinx.coroutines.test.UnconfinedTestDispatcher Loading @@ -38,6 +39,7 @@ import org.junit.runners.JUnit4 import org.mockito.Mock import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest @RunWith(JUnit4::class) class FlashlightQuickAffordanceConfigTest : LeakCheckedTest() { Loading @@ -51,7 +53,9 @@ class FlashlightQuickAffordanceConfigTest : LeakCheckedTest() { injectLeakCheckedDependency(FlashlightController::class.java) MockitoAnnotations.initMocks(this) flashlightController = SysuiLeakCheck().getLeakChecker(FlashlightController::class.java) as FakeFlashlightController flashlightController = SysuiLeakCheck().getLeakChecker(FlashlightController::class.java) as FakeFlashlightController underTest = FlashlightQuickAffordanceConfig(context, flashlightController) } Loading @@ -69,8 +73,12 @@ class FlashlightQuickAffordanceConfigTest : LeakCheckedTest() { // then assertTrue(lastValue is KeyguardQuickAffordanceConfig.LockScreenState.Visible) assertEquals(R.drawable.ic_flashlight_on, ((lastValue as KeyguardQuickAffordanceConfig.LockScreenState.Visible).icon as? Icon.Resource)?.res) assertEquals( R.drawable.qs_flashlight_icon_on, ((lastValue as KeyguardQuickAffordanceConfig.LockScreenState.Visible).icon as? Icon.Resource) ?.res ) job.cancel() } Loading @@ -88,8 +96,12 @@ class FlashlightQuickAffordanceConfigTest : LeakCheckedTest() { // then assertTrue(lastValue is KeyguardQuickAffordanceConfig.LockScreenState.Visible) assertEquals(R.drawable.ic_flashlight_off, ((lastValue as KeyguardQuickAffordanceConfig.LockScreenState.Visible).icon as? Icon.Resource)?.res) assertEquals( R.drawable.qs_flashlight_icon_off, ((lastValue as KeyguardQuickAffordanceConfig.LockScreenState.Visible).icon as? Icon.Resource) ?.res ) job.cancel() } Loading @@ -107,8 +119,12 @@ class FlashlightQuickAffordanceConfigTest : LeakCheckedTest() { // then assertTrue(lastValue is KeyguardQuickAffordanceConfig.LockScreenState.Visible) assertEquals(R.drawable.ic_flashlight_off, ((lastValue as KeyguardQuickAffordanceConfig.LockScreenState.Visible).icon as? Icon.Resource)?.res) assertEquals( R.drawable.qs_flashlight_icon_off, ((lastValue as KeyguardQuickAffordanceConfig.LockScreenState.Visible).icon as? Icon.Resource) ?.res ) job.cancel() } Loading Loading @@ -143,8 +159,11 @@ class FlashlightQuickAffordanceConfigTest : LeakCheckedTest() { // then assertTrue(lastValue is KeyguardQuickAffordanceConfig.LockScreenState.Visible) assertTrue((lastValue as KeyguardQuickAffordanceConfig.LockScreenState.Visible).activationState is ActivationState.Active) assertEquals(R.drawable.ic_flashlight_on, (lastValue.icon as? Icon.Resource)?.res) assertTrue( (lastValue as KeyguardQuickAffordanceConfig.LockScreenState.Visible).activationState is ActivationState.Active ) assertEquals(R.drawable.qs_flashlight_icon_on, (lastValue.icon as? Icon.Resource)?.res) job.cancel() } Loading @@ -162,8 +181,11 @@ class FlashlightQuickAffordanceConfigTest : LeakCheckedTest() { // then assertTrue(lastValue is KeyguardQuickAffordanceConfig.LockScreenState.Visible) assertTrue((lastValue as KeyguardQuickAffordanceConfig.LockScreenState.Visible).activationState is ActivationState.Inactive) assertEquals(R.drawable.ic_flashlight_off, (lastValue.icon as? Icon.Resource)?.res) assertTrue( (lastValue as KeyguardQuickAffordanceConfig.LockScreenState.Visible).activationState is ActivationState.Inactive ) assertEquals(R.drawable.qs_flashlight_icon_off, (lastValue.icon as? Icon.Resource)?.res) job.cancel() } Loading