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

Commit b0bed797 authored by Josh Tsuji's avatar Josh Tsuji Committed by Android (Google) Code Review
Browse files

Merge "Adds support for the in-window unlock animation to the Keyguard refactor." into main

parents c231a836 ac06c257
Loading
Loading
Loading
Loading
+7 −1
Original line number Diff line number Diff line
@@ -47,6 +47,7 @@ import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
import com.android.systemui.keyguard.ui.binder.KeyguardRootViewBinder;
import com.android.systemui.keyguard.ui.view.InWindowLauncherUnlockAnimationManager;
import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel;
import com.android.systemui.log.LogBuffer;
import com.android.systemui.log.core.LogLevel;
@@ -123,6 +124,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
    private View mSmartspaceView;

    private final KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
    private final InWindowLauncherUnlockAnimationManager mInWindowLauncherUnlockAnimationManager;

    private boolean mShownOnSecondaryDisplay = false;
    private boolean mOnlyClock = false;
@@ -190,7 +192,8 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
            AlwaysOnDisplayNotificationIconViewStore aodIconViewStore,
            KeyguardInteractor keyguardInteractor,
            KeyguardClockInteractor keyguardClockInteractor,
            FeatureFlagsClassic featureFlags) {
            FeatureFlagsClassic featureFlags,
            InWindowLauncherUnlockAnimationManager inWindowLauncherUnlockAnimationManager) {
        super(keyguardClockSwitch);
        mStatusBarStateController = statusBarStateController;
        mClockRegistry = clockRegistry;
@@ -214,6 +217,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
        mFeatureFlags = featureFlags;
        mKeyguardInteractor = keyguardInteractor;
        mKeyguardClockInteractor = keyguardClockInteractor;
        mInWindowLauncherUnlockAnimationManager = inWindowLauncherUnlockAnimationManager;

        mClockChangedListener = new ClockRegistry.ClockChangeListener() {
            @Override
@@ -438,6 +442,8 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS
        mSmartspaceView.setPaddingRelative(startPadding, 0, endPadding, 0);

        mKeyguardUnlockAnimationController.setLockscreenSmartspace(mSmartspaceView);
        mInWindowLauncherUnlockAnimationManager.setLockscreenSmartspace(mSmartspaceView);

        mView.setSmartspace(mSmartspaceView);
    }

+89 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 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 com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.InWindowLauncherUnlockAnimationInteractor
import com.android.systemui.shared.system.smartspace.ILauncherUnlockAnimationController
import com.android.systemui.shared.system.smartspace.SmartspaceState
import javax.inject.Inject
import kotlinx.coroutines.flow.MutableStateFlow

/**
 * State related to System UI's handling of the in-window Launcher unlock animations. This includes
 * the staggered icon entry animation that plays during unlock, as well as the smartspace shared
 * element animation, if supported.
 *
 * While the animations themselves occur fully in the Launcher window, System UI is responsible for
 * preparing/starting the animations, as well as synchronizing the smartspace state so that the two
 * smartspaces appear visually identical for the shared element animation.
 */
@SysUISingleton
class InWindowLauncherUnlockAnimationRepository @Inject constructor() {

    /**
     * Whether we have called [ILauncherUnlockAnimationController.playUnlockAnimation] during this
     * unlock sequence. This value is set back to false once
     * [InWindowLauncherUnlockAnimationInteractor.shouldStartInWindowAnimation] reverts to false,
     * which happens when we're no longer in transition to GONE or if the remote animation ends or
     * is cancelled.
     */
    val startedUnlockAnimation = MutableStateFlow(false)

    /**
     * The unlock amount we've explicitly passed to
     * [ILauncherUnlockAnimationController.setUnlockAmount]. This is used whenever System UI is
     * directly controlling the amount of the unlock animation, such as during a manual swipe to
     * unlock gesture.
     *
     * This value is *not* updated if we called
     * [ILauncherUnlockAnimationController.playUnlockAnimation] to ask Launcher to animate all the
     * way unlocked, since that animator is running in the Launcher window.
     */
    val manualUnlockAmount: MutableStateFlow<Float?> = MutableStateFlow(null)

    /**
     * The class name of the Launcher activity that provided us with a
     * [ILauncherUnlockAnimationController], if applicable. We can use this to check if that
     * launcher is underneath the lockscreen before playing in-window animations.
     *
     * If null, we have not been provided with a launcher unlock animation controller.
     */
    val launcherActivityClass: MutableStateFlow<String?> = MutableStateFlow(null)

    /**
     * Information about the Launcher's smartspace, which is passed to us via
     * [ILauncherUnlockAnimationController].
     */
    val launcherSmartspaceState: MutableStateFlow<SmartspaceState?> = MutableStateFlow(null)

    fun setStartedUnlockAnimation(started: Boolean) {
        startedUnlockAnimation.value = started
    }

    fun setManualUnlockAmount(amount: Float?) {
        manualUnlockAmount.value = amount
    }

    fun setLauncherActivityClass(className: String) {
        launcherActivityClass.value = className
    }

    fun setLauncherSmartspaceState(state: SmartspaceState?) {
        launcherSmartspaceState.value = state
    }
}
+14 −0
Original line number Diff line number Diff line
@@ -31,8 +31,14 @@ interface KeyguardSurfaceBehindRepository {
    /** Whether we're running animations on the surface. */
    val isAnimatingSurface: Flow<Boolean>

    /** Whether we have a RemoteAnimationTarget to run animations on the surface. */
    val isSurfaceRemoteAnimationTargetAvailable: Flow<Boolean>

    /** Set whether we're running animations on the surface. */
    fun setAnimatingSurface(animating: Boolean)

    /** Set whether we have a RemoteAnimationTarget with which to run animations on the surface. */
    fun setSurfaceRemoteAnimationTargetAvailable(available: Boolean)
}

@SysUISingleton
@@ -40,7 +46,15 @@ class KeyguardSurfaceBehindRepositoryImpl @Inject constructor() : KeyguardSurfac
    private val _isAnimatingSurface = MutableStateFlow(false)
    override val isAnimatingSurface = _isAnimatingSurface.asStateFlow()

    private val _isSurfaceRemoteAnimationTargetAvailable = MutableStateFlow(false)
    override val isSurfaceRemoteAnimationTargetAvailable =
        _isSurfaceRemoteAnimationTargetAvailable.asStateFlow()

    override fun setAnimatingSurface(animating: Boolean) {
        _isAnimatingSurface.value = animating
    }

    override fun setSurfaceRemoteAnimationTargetAvailable(available: Boolean) {
        _isSurfaceRemoteAnimationTargetAvailable.value = available
    }
}
+13 −2
Original line number Diff line number Diff line
@@ -35,6 +35,7 @@ import com.android.systemui.shade.data.repository.ShadeRepository
import com.android.systemui.util.kotlin.Utils.Companion.toQuad
import com.android.systemui.util.kotlin.Utils.Companion.toTriple
import com.android.systemui.util.kotlin.sample
import dagger.Lazy
import java.util.UUID
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
@@ -56,6 +57,7 @@ constructor(
    private val flags: FeatureFlags,
    private val shadeRepository: ShadeRepository,
    private val powerInteractor: PowerInteractor,
    inWindowLauncherUnlockAnimationInteractor: Lazy<InWindowLauncherUnlockAnimationInteractor>,
) :
    TransitionInteractor(
        fromState = KeyguardState.LOCKSCREEN,
@@ -104,12 +106,21 @@ constructor(
    val surfaceBehindModel: Flow<KeyguardSurfaceBehindModel?> =
        combine(
                transitionInteractor.startedKeyguardTransitionStep,
                transitionInteractor.transitionStepsFromState(KeyguardState.LOCKSCREEN)
            ) { startedStep, fromLockscreenStep ->
                transitionInteractor.transitionStepsFromState(KeyguardState.LOCKSCREEN),
                inWindowLauncherUnlockAnimationInteractor
                    .get()
                    .transitioningToGoneWithInWindowAnimation,
            ) { startedStep, fromLockscreenStep, transitioningToGoneWithInWindowAnimation ->
                if (startedStep.to != KeyguardState.GONE) {
                    // Only LOCKSCREEN -> GONE has specific surface params (for the unlock
                    // animation).
                    return@combine null
                } else if (transitioningToGoneWithInWindowAnimation) {
                    // If we're prepared for the in-window unlock, we're going to play an animation
                    // in the window. Make it fully visible.
                    KeyguardSurfaceBehindModel(
                        alpha = 1f,
                    )
                } else if (fromLockscreenStep.value > 0.5f) {
                    // Start the animation once we're 50% transitioned to GONE.
                    KeyguardSurfaceBehindModel(
+103 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 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.domain.interactor

import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.data.repository.InWindowLauncherUnlockAnimationRepository
import com.android.systemui.keyguard.data.repository.KeyguardSurfaceBehindRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.shared.system.ActivityManagerWrapper
import com.android.systemui.shared.system.smartspace.SmartspaceState
import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn

@SysUISingleton
class InWindowLauncherUnlockAnimationInteractor
@Inject
constructor(
    private val repository: InWindowLauncherUnlockAnimationRepository,
    @Application scope: CoroutineScope,
    transitionInteractor: KeyguardTransitionInteractor,
    surfaceBehindRepository: dagger.Lazy<KeyguardSurfaceBehindRepository>,
    private val activityManager: ActivityManagerWrapper,
) {
    val startedUnlockAnimation = repository.startedUnlockAnimation.asStateFlow()

    /**
     * Whether we've STARTED but not FINISHED a transition to GONE, and the preconditions are met to
     * play the in-window unlock animation.
     */
    val transitioningToGoneWithInWindowAnimation: StateFlow<Boolean> =
        transitionInteractor
            .isInTransitionToState(KeyguardState.GONE)
            .sample(repository.launcherActivityClass, ::Pair)
            .map { (isTransitioningToGone, launcherActivityClass) ->
                isTransitioningToGone && isActivityClassUnderneath(launcherActivityClass)
            }
            .stateIn(scope, SharingStarted.Eagerly, false)

    /**
     * Whether we should start the in-window unlock animation.
     *
     * This emits true once the Launcher surface becomes available while we're
     * [transitioningToGoneWithInWindowAnimation].
     */
    val shouldStartInWindowAnimation: StateFlow<Boolean> =
        combine(
                transitioningToGoneWithInWindowAnimation,
                surfaceBehindRepository.get().isSurfaceRemoteAnimationTargetAvailable,
            ) { transitioningWithInWindowAnimation, isSurfaceAvailable ->
                transitioningWithInWindowAnimation && isSurfaceAvailable
            }
            .stateIn(scope, SharingStarted.Eagerly, false)

    /** Sets whether we've started */
    fun setStartedUnlockAnimation(started: Boolean) {
        repository.setStartedUnlockAnimation(started)
    }

    fun setManualUnlockAmount(amount: Float) {
        repository.setManualUnlockAmount(amount)
    }

    fun setLauncherActivityClass(className: String) {
        repository.setLauncherActivityClass(className)
    }

    fun setLauncherSmartspaceState(state: SmartspaceState?) {
        repository.setLauncherSmartspaceState(state)
    }

    /**
     * Whether an activity with the given [activityClass] name is currently underneath the
     * lockscreen (it's at the top of the activity task stack).
     */
    private fun isActivityClassUnderneath(activityClass: String?): Boolean {
        return activityClass?.let {
            activityManager.runningTask?.topActivity?.className?.equals(it)
        }
            ?: false
    }
}
Loading