Merge "Added TransientTaskbarStashController implementation." into main

This commit is contained in:
Mykola Podolian
2024-07-26 16:59:46 +00:00
committed by Android (Google) Code Review
6 changed files with 753 additions and 45 deletions
@@ -81,12 +81,7 @@ interface BubbleStashController {
controllersAfterInitAction: ControllersAfterInitAction
)
/** Sets stashed and expanded state of the bubble bar */
fun updateStashedAndExpandedState(stash: Boolean = false, expand: Boolean = false)
/**
* Shows the bubble bar at [getBubbleBarTranslationY] position immediately without animation.
*/
/** Shows the bubble bar at [bubbleBarTranslationY] position immediately without animation. */
fun showBubbleBarImmediate()
/** Shows the bubble bar at [bubbleBarTranslationY] position immediately without animation. */
@@ -120,21 +115,17 @@ interface BubbleStashController {
* Stashes the bubble bar (transform to the handle view), or just shrink width of the expanded
* bubble bar based on the controller implementation.
*/
fun stashBubbleBar() {
updateStashedAndExpandedState(stash = true, expand = false)
}
fun stashBubbleBar()
/** Shows the bubble bar, and expands bubbles depending on [expandBubbles]. */
fun showBubbleBar(expandBubbles: Boolean) {
updateStashedAndExpandedState(stash = false, expandBubbles)
}
fun showBubbleBar(expandBubbles: Boolean)
// TODO(b/354218264): Move to BubbleBarViewAnimator
/**
* The difference on the Y axis between the center of the handle and the center of the bubble
* bar.
*/
fun getSlideInAnimationDistanceY(): Float
fun getDiffBetweenHandleAndBarCenters(): Float
// TODO(b/354218264): Move to BubbleBarViewAnimator
/** The distance the handle moves as part of the new bubble animation. */
@@ -190,13 +181,5 @@ interface BubbleStashController {
/** The scale bubble bar animates to when being stashed. */
const val STASHED_BAR_SCALE = 0.5f
/** Creates new instance of [BubbleStashController] */
@JvmStatic
fun newInstance(
taskbarHotseatDimensionsProvider: TaskbarHotseatDimensionsProvider
): BubbleStashController {
return PersistentTaskbarStashController(taskbarHotseatDimensionsProvider)
}
}
}
@@ -33,7 +33,7 @@ import com.android.launcher3.util.MultiPropertyFactory
import com.android.wm.shell.common.bubbles.BubbleBarLocation
import com.android.wm.shell.shared.animation.PhysicsAnimator
class PersistentTaskbarStashController(
class PersistentBubbleStashController(
private val taskbarHotseatDimensionsProvider: TaskbarHotseatDimensionsProvider,
) : BubbleStashController {
@@ -54,7 +54,7 @@ class PersistentTaskbarStashController(
}
if (onHome) {
// When transition to home we should show collapse the bubble bar
updateStashedAndExpandedState(stash = false, expand = false)
updateExpandedState(expand = false)
}
animateBubbleBarY()
bubbleBarViewController.onBubbleBarConfigurationChanged(/* animate= */ true)
@@ -66,7 +66,7 @@ class PersistentTaskbarStashController(
field = onOverview
if (!onOverview) {
// When transition from overview we should show collapse the bubble bar
updateStashedAndExpandedState(stash = false, expand = false)
updateExpandedState(expand = false)
}
bubbleBarViewController.onBubbleBarConfigurationChanged(/* animate= */ true)
}
@@ -114,7 +114,8 @@ class PersistentTaskbarStashController(
this.bubbleBarViewController = bubbleBarViewController
this.controllersAfterInitAction = controllersAfterInitAction
bubbleBarTranslationYAnimator = bubbleBarViewController.bubbleBarTranslationY
bubbleBarAlphaAnimator = bubbleBarViewController.bubbleBarAlpha.get(0)
// bubble bar has only alpha property, getting it at index 0
bubbleBarAlphaAnimator = bubbleBarViewController.bubbleBarAlpha.get(/* index= */ 0)
bubbleBarScaleAnimator = bubbleBarViewController.bubbleBarScale
}
@@ -131,16 +132,6 @@ class PersistentTaskbarStashController(
animatorSet.setDuration(BAR_STASH_DURATION).start()
}
override fun updateStashedAndExpandedState(stash: Boolean, expand: Boolean) {
if (bubbleBarViewController.isHiddenForNoBubbles) {
// If there are no bubbles the bar is invisible, nothing to do here.
return
}
if (bubbleBarViewController.isExpanded != expand) {
bubbleBarViewController.isExpanded = expand
}
}
override fun showBubbleBarImmediate() = showBubbleBarImmediate(bubbleBarTranslationY)
override fun showBubbleBarImmediate(bubbleBarTranslationY: Float) {
@@ -154,6 +145,14 @@ class PersistentTaskbarStashController(
// operation is performed.
}
override fun stashBubbleBar() {
updateExpandedState(expand = false)
}
override fun showBubbleBar(expandBubbles: Boolean) {
updateExpandedState(expandBubbles)
}
override fun stashBubbleBarImmediate() {
// When the bubble bar is shown for the persistent task bar, there is no handle view, so no
// operation is performed.
@@ -176,7 +175,7 @@ class PersistentTaskbarStashController(
override fun isEventOverBubbleBarViews(ev: MotionEvent): Boolean =
bubbleBarViewController.isEventOverAnyItem(ev)
override fun getSlideInAnimationDistanceY(): Float {
override fun getDiffBetweenHandleAndBarCenters(): Float {
// distance from the bottom of the screen and the bubble bar center.
return -bubbleBarViewController.bubbleBarCollapsedHeight / 2f
}
@@ -199,6 +198,16 @@ class PersistentTaskbarStashController(
// no op since does not have a handle view
}
private fun updateExpandedState(expand: Boolean) {
if (bubbleBarViewController.isHiddenForNoBubbles) {
// If there are no bubbles the bar is invisible, nothing to do here.
return
}
if (bubbleBarViewController.isExpanded != expand) {
bubbleBarViewController.isExpanded = expand
}
}
/** Animates bubble bar Y accordingly to the showing mode */
private fun animateBubbleBarY() {
val animator =
@@ -0,0 +1,373 @@
/*
* 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.launcher3.taskbar.bubbles.stashing
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.AnimatorSet
import android.content.res.Resources
import android.view.MotionEvent
import android.view.View
import androidx.annotation.VisibleForTesting
import com.android.launcher3.R
import com.android.launcher3.anim.AnimatedFloat
import com.android.launcher3.taskbar.StashedHandleViewController
import com.android.launcher3.taskbar.TaskbarInsetsController
import com.android.launcher3.taskbar.bubbles.BubbleBarViewController
import com.android.launcher3.taskbar.bubbles.BubbleStashedHandleViewController
import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController.Companion.BAR_STASH_DURATION
import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController.Companion.BAR_TRANSLATION_DURATION
import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController.Companion.STASHED_BAR_SCALE
import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController.ControllersAfterInitAction
import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController.TaskbarHotseatDimensionsProvider
import com.android.launcher3.util.MultiPropertyFactory
import com.android.wm.shell.common.bubbles.BubbleBarLocation
import com.android.wm.shell.shared.animation.PhysicsAnimator
class TransientBubbleStashController(
private val taskbarHotseatDimensionsProvider: TaskbarHotseatDimensionsProvider,
resources: Resources
) : BubbleStashController {
private lateinit var bubbleBarViewController: BubbleBarViewController
private lateinit var taskbarInsetsController: TaskbarInsetsController
private lateinit var controllersAfterInitAction: ControllersAfterInitAction
// stash view properties
private var bubbleStashedHandleViewController: BubbleStashedHandleViewController? = null
private var stashHandleViewAlpha: MultiPropertyFactory<View>.MultiProperty? = null
private var stashedHeight: Int = 0
// bubble bar properties
private lateinit var bubbleBarAlpha: MultiPropertyFactory<View>.MultiProperty
private lateinit var bubbleBarTranslationYAnimator: AnimatedFloat
private lateinit var bubbleBarScale: AnimatedFloat
private val mHandleCenterFromScreenBottom =
resources.getDimensionPixelSize(R.dimen.bubblebar_stashed_size) / 2f
private var animator: AnimatorSet? = null
override var isStashed: Boolean = false
@VisibleForTesting set
override var isBubblesShowingOnHome: Boolean = false
set(onHome) {
if (field == onHome) return
field = onHome
if (!bubbleBarViewController.hasBubbles()) {
// if there are no bubbles, there's nothing to show, so just return.
return
}
if (onHome) {
updateStashedAndExpandedState(stash = false, expand = false)
// When transitioning from app to home we need to animate the bubble bar
// here to align with hotseat center.
animateBubbleBarYToHotseat()
} else if (!bubbleBarViewController.isExpanded) {
updateStashedAndExpandedState(stash = true, expand = false)
}
bubbleBarViewController.onBubbleBarConfigurationChanged(/* animate= */ true)
}
override var isBubblesShowingOnOverview: Boolean = false
set(onOverview) {
if (field == onOverview) return
field = onOverview
if (onOverview) {
// When transitioning to overview we need to animate the bubble bar to align with
// the taskbar bottom.
animateBubbleBarYToTaskbar()
} else {
updateStashedAndExpandedState(stash = true, expand = false)
}
bubbleBarViewController.onBubbleBarConfigurationChanged(/* animate= */ true)
}
override var isSysuiLocked: Boolean = false
set(isLocked) {
if (field == isLocked) return
field = isLocked
if (!isLocked && bubbleBarViewController.hasBubbles()) {
animateAfterUnlock()
}
}
override val isTransientTaskBar: Boolean = true
override val bubbleBarTranslationYForHotseat: Float
get() {
val hotseatBottomSpace = taskbarHotseatDimensionsProvider.getHotseatBottomSpace()
val hotseatCellHeight = taskbarHotseatDimensionsProvider.getHotseatHeight()
val bubbleBarHeight: Float = bubbleBarViewController.bubbleBarCollapsedHeight
return -hotseatBottomSpace - (hotseatCellHeight - bubbleBarHeight) / 2
}
override val bubbleBarTranslationYForTaskbar: Float =
-taskbarHotseatDimensionsProvider.getTaskbarBottomSpace().toFloat()
/** Check if we have handle view controller */
override val hasHandleView: Boolean
get() = bubbleStashedHandleViewController != null
override fun init(
taskbarInsetsController: TaskbarInsetsController,
bubbleBarViewController: BubbleBarViewController,
bubbleStashedHandleViewController: BubbleStashedHandleViewController?,
controllersAfterInitAction: ControllersAfterInitAction
) {
this.taskbarInsetsController = taskbarInsetsController
this.bubbleBarViewController = bubbleBarViewController
this.bubbleStashedHandleViewController = bubbleStashedHandleViewController
this.controllersAfterInitAction = controllersAfterInitAction
bubbleBarTranslationYAnimator = bubbleBarViewController.bubbleBarTranslationY
// bubble bar has only alpha property, getting it at index 0
bubbleBarAlpha = bubbleBarViewController.bubbleBarAlpha.get(/* index= */ 0)
bubbleBarScale = bubbleBarViewController.bubbleBarScale
stashedHeight = bubbleStashedHandleViewController?.stashedHeight ?: 0
stashHandleViewAlpha =
bubbleStashedHandleViewController
?.stashedHandleAlpha
?.get(StashedHandleViewController.ALPHA_INDEX_STASHED)
}
private fun animateAfterUnlock() {
val animatorSet = AnimatorSet()
if (isBubblesShowingOnHome || isBubblesShowingOnOverview) {
isStashed = false
animatorSet.playTogether(
bubbleBarScale.animateToValue(1f),
bubbleBarTranslationYAnimator.animateToValue(bubbleBarTranslationY),
bubbleBarAlpha.animateToValue(1f)
)
} else {
isStashed = true
stashHandleViewAlpha?.let { animatorSet.playTogether(it.animateToValue(1f)) }
}
animatorSet.updateTouchRegionOnAnimationEnd().setDuration(BAR_STASH_DURATION).start()
}
override fun showBubbleBarImmediate() {
showBubbleBarImmediate(bubbleBarTranslationY)
}
override fun showBubbleBarImmediate(bubbleBarTranslationY: Float) {
bubbleStashedHandleViewController?.setTranslationYForSwipe(0f)
stashHandleViewAlpha?.value = 0f
this.bubbleBarTranslationYAnimator.updateValue(bubbleBarTranslationY)
bubbleBarAlpha.setValue(1f)
bubbleBarScale.updateValue(1f)
isStashed = false
onIsStashedChanged()
}
override fun stashBubbleBarImmediate() {
bubbleStashedHandleViewController?.setTranslationYForSwipe(0f)
stashHandleViewAlpha?.value = 1f
this.bubbleBarTranslationYAnimator.updateValue(getStashTranslation())
bubbleBarAlpha.setValue(0f)
bubbleBarScale.updateValue(STASHED_BAR_SCALE)
isStashed = true
onIsStashedChanged()
}
override fun getTouchableHeight(): Int =
when {
isStashed -> stashedHeight
isBubbleBarVisible() -> bubbleBarViewController.bubbleBarCollapsedHeight.toInt()
else -> 0
}
override fun isBubbleBarVisible(): Boolean = bubbleBarViewController.hasBubbles() && !isStashed
override fun onNewBubbleAnimationInterrupted(isStashed: Boolean, bubbleBarTranslationY: Float) =
if (isStashed) {
stashBubbleBarImmediate()
} else {
showBubbleBarImmediate(bubbleBarTranslationY)
}
/** Check if [ev] belongs to the stash handle or the bubble bar views. */
override fun isEventOverBubbleBarViews(ev: MotionEvent): Boolean {
val isOverHandle = bubbleStashedHandleViewController?.isEventOverHandle(ev) ?: false
return isOverHandle || bubbleBarViewController.isEventOverAnyItem(ev)
}
/** Set the bubble bar stash handle location . */
override fun setBubbleBarLocation(bubbleBarLocation: BubbleBarLocation) {
bubbleStashedHandleViewController?.setBubbleBarLocation(bubbleBarLocation)
}
override fun stashBubbleBar() {
updateStashedAndExpandedState(stash = true, expand = false)
}
override fun showBubbleBar(expandBubbles: Boolean) {
updateStashedAndExpandedState(stash = false, expandBubbles)
}
override fun getDiffBetweenHandleAndBarCenters(): Float {
// the difference between the centers of the handle and the bubble bar is the difference
// between their distance from the bottom of the screen.
val barCenter: Float = bubbleBarViewController.bubbleBarCollapsedHeight / 2f
return mHandleCenterFromScreenBottom - barCenter
}
override fun getStashedHandleTranslationForNewBubbleAnimation(): Float {
return -mHandleCenterFromScreenBottom
}
override fun getStashedHandlePhysicsAnimator(): PhysicsAnimator<View>? {
return bubbleStashedHandleViewController?.physicsAnimator
}
override fun updateTaskbarTouchRegion() {
taskbarInsetsController.onTaskbarOrBubblebarWindowHeightOrInsetsChanged()
}
override fun setHandleTranslationY(translationY: Float) {
bubbleStashedHandleViewController?.setTranslationYForSwipe(translationY)
}
private fun getStashTranslation(): Float {
return (bubbleBarViewController.bubbleBarCollapsedHeight - stashedHeight) / 2f
}
/**
* Create a stash animation.
*
* @param isStashed whether it's a stash animation or an unstash animation
* @param duration duration of the animation
* @return the animation
*/
@Suppress("SameParameterValue")
private fun createStashAnimator(isStashed: Boolean, duration: Long): AnimatorSet {
val animatorSet = AnimatorSet()
val fullLengthAnimatorSet = AnimatorSet()
// Not exactly half and may overlap. See [first|second]HalfDurationScale below.
val firstHalfAnimatorSet = AnimatorSet()
val secondHalfAnimatorSet = AnimatorSet()
val firstHalfDurationScale: Float
val secondHalfDurationScale: Float
val stashHandleAlphaValue: Float
if (isStashed) {
firstHalfDurationScale = 0.75f
secondHalfDurationScale = 0.5f
stashHandleAlphaValue = 1f
fullLengthAnimatorSet.play(
bubbleBarTranslationYAnimator.animateToValue(getStashTranslation())
)
firstHalfAnimatorSet.playTogether(
bubbleBarAlpha.animateToValue(0f),
bubbleBarScale.animateToValue(STASHED_BAR_SCALE)
)
} else {
firstHalfDurationScale = 0.5f
secondHalfDurationScale = 0.75f
stashHandleAlphaValue = 0f
fullLengthAnimatorSet.playTogether(
bubbleBarScale.animateToValue(1f),
bubbleBarTranslationYAnimator.animateToValue(bubbleBarTranslationY)
)
secondHalfAnimatorSet.playTogether(bubbleBarAlpha.animateToValue(1f))
}
stashHandleViewAlpha?.let {
secondHalfAnimatorSet.playTogether(it.animateToValue(stashHandleAlphaValue))
}
bubbleStashedHandleViewController?.createRevealAnimToIsStashed(isStashed)?.let {
fullLengthAnimatorSet.play(it)
}
fullLengthAnimatorSet.setDuration(duration)
firstHalfAnimatorSet.setDuration((duration * firstHalfDurationScale).toLong())
secondHalfAnimatorSet.setDuration((duration * secondHalfDurationScale).toLong())
secondHalfAnimatorSet.startDelay = (duration * (1 - secondHalfDurationScale)).toLong()
animatorSet.playTogether(fullLengthAnimatorSet, firstHalfAnimatorSet, secondHalfAnimatorSet)
animatorSet.addListener(
object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) {
animator = null
controllersAfterInitAction.runAfterInit {
if (isStashed) {
bubbleBarViewController.isExpanded = false
}
taskbarInsetsController.onTaskbarOrBubblebarWindowHeightOrInsetsChanged()
}
}
}
)
return animatorSet
}
private fun onIsStashedChanged() {
controllersAfterInitAction.runAfterInit {
taskbarInsetsController.onTaskbarOrBubblebarWindowHeightOrInsetsChanged()
bubbleStashedHandleViewController?.onIsStashedChanged()
}
}
private fun animateBubbleBarYToHotseat() {
translateBubbleBarYUpdateTouchRegionOnCompletion(bubbleBarTranslationYForHotseat)
}
private fun animateBubbleBarYToTaskbar() {
translateBubbleBarYUpdateTouchRegionOnCompletion(bubbleBarTranslationYForTaskbar)
}
private fun translateBubbleBarYUpdateTouchRegionOnCompletion(toY: Float) {
bubbleBarViewController.bubbleBarTranslationY
.animateToValue(toY)
.updateTouchRegionOnAnimationEnd()
.setDuration(BAR_TRANSLATION_DURATION)
.start()
}
@VisibleForTesting
fun updateStashedAndExpandedState(stash: Boolean, expand: Boolean) {
if (bubbleBarViewController.isHiddenForNoBubbles) {
// If there are no bubbles the bar and handle are invisible, nothing to do here.
return
}
val isStashed = stash && !isBubblesShowingOnHome && !isBubblesShowingOnOverview
if (this.isStashed != isStashed) {
this.isStashed = isStashed
// notify the view controller that the stash state is about to change so that it can
// cancel an ongoing animation if there is one.
// note that this has to be called before updating mIsStashed with the new value,
// otherwise interrupting an ongoing animation may update it again with the wrong state
bubbleBarViewController.onStashStateChanging()
animator?.cancel()
animator =
createStashAnimator(isStashed, BAR_STASH_DURATION).apply {
updateTouchRegionOnAnimationEnd()
start()
}
}
if (bubbleBarViewController.isExpanded != expand) {
bubbleBarViewController.isExpanded = expand
}
}
private fun Animator.updateTouchRegionOnAnimationEnd(): Animator {
this.addListener(
object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) {
onIsStashedChanged()
}
}
)
return this
}
}