Loading packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java +7 −1 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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; Loading @@ -214,6 +217,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS mFeatureFlags = featureFlags; mKeyguardInteractor = keyguardInteractor; mKeyguardClockInteractor = keyguardClockInteractor; mInWindowLauncherUnlockAnimationManager = inWindowLauncherUnlockAnimationManager; mClockChangedListener = new ClockRegistry.ClockChangeListener() { @Override Loading Loading @@ -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); } Loading packages/SystemUI/src/com/android/systemui/keyguard/data/repository/InWindowLauncherUnlockAnimationRepository.kt 0 → 100644 +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 } } packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardSurfaceBehindRepository.kt +14 −0 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 } } packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt +13 −2 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -56,6 +57,7 @@ constructor( private val flags: FeatureFlags, private val shadeRepository: ShadeRepository, private val powerInteractor: PowerInteractor, inWindowLauncherUnlockAnimationInteractor: Lazy<InWindowLauncherUnlockAnimationInteractor>, ) : TransitionInteractor( fromState = KeyguardState.LOCKSCREEN, Loading Loading @@ -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( Loading packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/InWindowLauncherUnlockAnimationInteractor.kt 0 → 100644 +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
packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java +7 −1 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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; Loading @@ -214,6 +217,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS mFeatureFlags = featureFlags; mKeyguardInteractor = keyguardInteractor; mKeyguardClockInteractor = keyguardClockInteractor; mInWindowLauncherUnlockAnimationManager = inWindowLauncherUnlockAnimationManager; mClockChangedListener = new ClockRegistry.ClockChangeListener() { @Override Loading Loading @@ -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); } Loading
packages/SystemUI/src/com/android/systemui/keyguard/data/repository/InWindowLauncherUnlockAnimationRepository.kt 0 → 100644 +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 } }
packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardSurfaceBehindRepository.kt +14 −0 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 } }
packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt +13 −2 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -56,6 +57,7 @@ constructor( private val flags: FeatureFlags, private val shadeRepository: ShadeRepository, private val powerInteractor: PowerInteractor, inWindowLauncherUnlockAnimationInteractor: Lazy<InWindowLauncherUnlockAnimationInteractor>, ) : TransitionInteractor( fromState = KeyguardState.LOCKSCREEN, Loading Loading @@ -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( Loading
packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/InWindowLauncherUnlockAnimationInteractor.kt 0 → 100644 +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 } }