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

Commit 5fb8f031 authored by Hawkwood Glazier's avatar Hawkwood Glazier
Browse files

Execute fold animation using clock interactor

Bug: 329244366
Test: Manually checked fold animation
Flag: ACONFIG com.android.systemui.migrate_clocks_to_blueprint STAGING
Change-Id: I36fd71dfb918d4eb23eb8bd4d6b5f8c965f6c171
parent 5cdd221f
Loading
Loading
Loading
Loading
+8 −5
Original line number Original line Diff line number Diff line
@@ -29,8 +29,7 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.StateFlow


private val TAG = KeyguardClockInteractor::class.simpleName
private val TAG = KeyguardClockInteractor::class.simpleName
/** Manages keyguard clock for the lockscreen root view. */
/** Manages and ecapsulates the clock components of the lockscreen root view. */
/** Encapsulates business-logic related to the keyguard clock. */
@SysUISingleton
@SysUISingleton
class KeyguardClockInteractor
class KeyguardClockInteractor
@Inject
@Inject
@@ -46,6 +45,8 @@ constructor(


    val previewClock: Flow<ClockController> = keyguardClockRepository.previewClock
    val previewClock: Flow<ClockController> = keyguardClockRepository.previewClock


    val clockEventController: ClockEventController by keyguardClockRepository::clockEventController

    var clock: ClockController? by keyguardClockRepository.clockEventController::clock
    var clock: ClockController? by keyguardClockRepository.clockEventController::clock


    val clockSize: StateFlow<Int> = keyguardClockRepository.clockSize
    val clockSize: StateFlow<Int> = keyguardClockRepository.clockSize
@@ -53,8 +54,10 @@ constructor(
        keyguardClockRepository.setClockSize(size)
        keyguardClockRepository.setClockSize(size)
    }
    }


    val clockEventController: ClockEventController
    fun animateFoldToAod(foldFraction: Float) {
        get() {
        clock?.let { clock ->
            return keyguardClockRepository.clockEventController
            clock.smallClock.animations.fold(foldFraction)
            clock.largeClock.animations.fold(foldFraction)
        }
    }
    }
}
}
+99 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2024 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 android.animation.ValueAnimator
import android.view.ViewGroup
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.TransitionInfo
import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled
import com.android.systemui.shade.NotificationPanelViewController
import com.android.systemui.shade.ShadeFoldAnimator
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch

@SysUISingleton
class ToAodFoldTransitionInteractor
@Inject
constructor(
    private val keyguardClockInteractor: KeyguardClockInteractor,
    private val transitionInteractor: KeyguardTransitionInteractor,
    private val transitionRepository: KeyguardTransitionRepository,
    @Application private val mainScope: CoroutineScope,
    @Main private val mainDispatcher: CoroutineDispatcher,
) {
    private var parentAnimator: NotificationPanelViewController.ShadeFoldAnimatorImpl? = null

    // TODO(b/331770313): Migrate to PowerInteractor; Deprecate ShadeFoldAnimator again
    val foldAnimator =
        object : ShadeFoldAnimator {
            override val view: ViewGroup?
                get() = throw NotImplementedError("Deprecated. Do not call.")

            override fun prepareFoldToAodAnimation() {
                forceToAod()
                parentAnimator?.prepareFoldToAodAnimation()
            }

            override fun startFoldToAodAnimation(
                startAction: Runnable,
                endAction: Runnable,
                cancelAction: Runnable
            ) {
                parentAnimator?.let {
                    it.buildViewAnimator(startAction, endAction, cancelAction)
                        .setUpdateListener {
                            keyguardClockInteractor.animateFoldToAod(it.animatedFraction)
                        }
                        .start()
                }
            }

            override fun cancelFoldToAodAnimation() {
                parentAnimator?.cancelFoldToAodAnimation()
            }
        }

    fun initialize(parentAnimator: ShadeFoldAnimator) {
        this.parentAnimator =
            parentAnimator as NotificationPanelViewController.ShadeFoldAnimatorImpl?
    }

    /** Forces the keyguard into AOD or Doze */
    private fun forceToAod() {
        mainScope.launch(mainDispatcher) {
            transitionRepository.startTransition(
                TransitionInfo(
                    "$TAG (Fold transition triggered)",
                    transitionInteractor.getCurrentState(),
                    transitionInteractor.asleepKeyguardState.value,
                    ValueAnimator().apply { duration = 0 },
                    TransitionModeOnCanceled.LAST_VALUE,
                )
            )
        }
    }

    companion object {
        private val TAG = ToAodFoldTransitionInteractor::class.simpleName!!
    }
}
+26 −18
Original line number Original line Diff line number Diff line
@@ -437,7 +437,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
    private final FalsingManager mFalsingManager;
    private final FalsingManager mFalsingManager;
    private final FalsingCollector mFalsingCollector;
    private final FalsingCollector mFalsingCollector;
    private final ShadeHeadsUpTrackerImpl mShadeHeadsUpTracker = new ShadeHeadsUpTrackerImpl();
    private final ShadeHeadsUpTrackerImpl mShadeHeadsUpTracker = new ShadeHeadsUpTrackerImpl();
    private final ShadeFoldAnimator mShadeFoldAnimator = new ShadeFoldAnimatorImpl();
    private final ShadeFoldAnimatorImpl mShadeFoldAnimator = new ShadeFoldAnimatorImpl();


    private boolean mShowIconsWhenExpanded;
    private boolean mShowIconsWhenExpanded;
    private int mIndicationBottomPadding;
    private int mIndicationBottomPadding;
@@ -3310,19 +3310,19 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
    }
    }


    @Override
    @Override
    public ShadeFoldAnimator getShadeFoldAnimator() {
    public ShadeFoldAnimatorImpl getShadeFoldAnimator() {
        return mShadeFoldAnimator;
        return mShadeFoldAnimator;
    }
    }


    private final class ShadeFoldAnimatorImpl implements ShadeFoldAnimator {
    @Deprecated
    public final class ShadeFoldAnimatorImpl implements ShadeFoldAnimator {
        /** Updates the views to the initial state for the fold to AOD animation. */
        /** Updates the views to the initial state for the fold to AOD animation. */
        @Override
        @Override
        public void prepareFoldToAodAnimation() {
        public void prepareFoldToAodAnimation() {
            if (MigrateClocksToBlueprint.isEnabled()) {
            if (!MigrateClocksToBlueprint.isEnabled()) {
                return;
            }
                // Force show AOD UI even if we are not locked
                // Force show AOD UI even if we are not locked
                showAodUi();
                showAodUi();
            }


            // Move the content of the AOD all the way to the left
            // Move the content of the AOD all the way to the left
            // so we can animate to the initial position
            // so we can animate to the initial position
@@ -3340,14 +3340,29 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
         * @param cancelAction invoked when the animation is cancelled, before endAction.
         * @param cancelAction invoked when the animation is cancelled, before endAction.
         */
         */
        @Override
        @Override
        public void startFoldToAodAnimation(Runnable startAction, Runnable endAction,
        public void startFoldToAodAnimation(
                Runnable cancelAction) {
                Runnable startAction, Runnable endAction, Runnable cancelAction) {
            if (MigrateClocksToBlueprint.isEnabled()) {
            if (MigrateClocksToBlueprint.isEnabled()) {
                return;
                return;
            }
            }

            buildViewAnimator(startAction, endAction, cancelAction)
                    .setUpdateListener(anim -> mKeyguardStatusViewController
                            .animateFoldToAod(anim.getAnimatedFraction()))
                    .start();
        }

        /**
         * Builds the default NPVC fold animator
         *
         * @deprecated Temporary stop-gap. Do not use outside of keyguard fold transition.
         */
        @Deprecated
        public ViewPropertyAnimator buildViewAnimator(
                Runnable startAction, Runnable endAction, Runnable cancelAction) {
            final ViewPropertyAnimator viewAnimator = mView.animate();
            final ViewPropertyAnimator viewAnimator = mView.animate();
            viewAnimator.cancel();
            viewAnimator.cancel();
            viewAnimator
            return viewAnimator
                    .translationX(0)
                    .translationX(0)
                    .alpha(1f)
                    .alpha(1f)
                    .setDuration(ANIMATION_DURATION_FOLD_TO_AOD)
                    .setDuration(ANIMATION_DURATION_FOLD_TO_AOD)
@@ -3370,19 +3385,12 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump
                            viewAnimator.setListener(null);
                            viewAnimator.setListener(null);
                            viewAnimator.setUpdateListener(null);
                            viewAnimator.setUpdateListener(null);
                        }
                        }
                    })
                    });
                    .setUpdateListener(anim ->
                            mKeyguardStatusViewController.animateFoldToAod(
                                    anim.getAnimatedFraction()))
                    .start();
        }
        }


        /** Cancels fold to AOD transition and resets view state. */
        /** Cancels fold to AOD transition and resets view state. */
        @Override
        @Override
        public void cancelFoldToAodAnimation() {
        public void cancelFoldToAodAnimation() {
            if (MigrateClocksToBlueprint.isEnabled()) {
                return;
            }
            cancelAnimation();
            cancelAnimation();
            resetAlpha();
            resetAlpha();
            resetTranslation();
            resetTranslation();
+7 −5
Original line number Original line Diff line number Diff line
@@ -143,10 +143,11 @@ interface ShadeHeadsUpTracker {
}
}


/** Handles the lifecycle of the shade's animation that happens when folding a foldable. */
/** Handles the lifecycle of the shade's animation that happens when folding a foldable. */
@Deprecated("This interface should not be used in scene container.")
@Deprecated("This interface should not be used in scene container. Needs flexiglass equivalent.")
interface ShadeFoldAnimator {
interface ShadeFoldAnimator {
    /** Updates the views to the initial state for the fold to AOD animation. */
    /** Updates the views to the initial state for the fold to AOD animation. */
    @Deprecated("Not used when migrateClocksToBlueprint enabled") fun prepareFoldToAodAnimation()
    @Deprecated("Used by the Keyguard Fold Transition. Needs flexiglass equivalent.")
    fun prepareFoldToAodAnimation()


    /**
    /**
     * Starts fold to AOD animation.
     * Starts fold to AOD animation.
@@ -155,14 +156,15 @@ interface ShadeFoldAnimator {
     * @param endAction invoked when the animation finishes, also if it was cancelled.
     * @param endAction invoked when the animation finishes, also if it was cancelled.
     * @param cancelAction invoked when the animation is cancelled, before endAction.
     * @param cancelAction invoked when the animation is cancelled, before endAction.
     */
     */
    @Deprecated("Not used when migrateClocksToBlueprint enabled")
    @Deprecated("Not used when migrateClocksToBlueprint enabled.")
    fun startFoldToAodAnimation(startAction: Runnable, endAction: Runnable, cancelAction: Runnable)
    fun startFoldToAodAnimation(startAction: Runnable, endAction: Runnable, cancelAction: Runnable)


    /** Cancels fold to AOD transition and resets view state. */
    /** Cancels fold to AOD transition and resets view state. */
    @Deprecated("Not used when migrateClocksToBlueprint enabled") fun cancelFoldToAodAnimation()
    @Deprecated("Used by the Keyguard Fold Transition. Needs flexiglass equivalent.")
    fun cancelFoldToAodAnimation()


    /** Returns the main view of the shade. */
    /** Returns the main view of the shade. */
    @Deprecated("Not used in Scene Container") val view: ViewGroup?
    @Deprecated("Not used when migrateClocksToBlueprint enabled.") val view: ViewGroup?
}
}


/**
/**
+65 −54
Original line number Original line Diff line number Diff line
@@ -23,10 +23,11 @@ import android.os.PowerManager
import android.provider.Settings
import android.provider.Settings
import androidx.core.view.OneShotPreDrawListener
import androidx.core.view.OneShotPreDrawListener
import com.android.internal.util.LatencyTracker
import com.android.internal.util.LatencyTracker
import com.android.systemui.Flags.migrateClocksToBlueprint
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.keyguard.MigrateClocksToBlueprint
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.ToAodFoldTransitionInteractor
import com.android.systemui.shade.ShadeFoldAnimator
import com.android.systemui.shade.ShadeFoldAnimator
import com.android.systemui.shade.ShadeViewController
import com.android.systemui.shade.ShadeViewController
import com.android.systemui.statusbar.LightRevealScrim
import com.android.systemui.statusbar.LightRevealScrim
@@ -55,6 +56,7 @@ constructor(
    private val globalSettings: GlobalSettings,
    private val globalSettings: GlobalSettings,
    private val latencyTracker: LatencyTracker,
    private val latencyTracker: LatencyTracker,
    private val keyguardInteractor: Lazy<KeyguardInteractor>,
    private val keyguardInteractor: Lazy<KeyguardInteractor>,
    private val foldTransitionInteractor: Lazy<ToAodFoldTransitionInteractor>,
) : CallbackController<FoldAodAnimationStatus>, ScreenOffAnimation, WakefulnessLifecycle.Observer {
) : CallbackController<FoldAodAnimationStatus>, ScreenOffAnimation, WakefulnessLifecycle.Observer {


    private lateinit var shadeViewController: ShadeViewController
    private lateinit var shadeViewController: ShadeViewController
@@ -74,7 +76,7 @@ constructor(
    private val foldToAodLatencyTracker = FoldToAodLatencyTracker()
    private val foldToAodLatencyTracker = FoldToAodLatencyTracker()


    private val startAnimationRunnable = Runnable {
    private val startAnimationRunnable = Runnable {
        getShadeFoldAnimator().startFoldToAodAnimation(
        shadeFoldAnimator.startFoldToAodAnimation(
            /* startAction= */ { foldToAodLatencyTracker.onAnimationStarted() },
            /* startAction= */ { foldToAodLatencyTracker.onAnimationStarted() },
            /* endAction= */ { setAnimationState(playing = false) },
            /* endAction= */ { setAnimationState(playing = false) },
            /* cancelAction= */ { setAnimationState(playing = false) },
            /* cancelAction= */ { setAnimationState(playing = false) },
@@ -87,6 +89,7 @@ constructor(
        lightRevealScrim: LightRevealScrim,
        lightRevealScrim: LightRevealScrim,
    ) {
    ) {
        this.shadeViewController = shadeViewController
        this.shadeViewController = shadeViewController
        foldTransitionInteractor.get().initialize(shadeViewController.shadeFoldAnimator)


        deviceStateManager.registerCallback(mainExecutor, FoldListener())
        deviceStateManager.registerCallback(mainExecutor, FoldListener())
        wakefulnessLifecycle.addObserver(this)
        wakefulnessLifecycle.addObserver(this)
@@ -103,7 +106,7 @@ constructor(
    override fun startAnimation(): Boolean =
    override fun startAnimation(): Boolean =
        if (shouldStartAnimation()) {
        if (shouldStartAnimation()) {
            setAnimationState(playing = true)
            setAnimationState(playing = true)
            getShadeFoldAnimator().prepareFoldToAodAnimation()
            shadeFoldAnimator.prepareFoldToAodAnimation()
            true
            true
        } else {
        } else {
            setAnimationState(playing = false)
            setAnimationState(playing = false)
@@ -114,14 +117,20 @@ constructor(
        if (isAnimationPlaying) {
        if (isAnimationPlaying) {
            foldToAodLatencyTracker.cancel()
            foldToAodLatencyTracker.cancel()
            cancelAnimation?.run()
            cancelAnimation?.run()
            getShadeFoldAnimator().cancelFoldToAodAnimation()
            shadeFoldAnimator.cancelFoldToAodAnimation()
        }
        }


        setAnimationState(playing = false)
        setAnimationState(playing = false)
    }
    }


    private fun getShadeFoldAnimator(): ShadeFoldAnimator =
    private val shadeFoldAnimator: ShadeFoldAnimator
        get() {
            return if (MigrateClocksToBlueprint.isEnabled) {
                foldTransitionInteractor.get().foldAnimator
            } else {
                shadeViewController.shadeFoldAnimator
                shadeViewController.shadeFoldAnimator
            }
        }


    private fun setAnimationState(playing: Boolean) {
    private fun setAnimationState(playing: Boolean) {
        shouldPlayAnimation = playing
        shouldPlayAnimation = playing
@@ -137,28 +146,32 @@ constructor(
     * @see [com.android.systemui.keyguard.KeyguardViewMediator]
     * @see [com.android.systemui.keyguard.KeyguardViewMediator]
     */
     */
    @BinderThread
    @BinderThread
    fun onScreenTurningOn(onReady: Runnable) = mainExecutor.execute {
    fun onScreenTurningOn(onReady: Runnable) =
        mainExecutor.execute {
            if (shouldPlayAnimation) {
            if (shouldPlayAnimation) {
                // The device was not dozing and going to sleep after folding, play the animation
                // The device was not dozing and going to sleep after folding, play the animation

                if (isScrimOpaque) {
                if (isScrimOpaque) {
                    onReady.run()
                    onReady.run()
                } else {
                } else {
                    pendingScrimReadyCallback = onReady
                    pendingScrimReadyCallback = onReady
                }
                }
        } else if (isFolded && !isFoldHandled && alwaysOnEnabled &&
            } else if (
                keyguardInteractor.get().isDozing.value) {
                isFolded &&
                    !isFoldHandled &&
                    alwaysOnEnabled &&
                    keyguardInteractor.get().isDozing.value
            ) {
                setAnimationState(playing = true)
                setAnimationState(playing = true)
            getShadeFoldAnimator().prepareFoldToAodAnimation()
                shadeFoldAnimator.prepareFoldToAodAnimation()


                // We don't need to wait for the scrim as it is already displayed
                // We don't need to wait for the scrim as it is already displayed
                // but we should wait for the initial animation preparations to be drawn
                // but we should wait for the initial animation preparations to be drawn
                // (setting initial alpha/translation)
                // (setting initial alpha/translation)
                // TODO(b/254878364): remove this call to NPVC.getView()
                // TODO(b/254878364): remove this call to NPVC.getView()
            if (!migrateClocksToBlueprint()) {
                if (!MigrateClocksToBlueprint.isEnabled) {
                getShadeFoldAnimator().view?.let {
                    shadeFoldAnimator.view?.let { OneShotPreDrawListener.add(it, onReady) }
                    OneShotPreDrawListener.add(it, onReady)
                } else {
                }
                    onReady.run()
                }
                }
            } else {
            } else {
                // No animation, call ready callback immediately
                // No animation, call ready callback immediately
@@ -184,15 +197,14 @@ constructor(
    }
    }


    @BinderThread
    @BinderThread
    fun onScreenTurnedOn() = mainExecutor.execute {
    fun onScreenTurnedOn() =
        mainExecutor.execute {
            if (shouldPlayAnimation) {
            if (shouldPlayAnimation) {
                cancelAnimation?.run()
                cancelAnimation?.run()


                // Post starting the animation to the next frame to avoid junk due to inset changes
                // Post starting the animation to the next frame to avoid junk due to inset changes
            cancelAnimation = mainExecutor.executeDelayed(
                cancelAnimation =
                startAnimationRunnable,
                    mainExecutor.executeDelayed(startAnimationRunnable, /* delayMillis= */ 0)
                /* delayMillis= */ 0
            )
                shouldPlayAnimation = false
                shouldPlayAnimation = false
            }
            }
        }
        }
@@ -247,7 +259,6 @@ constructor(
     * Tracks the latency of fold to AOD using [LatencyTracker].
     * Tracks the latency of fold to AOD using [LatencyTracker].
     *
     *
     * Events that trigger start and end are:
     * Events that trigger start and end are:
     *
     * - Start: Once [DeviceStateManager] sends the folded signal [FoldToAodLatencyTracker.onFolded]
     * - Start: Once [DeviceStateManager] sends the folded signal [FoldToAodLatencyTracker.onFolded]
     *   is called and latency tracking starts.
     *   is called and latency tracking starts.
     * - End: Once the fold -> AOD animation starts, [FoldToAodLatencyTracker.onAnimationStarted] is
     * - End: Once the fold -> AOD animation starts, [FoldToAodLatencyTracker.onAnimationStarted] is
Loading