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

Commit 4b40d32a authored by Nicolo' Mazzucato's avatar Nicolo' Mazzucato
Browse files

Animate notification shade during unfold

Adds some constant translation to elements of the notification shade during fold/unfold. This is only applied when the device is in natural rotation.

KeyguardUnfoldTransition was doing the same: the logic has been extracted in UnfoldConstantTranslateAnimator, that is now used by both classes.
KeyguardUnfoldTransition's tests related to the animator have been moved to UnfoldConstantTranslateAnimatorTest.kt.

+ Minor formatting issues fixed (using ktfmt)

Bug: 201411030
Test: atest UnfoldConstantTranslateAnimatorTest KeyguardUnfoldTransitionTest + Tested manually on lockscreen and notification shade
Change-Id: Ic8d4cf5d7b2a9f8fc366842623c87c15cf79766f
parent d42a4f09
Loading
Loading
Loading
Loading
+96 −0
Original line number 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.shared.animation

import android.view.View
import android.view.ViewGroup
import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator.ViewIdToTranslate
import com.android.systemui.unfold.UnfoldTransitionProgressProvider
import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
import java.lang.ref.WeakReference

/**
 * Translates items away/towards the hinge when the device is opened/closed, according to the
 * direction specified in [ViewIdToTranslate.direction], for a maximum of [translationMax] when
 * progresses are 0.
 */
class UnfoldConstantTranslateAnimator(
    private val viewsIdToTranslate: Set<ViewIdToTranslate>,
    private val progressProvider: UnfoldTransitionProgressProvider
) : TransitionProgressListener {

    private var viewsToTranslate = listOf<ViewToTranslate>()
    private lateinit var rootView: ViewGroup
    private var translationMax = 0f

    fun init(rootView: ViewGroup, translationMax: Float) {
        this.rootView = rootView
        this.translationMax = translationMax
        progressProvider.addCallback(this)
    }

    override fun onTransitionStarted() {
        registerViewsForAnimation(rootView, viewsIdToTranslate)
    }

    override fun onTransitionProgress(progress: Float) {
        translateViews(progress)
    }

    override fun onTransitionFinished() {
        translateViews(progress = 1f)
    }

    private fun translateViews(progress: Float) {
        // progress == 0 -> -translationMax
        // progress == 1 -> 0
        val xTrans = (progress - 1f) * translationMax
        viewsToTranslate.forEach { (view, direction, shouldBeAnimated) ->
            if (shouldBeAnimated()) {
                view.get()?.translationX = xTrans * direction.multiplier
            }
        }
    }

    /** Finds in [parent] all views specified by [ids] and register them for the animation. */
    private fun registerViewsForAnimation(parent: ViewGroup, ids: Set<ViewIdToTranslate>) {
        viewsToTranslate =
            ids.mapNotNull { (id, dir, pred) ->
                parent.findViewById<View>(id)?.let { view ->
                    ViewToTranslate(WeakReference(view), dir, pred)
                }
            }
    }

    /** Represents a view to animate. [rootView] should contain a view with [viewId] inside. */
    data class ViewIdToTranslate(
        val viewId: Int,
        val direction: Direction,
        val shouldBeAnimated: () -> Boolean = { true }
    )

    private data class ViewToTranslate(
        val view: WeakReference<View>,
        val direction: Direction,
        val shouldBeAnimated: () -> Boolean
    )

    /** Direction of the animation. */
    enum class Direction(val multiplier: Float) {
        LEFT(-1f),
        RIGHT(1f),
    }
}
+27 −72
Original line number Diff line number Diff line
@@ -17,11 +17,13 @@
package com.android.keyguard

import android.content.Context
import android.view.View
import android.view.ViewGroup
import com.android.systemui.R
import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator
import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator.Direction.LEFT
import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator.Direction.RIGHT
import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator.ViewIdToTranslate
import com.android.systemui.unfold.SysUIUnfoldScope
import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider
import javax.inject.Inject

@@ -30,84 +32,37 @@ import javax.inject.Inject
 * the set of ids, which also dictact which direction to move and when, via a filter function.
 */
@SysUIUnfoldScope
class KeyguardUnfoldTransition @Inject constructor(
    val context: Context,
    val unfoldProgressProvider: NaturalRotationUnfoldProgressProvider
class KeyguardUnfoldTransition
@Inject
constructor(
    private val context: Context,
    unfoldProgressProvider: NaturalRotationUnfoldProgressProvider
) {

    companion object {
        final val LEFT = -1
        final val RIGHT = 1
    }
    /** Certain views only need to move if they are not currently centered */
    var statusViewCentered = false

    private val filterSplitShadeOnly = { !statusViewCentered }
    private val filterNever = { true }

    private val ids = setOf(
        Triple(R.id.keyguard_status_area, LEFT, filterNever),
        Triple(R.id.controls_button, LEFT, filterNever),
        Triple(R.id.lockscreen_clock_view_large, LEFT, filterSplitShadeOnly),
        Triple(R.id.lockscreen_clock_view, LEFT, filterNever),
        Triple(R.id.notification_stack_scroller, RIGHT, filterSplitShadeOnly),
        Triple(R.id.wallet_button, RIGHT, filterNever)
    )
    private var parent: ViewGroup? = null
    private var views = listOf<Triple<View, Int, () -> Boolean>>()
    private var xTranslationMax = 0f

    /**
     * Certain views only need to move if they are not currently centered
     */
    var statusViewCentered = false

    init {
        unfoldProgressProvider.addCallback(
            object : TransitionProgressListener {
                override fun onTransitionStarted() {
                    findViews()
                }

                override fun onTransitionProgress(progress: Float) {
                    translateViews(progress)
                }

                override fun onTransitionFinished() {
                    translateViews(1f)
                }
            }
        )
    private val translateAnimator by lazy {
        UnfoldConstantTranslateAnimator(
            viewsIdToTranslate =
                setOf(
                    ViewIdToTranslate(R.id.keyguard_status_area, LEFT, filterNever),
                    ViewIdToTranslate(R.id.controls_button, LEFT, filterNever),
                    ViewIdToTranslate(R.id.lockscreen_clock_view_large, LEFT, filterSplitShadeOnly),
                    ViewIdToTranslate(R.id.lockscreen_clock_view, LEFT, filterNever),
                    ViewIdToTranslate(
                        R.id.notification_stack_scroller, RIGHT, filterSplitShadeOnly),
                    ViewIdToTranslate(R.id.wallet_button, RIGHT, filterNever)),
            progressProvider = unfoldProgressProvider)
    }

    /**
     * Relies on the [parent] to locate views to translate
     */
    /** Relies on the [parent] to locate views to translate. */
    fun setup(parent: ViewGroup) {
        this.parent = parent
        xTranslationMax = context.resources.getDimensionPixelSize(
            R.dimen.keyguard_unfold_translation_x).toFloat()
    }

    /**
     * Manually translate views based on set direction. At the moment
     * [UnfoldMoveFromCenterAnimator] exists but moves all views a dynamic distance
     * from their mid-point. This code instead will only ever translate by a fixed amount.
     */
    private fun translateViews(progress: Float) {
        val xTrans = progress * xTranslationMax - xTranslationMax
        views.forEach {
            (view, direction, pred) -> if (pred()) {
                view.setTranslationX(xTrans * direction)
            }
        }
    }

    private fun findViews() {
        parent?.let { p ->
            views = ids.mapNotNull {
                (id, direction, pred) -> p.findViewById<View>(id)?.let {
                    Triple(it, direction, pred)
                }
            }
        }
        val translationMax =
            context.resources.getDimensionPixelSize(R.dimen.keyguard_unfold_translation_x).toFloat()
        translateAnimator.init(parent, translationMax)
    }
}
+52 −0
Original line number 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.statusbar.phone

import android.content.Context
import android.view.ViewGroup
import com.android.systemui.R
import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator
import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator.Direction.LEFT
import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator.Direction.RIGHT
import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator.ViewIdToTranslate
import com.android.systemui.unfold.SysUIUnfoldScope
import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider
import javax.inject.Inject

@SysUIUnfoldScope
class NotificationPanelUnfoldAnimationController
@Inject
constructor(private val context: Context, progressProvider: NaturalRotationUnfoldProgressProvider) {

    private val translateAnimator by lazy {
        UnfoldConstantTranslateAnimator(
            viewsIdToTranslate =
                setOf(
                    ViewIdToTranslate(R.id.quick_settings_panel, LEFT),
                    ViewIdToTranslate(R.id.notification_stack_scroller, RIGHT),
                    ViewIdToTranslate(R.id.rightLayout, RIGHT),
                    ViewIdToTranslate(R.id.clock, LEFT),
                    ViewIdToTranslate(R.id.date, LEFT)),
            progressProvider = progressProvider)
    }

    fun setup(root: ViewGroup) {
        val translationMax =
            context.resources.getDimensionPixelSize(R.dimen.notification_side_paddings).toFloat()
        translateAnimator.init(root, translationMax)
    }
}
+7 −0
Original line number Diff line number Diff line
@@ -669,6 +669,8 @@ public class NotificationPanelViewController extends PanelViewController
    private boolean mStatusViewCentered = true;

    private Optional<KeyguardUnfoldTransition> mKeyguardUnfoldTransition;
    private Optional<NotificationPanelUnfoldAnimationController>
            mNotificationPanelUnfoldAnimationController;

    private final ListenerSet<Callbacks> mNotifEventSourceCallbacks = new ListenerSet<>();

@@ -929,6 +931,8 @@ public class NotificationPanelViewController extends PanelViewController

        mMaxKeyguardNotifications = resources.getInteger(R.integer.keyguard_max_notification_count);
        mKeyguardUnfoldTransition = unfoldComponent.map(c -> c.getKeyguardUnfoldTransition());
        mNotificationPanelUnfoldAnimationController = unfoldComponent.map(
                SysUIUnfoldComponent::getNotificationPanelUnfoldAnimationController);

        mCommunalSourceMonitorCallback = (source) -> {
            mUiExecutor.execute(() -> setCommunalSource(source));
@@ -1064,6 +1068,8 @@ public class NotificationPanelViewController extends PanelViewController

        mTapAgainViewController.init();
        mKeyguardUnfoldTransition.ifPresent(u -> u.setup(mView));
        mNotificationPanelUnfoldAnimationController.ifPresent(controller ->
                controller.setup(mNotificationContainerParent));
    }

    @Override
@@ -1319,6 +1325,7 @@ public class NotificationPanelViewController extends PanelViewController
        setKeyguardBottomAreaVisibility(mBarState, false);

        mKeyguardUnfoldTransition.ifPresent(u -> u.setup(mView));
        mNotificationPanelUnfoldAnimationController.ifPresent(u -> u.setup(mView));
    }

    private void attachSplitShadeMediaPlayerContainer(FrameLayout container) {
+3 −0
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.systemui.unfold

import com.android.keyguard.KeyguardUnfoldTransition
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.statusbar.phone.NotificationPanelUnfoldAnimationController
import com.android.systemui.statusbar.phone.StatusBarMoveFromCenterAnimationController
import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider
import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider
@@ -85,6 +86,8 @@ interface SysUIUnfoldComponent {

    fun getStatusBarMoveFromCenterAnimationController(): StatusBarMoveFromCenterAnimationController

    fun getNotificationPanelUnfoldAnimationController(): NotificationPanelUnfoldAnimationController

    fun getFoldAodAnimationController(): FoldAodAnimationController

    fun getUnfoldTransitionWallpaperController(): UnfoldTransitionWallpaperController
Loading