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

Commit f309064d authored by Matt Pietal's avatar Matt Pietal Committed by Android (Google) Code Review
Browse files

Merge changes from topic "cherrypick-mpietal_rewire-m59jl0izrt"

* changes:
  Add KeyguardTransitionRepository
  Rewire dozing for clock events
parents 3f7262ee 0e8f2a2e
Loading
Loading
Loading
Loading
+71 −27
Original line number Original line Diff line number Diff line
@@ -23,12 +23,18 @@ import android.content.res.Resources
import android.text.format.DateFormat
import android.text.format.DateFormat
import android.util.TypedValue
import android.util.TypedValue
import android.view.View
import android.view.View
import androidx.annotation.VisibleForTesting
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags.REGION_SAMPLING
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.plugins.ClockController
import com.android.systemui.plugins.ClockController
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.shared.regionsampling.RegionSamplingInstance
import com.android.systemui.shared.regionsampling.RegionSamplingInstance
import com.android.systemui.statusbar.policy.BatteryController
import com.android.systemui.statusbar.policy.BatteryController
import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback
import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback
@@ -38,13 +44,20 @@ import java.util.Locale
import java.util.TimeZone
import java.util.TimeZone
import java.util.concurrent.Executor
import java.util.concurrent.Executor
import javax.inject.Inject
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.DisposableHandle
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.launch


/**
/**
 * Controller for a Clock provided by the registry and used on the keyguard. Instantiated by
 * Controller for a Clock provided by the registry and used on the keyguard. Instantiated by
 * [KeyguardClockSwitchController]. Functionality is forked from [AnimatableClockController].
 * [KeyguardClockSwitchController]. Functionality is forked from [AnimatableClockController].
 */
 */
open class ClockEventController @Inject constructor(
open class ClockEventController @Inject constructor(
    private val statusBarStateController: StatusBarStateController,
    private val keyguardInteractor: KeyguardInteractor,
    private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
    private val broadcastDispatcher: BroadcastDispatcher,
    private val broadcastDispatcher: BroadcastDispatcher,
    private val batteryController: BatteryController,
    private val batteryController: BatteryController,
    private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
    private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
@@ -53,7 +66,7 @@ open class ClockEventController @Inject constructor(
    private val context: Context,
    private val context: Context,
    @Main private val mainExecutor: Executor,
    @Main private val mainExecutor: Executor,
    @Background private val bgExecutor: Executor,
    @Background private val bgExecutor: Executor,
    private val featureFlags: FeatureFlags,
    private val featureFlags: FeatureFlags
) {
) {
    var clock: ClockController? = null
    var clock: ClockController? = null
        set(value) {
        set(value) {
@@ -70,9 +83,9 @@ open class ClockEventController @Inject constructor(
    private var isCharging = false
    private var isCharging = false
    private var dozeAmount = 0f
    private var dozeAmount = 0f
    private var isKeyguardVisible = false
    private var isKeyguardVisible = false

    private var isRegistered = false
    private val regionSamplingEnabled =
    private var disposableHandle: DisposableHandle? = null
            featureFlags.isEnabled(com.android.systemui.flags.Flags.REGION_SAMPLING)
    private val regionSamplingEnabled = featureFlags.isEnabled(REGION_SAMPLING)


    private fun updateColors() {
    private fun updateColors() {
        if (regionSamplingEnabled && smallRegionSampler != null && largeRegionSampler != null) {
        if (regionSamplingEnabled && smallRegionSampler != null && largeRegionSampler != null) {
@@ -165,15 +178,6 @@ open class ClockEventController @Inject constructor(
        }
        }
    }
    }


    private val statusBarStateListener = object : StatusBarStateController.StateListener {
        override fun onDozeAmountChanged(linear: Float, eased: Float) {
            clock?.animations?.doze(linear)

            isDozing = linear > dozeAmount
            dozeAmount = linear
        }
    }

    private val keyguardUpdateMonitorCallback = object : KeyguardUpdateMonitorCallback() {
    private val keyguardUpdateMonitorCallback = object : KeyguardUpdateMonitorCallback() {
        override fun onKeyguardVisibilityChanged(visible: Boolean) {
        override fun onKeyguardVisibilityChanged(visible: Boolean) {
            isKeyguardVisible = visible
            isKeyguardVisible = visible
@@ -195,13 +199,11 @@ open class ClockEventController @Inject constructor(
        }
        }
    }
    }


    init {
    fun registerListeners(parent: View) {
        isDozing = statusBarStateController.isDozing
        if (isRegistered) {
            return
        }
        }

        isRegistered = true
    fun registerListeners() {
        dozeAmount = statusBarStateController.dozeAmount
        isDozing = statusBarStateController.isDozing || dozeAmount != 0f


        broadcastDispatcher.registerReceiver(
        broadcastDispatcher.registerReceiver(
            localeBroadcastReceiver,
            localeBroadcastReceiver,
@@ -210,17 +212,28 @@ open class ClockEventController @Inject constructor(
        configurationController.addCallback(configListener)
        configurationController.addCallback(configListener)
        batteryController.addCallback(batteryCallback)
        batteryController.addCallback(batteryCallback)
        keyguardUpdateMonitor.registerCallback(keyguardUpdateMonitorCallback)
        keyguardUpdateMonitor.registerCallback(keyguardUpdateMonitorCallback)
        statusBarStateController.addCallback(statusBarStateListener)
        smallRegionSampler?.startRegionSampler()
        smallRegionSampler?.startRegionSampler()
        largeRegionSampler?.startRegionSampler()
        largeRegionSampler?.startRegionSampler()
        disposableHandle = parent.repeatWhenAttached {
            repeatOnLifecycle(Lifecycle.State.STARTED) {
                listenForDozing(this)
                listenForDozeAmount(this)
                listenForDozeAmountTransition(this)
            }
        }
    }
    }


    fun unregisterListeners() {
    fun unregisterListeners() {
        if (!isRegistered) {
            return
        }
        isRegistered = false

        disposableHandle?.dispose()
        broadcastDispatcher.unregisterReceiver(localeBroadcastReceiver)
        broadcastDispatcher.unregisterReceiver(localeBroadcastReceiver)
        configurationController.removeCallback(configListener)
        configurationController.removeCallback(configListener)
        batteryController.removeCallback(batteryCallback)
        batteryController.removeCallback(batteryCallback)
        keyguardUpdateMonitor.removeCallback(keyguardUpdateMonitorCallback)
        keyguardUpdateMonitor.removeCallback(keyguardUpdateMonitorCallback)
        statusBarStateController.removeCallback(statusBarStateListener)
        smallRegionSampler?.stopRegionSampler()
        smallRegionSampler?.stopRegionSampler()
        largeRegionSampler?.stopRegionSampler()
        largeRegionSampler?.stopRegionSampler()
    }
    }
@@ -235,8 +248,39 @@ open class ClockEventController @Inject constructor(
        largeRegionSampler?.dump(pw)
        largeRegionSampler?.dump(pw)
    }
    }


    companion object {
    @VisibleForTesting
        private val TAG = ClockEventController::class.simpleName
    internal fun listenForDozeAmount(scope: CoroutineScope): Job {
        private const val FORMAT_NUMBER = 1234567890
        return scope.launch {
            keyguardInteractor.dozeAmount.collect {
                dozeAmount = it
                clock?.animations?.doze(dozeAmount)
            }
        }
    }

    @VisibleForTesting
    internal fun listenForDozeAmountTransition(scope: CoroutineScope): Job {
        return scope.launch {
            keyguardTransitionInteractor.aodToLockscreenTransition.collect {
                // Would eventually run this:
                // dozeAmount = it.value
                // clock?.animations?.doze(dozeAmount)
            }
        }
    }

    @VisibleForTesting
    internal fun listenForDozing(scope: CoroutineScope): Job {
        return scope.launch {
            combine (
                keyguardInteractor.dozeAmount,
                keyguardInteractor.isDozing,
            ) { localDozeAmount, localIsDozing ->
                localDozeAmount > dozeAmount || localIsDozing
            }
            .collect { localIsDozing ->
                isDozing = localIsDozing
            }
        }
    }
    }
}
}
+1 −1
Original line number Original line Diff line number Diff line
@@ -165,7 +165,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
    protected void onViewAttached() {
    protected void onViewAttached() {
        mClockRegistry.registerClockChangeListener(mClockChangedListener);
        mClockRegistry.registerClockChangeListener(mClockChangedListener);
        setClock(mClockRegistry.createCurrentClock());
        setClock(mClockRegistry.createCurrentClock());
        mClockEventController.registerListeners();
        mClockEventController.registerListeners(mView);
        mKeyguardClockTopMargin =
        mKeyguardClockTopMargin =
                mView.getResources().getDimensionPixelSize(R.dimen.keyguard_clock_top_margin);
                mView.getResources().getDimensionPixelSize(R.dimen.keyguard_clock_top_margin);


+2 −0
Original line number Original line Diff line number Diff line
@@ -43,6 +43,7 @@ import com.android.systemui.keyguard.DismissCallbackRegistry;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.keyguard.data.repository.KeyguardRepositoryModule;
import com.android.systemui.keyguard.data.repository.KeyguardRepositoryModule;
import com.android.systemui.keyguard.domain.interactor.StartKeyguardTransitionModule;
import com.android.systemui.keyguard.domain.quickaffordance.KeyguardQuickAffordanceModule;
import com.android.systemui.keyguard.domain.quickaffordance.KeyguardQuickAffordanceModule;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.statusbar.NotificationShadeDepthController;
import com.android.systemui.statusbar.NotificationShadeDepthController;
@@ -72,6 +73,7 @@ import dagger.Provides;
            FalsingModule.class,
            FalsingModule.class,
            KeyguardQuickAffordanceModule.class,
            KeyguardQuickAffordanceModule.class,
            KeyguardRepositoryModule.class,
            KeyguardRepositoryModule.class,
            StartKeyguardTransitionModule.class,
        })
        })
public class KeyguardModule {
public class KeyguardModule {
    /**
    /**
+31 −0
Original line number Original line Diff line number Diff line
@@ -21,6 +21,7 @@ import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCall
import com.android.systemui.common.shared.model.Position
import com.android.systemui.common.shared.model.Position
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.doze.DozeHost
import com.android.systemui.doze.DozeHost
import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.statusbar.policy.KeyguardStateController
import javax.inject.Inject
import javax.inject.Inject
@@ -85,6 +86,9 @@ interface KeyguardRepository {
     */
     */
    val dozeAmount: Flow<Float>
    val dozeAmount: Flow<Float>


    /** Observable for the [StatusBarState] */
    val statusBarState: Flow<StatusBarState>

    /**
    /**
     * Returns `true` if the keyguard is showing; `false` otherwise.
     * Returns `true` if the keyguard is showing; `false` otherwise.
     *
     *
@@ -185,6 +189,24 @@ constructor(
        return keyguardStateController.isShowing
        return keyguardStateController.isShowing
    }
    }


    override val statusBarState: Flow<StatusBarState> = conflatedCallbackFlow {
        val callback =
            object : StatusBarStateController.StateListener {
                override fun onStateChanged(state: Int) {
                    trySendWithFailureLogging(statusBarStateIntToObject(state), TAG, "state")
                }
            }

        statusBarStateController.addCallback(callback)
        trySendWithFailureLogging(
            statusBarStateIntToObject(statusBarStateController.getState()),
            TAG,
            "initial state"
        )

        awaitClose { statusBarStateController.removeCallback(callback) }
    }

    override fun setAnimateDozingTransitions(animate: Boolean) {
    override fun setAnimateDozingTransitions(animate: Boolean) {
        _animateBottomAreaDozingTransitions.value = animate
        _animateBottomAreaDozingTransitions.value = animate
    }
    }
@@ -197,6 +219,15 @@ constructor(
        _clockPosition.value = Position(x, y)
        _clockPosition.value = Position(x, y)
    }
    }


    private fun statusBarStateIntToObject(value: Int): StatusBarState {
        return when (value) {
            0 -> StatusBarState.SHADE
            1 -> StatusBarState.KEYGUARD
            2 -> StatusBarState.SHADE_LOCKED
            else -> throw IllegalArgumentException("Invalid StatusBarState value: $value")
        }
    }

    companion object {
    companion object {
        private const val TAG = "KeyguardRepositoryImpl"
        private const val TAG = "KeyguardRepositoryImpl"
    }
    }
+169 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2022 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.keyguard.data.repository

import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.ValueAnimator
import android.animation.ValueAnimator.AnimatorUpdateListener
import android.annotation.FloatRange
import android.util.Log
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionInfo
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
import java.util.UUID
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.filter

@SysUISingleton
class KeyguardTransitionRepository @Inject constructor() {
    /*
     * Each transition between [KeyguardState]s will have an associated Flow.
     * In order to collect these events, clients should call [transition].
     */
    private val _transitions = MutableStateFlow(TransitionStep())
    val transitions = _transitions.asStateFlow()

    /* Information about the active transition. */
    private var currentTransitionInfo: TransitionInfo? = null
    /*
     * When manual control of the transition is requested, a unique [UUID] is used as the handle
     * to permit calls to [updateTransition]
     */
    private var updateTransitionId: UUID? = null

    /**
     * Interactors that require information about changes between [KeyguardState]s will call this to
     * register themselves for flowable [TransitionStep]s when that transition occurs.
     */
    fun transition(from: KeyguardState, to: KeyguardState): Flow<TransitionStep> {
        return transitions.filter { step -> step.from == from && step.to == to }
    }

    /**
     * Begin a transition from one state to another. The [info.from] must match
     * [currentTransitionInfo.to], or the request will be denied. This is enforced to avoid
     * unplanned transitions.
     */
    fun startTransition(info: TransitionInfo): UUID? {
        if (currentTransitionInfo != null) {
            // Open questions:
            // * Queue of transitions? buffer of 1?
            // * Are transitions cancellable if a new one is triggered?
            // * What validation does this need to do?
            Log.wtf(TAG, "Transition still active: $currentTransitionInfo")
            return null
        }
        currentTransitionInfo?.animator?.cancel()

        currentTransitionInfo = info
        info.animator?.let { animator ->
            // An animator was provided, so use it to run the transition
            animator.setFloatValues(0f, 1f)
            val updateListener =
                object : AnimatorUpdateListener {
                    override fun onAnimationUpdate(animation: ValueAnimator) {
                        emitTransition(
                            info,
                            (animation.getAnimatedValue() as Float),
                            TransitionState.RUNNING
                        )
                    }
                }
            val adapter =
                object : AnimatorListenerAdapter() {
                    override fun onAnimationStart(animation: Animator) {
                        Log.i(TAG, "Starting transition: $info")
                        emitTransition(info, 0f, TransitionState.STARTED)
                    }
                    override fun onAnimationCancel(animation: Animator) {
                        Log.i(TAG, "Cancelling transition: $info")
                    }
                    override fun onAnimationEnd(animation: Animator) {
                        Log.i(TAG, "Ending transition: $info")
                        emitTransition(info, 1f, TransitionState.FINISHED)
                        animator.removeListener(this)
                        animator.removeUpdateListener(updateListener)
                    }
                }
            animator.addListener(adapter)
            animator.addUpdateListener(updateListener)
            animator.start()
            return@startTransition null
        }
            ?: run {
                Log.i(TAG, "Starting transition (manual): $info")
                emitTransition(info, 0f, TransitionState.STARTED)

                // No animator, so it's manual. Provide a mechanism to callback
                updateTransitionId = UUID.randomUUID()
                return@startTransition updateTransitionId
            }
    }

    /**
     * Allows manual control of a transition. When calling [startTransition], the consumer must pass
     * in a null animator. In return, it will get a unique [UUID] that will be validated to allow
     * further updates.
     *
     * When the transition is over, TransitionState.FINISHED must be passed into the [state]
     * parameter.
     */
    fun updateTransition(
        transitionId: UUID,
        @FloatRange(from = 0.0, to = 1.0) value: Float,
        state: TransitionState
    ) {
        if (updateTransitionId != transitionId) {
            Log.wtf(TAG, "Attempting to update with old/invalid transitionId: $transitionId")
            return
        }

        if (currentTransitionInfo == null) {
            Log.wtf(TAG, "Attempting to update with null 'currentTransitionInfo'")
            return
        }

        currentTransitionInfo?.let { info ->
            if (state == TransitionState.FINISHED) {
                updateTransitionId = null
                Log.i(TAG, "Ending transition: $info")
            }

            emitTransition(info, value, state)
        }
    }

    private fun emitTransition(
        info: TransitionInfo,
        value: Float,
        transitionState: TransitionState
    ) {
        if (transitionState == TransitionState.FINISHED) {
            currentTransitionInfo = null
        }
        _transitions.value = TransitionStep(info.from, info.to, value, transitionState)
    }

    companion object {
        private const val TAG = "KeyguardTransitionRepository"
    }
}
Loading