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

Commit d3613e2a authored by Ale Nijamkin's avatar Ale Nijamkin Committed by Automerger Merge Worker
Browse files

Merge "Support animatable icons to quick affordances." into tm-qpr-dev am: e155f5c7

parents cad25dc1 e155f5c7
Loading
Loading
Loading
Loading
+61 −59
Original line number Original line Diff line number Diff line
@@ -21,19 +21,21 @@ import android.content.Context
import com.android.systemui.R
import com.android.systemui.R
import com.android.systemui.animation.Expandable
import com.android.systemui.animation.Expandable
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
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.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
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.keyguard.shared.quickaffordance.ActivationState
import com.android.systemui.statusbar.policy.FlashlightController
import com.android.systemui.statusbar.policy.FlashlightController
import javax.inject.Inject
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.Flow
import javax.inject.Inject


@SysUISingleton
@SysUISingleton
class FlashlightQuickAffordanceConfig @Inject constructor(
class FlashlightQuickAffordanceConfig
@Inject
constructor(
    @Application private val context: Context,
    @Application private val context: Context,
    private val flashlightController: FlashlightController,
    private val flashlightController: FlashlightController,
) : KeyguardQuickAffordanceConfig {
) : KeyguardQuickAffordanceConfig {
@@ -46,7 +48,7 @@ class FlashlightQuickAffordanceConfig @Inject constructor(
            override fun toLockScreenState(): KeyguardQuickAffordanceConfig.LockScreenState =
            override fun toLockScreenState(): KeyguardQuickAffordanceConfig.LockScreenState =
                KeyguardQuickAffordanceConfig.LockScreenState.Visible(
                KeyguardQuickAffordanceConfig.LockScreenState.Visible(
                    Icon.Resource(
                    Icon.Resource(
                        R.drawable.ic_flashlight_on,
                        R.drawable.qs_flashlight_icon_on,
                        ContentDescription.Resource(R.string.quick_settings_flashlight_label)
                        ContentDescription.Resource(R.string.quick_settings_flashlight_label)
                    ),
                    ),
                    ActivationState.Active
                    ActivationState.Active
@@ -57,7 +59,7 @@ class FlashlightQuickAffordanceConfig @Inject constructor(
            override fun toLockScreenState(): KeyguardQuickAffordanceConfig.LockScreenState =
            override fun toLockScreenState(): KeyguardQuickAffordanceConfig.LockScreenState =
                KeyguardQuickAffordanceConfig.LockScreenState.Visible(
                KeyguardQuickAffordanceConfig.LockScreenState.Visible(
                    Icon.Resource(
                    Icon.Resource(
                        R.drawable.ic_flashlight_off,
                        R.drawable.qs_flashlight_icon_off,
                        ContentDescription.Resource(R.string.quick_settings_flashlight_label)
                        ContentDescription.Resource(R.string.quick_settings_flashlight_label)
                    ),
                    ),
                    ActivationState.Inactive
                    ActivationState.Inactive
@@ -77,15 +79,12 @@ class FlashlightQuickAffordanceConfig @Inject constructor(
        get() = context.getString(R.string.quick_settings_flashlight_label)
        get() = context.getString(R.string.quick_settings_flashlight_label)


    override val pickerIconResourceId: Int
    override val pickerIconResourceId: Int
        get() = if (flashlightController.isEnabled) {
        get() = R.drawable.ic_flashlight_off
            R.drawable.ic_flashlight_on
        } else {
            R.drawable.ic_flashlight_off
        }


    override val lockScreenState: Flow<KeyguardQuickAffordanceConfig.LockScreenState> =
    override val lockScreenState: Flow<KeyguardQuickAffordanceConfig.LockScreenState> =
        conflatedCallbackFlow {
        conflatedCallbackFlow {
        val flashlightCallback = object : FlashlightController.FlashlightListener {
            val flashlightCallback =
                object : FlashlightController.FlashlightListener {
                    override fun onFlashlightChanged(enabled: Boolean) {
                    override fun onFlashlightChanged(enabled: Boolean) {
                        trySendWithFailureLogging(
                        trySendWithFailureLogging(
                            if (enabled) {
                            if (enabled) {
@@ -98,7 +97,10 @@ class FlashlightQuickAffordanceConfig @Inject constructor(
                    }
                    }


                    override fun onFlashlightError() {
                    override fun onFlashlightError() {
                trySendWithFailureLogging(FlashlightState.OffAvailable.toLockScreenState(), TAG)
                        trySendWithFailureLogging(
                            FlashlightState.OffAvailable.toLockScreenState(),
                            TAG
                        )
                    }
                    }


                    override fun onFlashlightAvailabilityChanged(available: Boolean) {
                    override fun onFlashlightAvailabilityChanged(available: Boolean) {
@@ -119,15 +121,15 @@ class FlashlightQuickAffordanceConfig @Inject constructor(


            flashlightController.addCallback(flashlightCallback)
            flashlightController.addCallback(flashlightCallback)


        awaitClose {
            awaitClose { flashlightController.removeCallback(flashlightCallback) }
            flashlightController.removeCallback(flashlightCallback)
        }
        }
        }


    override fun onTriggered(expandable: Expandable?):
    override fun onTriggered(
            KeyguardQuickAffordanceConfig.OnTriggeredResult {
        expandable: Expandable?
        flashlightController
    ): KeyguardQuickAffordanceConfig.OnTriggeredResult {
                .setFlashlight(flashlightController.isAvailable && !flashlightController.isEnabled)
        flashlightController.setFlashlight(
            flashlightController.isAvailable && !flashlightController.isEnabled
        )
        return KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
        return KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
    }
    }


+26 −4
Original line number Original line Diff line number Diff line
@@ -16,6 +16,7 @@


package com.android.systemui.keyguard.ui.binder
package com.android.systemui.keyguard.ui.binder


import android.graphics.drawable.Animatable2
import android.util.Size
import android.util.Size
import android.util.TypedValue
import android.util.TypedValue
import android.view.View
import android.view.View
@@ -27,12 +28,11 @@ import androidx.core.view.isVisible
import androidx.core.view.updateLayoutParams
import androidx.core.view.updateLayoutParams
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
import androidx.lifecycle.repeatOnLifecycle
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.LockIconViewController
import com.android.settingslib.Utils
import com.android.settingslib.Utils
import com.android.systemui.R
import com.android.systemui.R
import com.android.systemui.animation.Expandable
import com.android.systemui.animation.Expandable
import com.android.systemui.animation.Interpolators
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.common.ui.binder.IconViewBinder
import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordanceViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordanceViewModel
@@ -73,7 +73,8 @@ object KeyguardBottomAreaViewBinder {
        fun onConfigurationChanged()
        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
        fun shouldConstrainToTopOfLockIcon(): Boolean
    }
    }
@@ -248,6 +249,27 @@ object KeyguardBottomAreaViewBinder {


        IconViewBinder.bind(viewModel.icon, view)
        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.isActivated = viewModel.isActivated
        view.drawable.setTint(
        view.drawable.setTint(
            Utils.getColorAttrDefaultColor(
            Utils.getColorAttrDefaultColor(
+65 −43
Original line number Original line Diff line number Diff line
@@ -25,6 +25,7 @@ import com.android.systemui.keyguard.shared.quickaffordance.ActivationState
import com.android.systemui.statusbar.policy.FlashlightController
import com.android.systemui.statusbar.policy.FlashlightController
import com.android.systemui.utils.leaks.FakeFlashlightController
import com.android.systemui.utils.leaks.FakeFlashlightController
import com.android.systemui.utils.leaks.LeakCheckedTest
import com.android.systemui.utils.leaks.LeakCheckedTest
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.launch
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.UnconfinedTestDispatcher
@@ -38,6 +39,7 @@ import org.junit.runners.JUnit4
import org.mockito.Mock
import org.mockito.Mock
import org.mockito.MockitoAnnotations
import org.mockito.MockitoAnnotations


@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@SmallTest
@RunWith(JUnit4::class)
@RunWith(JUnit4::class)
class FlashlightQuickAffordanceConfigTest : LeakCheckedTest() {
class FlashlightQuickAffordanceConfigTest : LeakCheckedTest() {
@@ -51,7 +53,9 @@ class FlashlightQuickAffordanceConfigTest : LeakCheckedTest() {
        injectLeakCheckedDependency(FlashlightController::class.java)
        injectLeakCheckedDependency(FlashlightController::class.java)
        MockitoAnnotations.initMocks(this)
        MockitoAnnotations.initMocks(this)


        flashlightController = SysuiLeakCheck().getLeakChecker(FlashlightController::class.java) as FakeFlashlightController
        flashlightController =
            SysuiLeakCheck().getLeakChecker(FlashlightController::class.java)
                as FakeFlashlightController
        underTest = FlashlightQuickAffordanceConfig(context, flashlightController)
        underTest = FlashlightQuickAffordanceConfig(context, flashlightController)
    }
    }


@@ -69,8 +73,12 @@ class FlashlightQuickAffordanceConfigTest : LeakCheckedTest() {


        // then
        // then
        assertTrue(lastValue is KeyguardQuickAffordanceConfig.LockScreenState.Visible)
        assertTrue(lastValue is KeyguardQuickAffordanceConfig.LockScreenState.Visible)
        assertEquals(R.drawable.ic_flashlight_on,
        assertEquals(
                ((lastValue as KeyguardQuickAffordanceConfig.LockScreenState.Visible).icon as? Icon.Resource)?.res)
            R.drawable.qs_flashlight_icon_on,
            ((lastValue as KeyguardQuickAffordanceConfig.LockScreenState.Visible).icon
                    as? Icon.Resource)
                ?.res
        )
        job.cancel()
        job.cancel()
    }
    }


@@ -88,8 +96,12 @@ class FlashlightQuickAffordanceConfigTest : LeakCheckedTest() {


        // then
        // then
        assertTrue(lastValue is KeyguardQuickAffordanceConfig.LockScreenState.Visible)
        assertTrue(lastValue is KeyguardQuickAffordanceConfig.LockScreenState.Visible)
        assertEquals(R.drawable.ic_flashlight_off,
        assertEquals(
                ((lastValue as KeyguardQuickAffordanceConfig.LockScreenState.Visible).icon as? Icon.Resource)?.res)
            R.drawable.qs_flashlight_icon_off,
            ((lastValue as KeyguardQuickAffordanceConfig.LockScreenState.Visible).icon
                    as? Icon.Resource)
                ?.res
        )
        job.cancel()
        job.cancel()
    }
    }


@@ -107,8 +119,12 @@ class FlashlightQuickAffordanceConfigTest : LeakCheckedTest() {


        // then
        // then
        assertTrue(lastValue is KeyguardQuickAffordanceConfig.LockScreenState.Visible)
        assertTrue(lastValue is KeyguardQuickAffordanceConfig.LockScreenState.Visible)
        assertEquals(R.drawable.ic_flashlight_off,
        assertEquals(
                ((lastValue as KeyguardQuickAffordanceConfig.LockScreenState.Visible).icon as? Icon.Resource)?.res)
            R.drawable.qs_flashlight_icon_off,
            ((lastValue as KeyguardQuickAffordanceConfig.LockScreenState.Visible).icon
                    as? Icon.Resource)
                ?.res
        )
        job.cancel()
        job.cancel()
    }
    }


@@ -143,8 +159,11 @@ class FlashlightQuickAffordanceConfigTest : LeakCheckedTest() {


        // then
        // then
        assertTrue(lastValue is KeyguardQuickAffordanceConfig.LockScreenState.Visible)
        assertTrue(lastValue is KeyguardQuickAffordanceConfig.LockScreenState.Visible)
        assertTrue((lastValue as KeyguardQuickAffordanceConfig.LockScreenState.Visible).activationState is ActivationState.Active)
        assertTrue(
        assertEquals(R.drawable.ic_flashlight_on, (lastValue.icon as? Icon.Resource)?.res)
            (lastValue as KeyguardQuickAffordanceConfig.LockScreenState.Visible).activationState
                is ActivationState.Active
        )
        assertEquals(R.drawable.qs_flashlight_icon_on, (lastValue.icon as? Icon.Resource)?.res)
        job.cancel()
        job.cancel()
    }
    }


@@ -162,8 +181,11 @@ class FlashlightQuickAffordanceConfigTest : LeakCheckedTest() {


        // then
        // then
        assertTrue(lastValue is KeyguardQuickAffordanceConfig.LockScreenState.Visible)
        assertTrue(lastValue is KeyguardQuickAffordanceConfig.LockScreenState.Visible)
        assertTrue((lastValue as KeyguardQuickAffordanceConfig.LockScreenState.Visible).activationState is ActivationState.Inactive)
        assertTrue(
        assertEquals(R.drawable.ic_flashlight_off, (lastValue.icon as? Icon.Resource)?.res)
            (lastValue as KeyguardQuickAffordanceConfig.LockScreenState.Visible).activationState
                is ActivationState.Inactive
        )
        assertEquals(R.drawable.qs_flashlight_icon_off, (lastValue.icon as? Icon.Resource)?.res)
        job.cancel()
        job.cancel()
    }
    }