Loading quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarBackground.kt +3 −3 Original line number Diff line number Diff line Loading @@ -15,6 +15,7 @@ */ package com.android.launcher3.taskbar.bubbles import android.content.Context import android.graphics.Canvas import android.graphics.Color import android.graphics.ColorFilter Loading @@ -27,12 +28,10 @@ import com.android.launcher3.R import com.android.launcher3.Utilities import com.android.launcher3.Utilities.mapToRange import com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound import com.android.launcher3.taskbar.TaskbarActivityContext import com.android.wm.shell.common.TriangleShape /** Drawable for the background of the bubble bar. */ class BubbleBarBackground(context: TaskbarActivityContext, private val backgroundHeight: Float) : Drawable() { class BubbleBarBackground(context: Context, private val backgroundHeight: Float) : Drawable() { private val DARK_THEME_SHADOW_ALPHA = 51f private val LIGHT_THEME_SHADOW_ALPHA = 25f Loading @@ -46,6 +45,7 @@ class BubbleBarBackground(context: TaskbarActivityContext, private val backgroun var arrowPositionX: Float = 0f private set private var showingArrow: Boolean = false private var arrowDrawable: ShapeDrawable Loading quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java +31 −5 Original line number Diff line number Diff line Loading @@ -39,8 +39,6 @@ import androidx.dynamicanimation.animation.SpringForce; import com.android.launcher3.R; import com.android.launcher3.anim.SpringAnimationBuilder; import com.android.launcher3.taskbar.TaskbarActivityContext; import com.android.launcher3.views.ActivityContext; import com.android.wm.shell.common.bubbles.BubbleBarLocation; import java.util.List; Loading Loading @@ -159,8 +157,6 @@ public class BubbleBarView extends FrameLayout { public BubbleBarView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); TaskbarActivityContext activityContext = ActivityContext.lookupContext(context); setAlpha(0); setVisibility(INVISIBLE); mIconOverlapAmount = getResources().getDimensionPixelSize(R.dimen.bubblebar_icon_overlap); Loading @@ -171,7 +167,7 @@ public class BubbleBarView extends FrameLayout { setClipToPadding(false); mBubbleBarBackground = new BubbleBarBackground(activityContext, mBubbleBarBackground = new BubbleBarBackground(context, getResources().getDimensionPixelSize(R.dimen.bubblebar_size)); setBackgroundDrawable(mBubbleBarBackground); Loading Loading @@ -379,6 +375,36 @@ public class BubbleBarView extends FrameLayout { return mRelativePivotY; } /** Prepares for animating a bubble while being stashed. */ public void prepareForAnimatingBubbleWhileStashed(String bubbleKey) { // we're about to animate the new bubble in. the new bubble has already been added to this // view, but we're currently stashed, so before we can start the animation we need make // everything else in the bubble bar invisible, except for the bubble that's being animated. setBackground(null); for (int i = 0; i < getChildCount(); i++) { final BubbleView view = (BubbleView) getChildAt(i); final String key = view.getBubble().getKey(); if (!bubbleKey.equals(key)) { view.setVisibility(INVISIBLE); } } setVisibility(VISIBLE); setAlpha(1); setTranslationY(0); setScaleX(1); setScaleY(1); } /** Resets the state after the bubble animation completed. */ public void onAnimatingBubbleCompleted() { setBackground(mBubbleBarBackground); for (int i = 0; i < getChildCount(); i++) { final BubbleView view = (BubbleView) getChildAt(i); view.setVisibility(VISIBLE); view.setAlpha(1f); } } // TODO: (b/280605790) animate it @Override public void addView(View child, int index, ViewGroup.LayoutParams params) { Loading quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java +11 −0 Original line number Diff line number Diff line Loading @@ -34,6 +34,7 @@ import com.android.launcher3.taskbar.TaskbarActivityContext; import com.android.launcher3.taskbar.TaskbarControllers; import com.android.launcher3.taskbar.TaskbarInsetsController; import com.android.launcher3.taskbar.TaskbarStashController; import com.android.launcher3.taskbar.bubbles.animation.BubbleBarViewAnimator; import com.android.launcher3.util.MultiPropertyFactory; import com.android.launcher3.util.MultiValueAlpha; import com.android.quickstep.SystemUiProxy; Loading Loading @@ -81,6 +82,8 @@ public class BubbleBarViewController { private boolean mHiddenForNoBubbles = true; private boolean mShouldShowEducation; private BubbleBarViewAnimator mBubbleBarViewAnimator; public BubbleBarViewController(TaskbarActivityContext activity, BubbleBarView barView) { mActivity = activity; mBarView = barView; Loading Loading @@ -113,6 +116,8 @@ public class BubbleBarViewController { mBarView.addOnLayoutChangeListener((view, i, i1, i2, i3, i4, i5, i6, i7) -> mTaskbarInsetsController.onTaskbarOrBubblebarWindowHeightOrInsetsChanged() ); mBubbleBarViewAnimator = new BubbleBarViewAnimator(mBarView, mBubbleStashController); } private void onBubbleClicked(View v) { Loading Loading @@ -316,6 +321,12 @@ public class BubbleBarViewController { new FrameLayout.LayoutParams(mIconSize, mIconSize, Gravity.LEFT)); b.getView().setOnClickListener(mBubbleClickListener); mBubbleDragController.setupBubbleView(b.getView()); boolean isStashedOrGone = mBubbleStashController.isStashed() || mBarView.getVisibility() != VISIBLE; if (b instanceof BubbleBarBubble && isStashedOrGone) { mBubbleBarViewAnimator.animateBubbleInForStashed((BubbleBarBubble) b); } } else { Log.w(TAG, "addBubble, bubble was null!"); } Loading quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleView.java +3 −3 Original line number Diff line number Diff line Loading @@ -145,7 +145,7 @@ public class BubbleView extends ConstraintLayout { } /** Sets the bubble being rendered in this view. */ void setBubble(BubbleBarBubble bubble) { public void setBubble(BubbleBarBubble bubble) { mBubble = bubble; mBubbleIcon.setImageBitmap(bubble.getIcon()); mAppIcon.setImageBitmap(bubble.getBadge()); Loading @@ -159,7 +159,7 @@ public class BubbleView extends ConstraintLayout { * the list of bubbles. It doesn't show an app icon because it is part of system UI / doesn't * come from an app. */ void setOverflow(BubbleBarOverflow overflow, Bitmap bitmap) { public void setOverflow(BubbleBarOverflow overflow, Bitmap bitmap) { mBubble = overflow; mBubbleIcon.setImageBitmap(bitmap); mAppIcon.setVisibility(GONE); // Overflow doesn't show the app badge Loading @@ -168,7 +168,7 @@ public class BubbleView extends ConstraintLayout { /** Returns the bubble being rendered in this view. */ @Nullable BubbleBarItem getBubble() { public BubbleBarItem getBubble() { return mBubble; } Loading quickstep/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimator.kt 0 → 100644 +118 −0 Original line number 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.launcher3.taskbar.bubbles.animation import android.view.View import android.view.View.VISIBLE import androidx.dynamicanimation.animation.DynamicAnimation import androidx.dynamicanimation.animation.SpringForce import com.android.launcher3.taskbar.bubbles.BubbleBarBubble import com.android.launcher3.taskbar.bubbles.BubbleBarView import com.android.launcher3.taskbar.bubbles.BubbleStashController import com.android.launcher3.taskbar.bubbles.BubbleView import com.android.wm.shell.shared.animation.PhysicsAnimator /** Handles animations for bubble bar bubbles. */ class BubbleBarViewAnimator @JvmOverloads constructor( private val bubbleBarView: BubbleBarView, private val bubbleStashController: BubbleStashController, private val scheduler: Scheduler = HandlerScheduler(bubbleBarView) ) { private companion object { /** The time to show the flyout. */ const val FLYOUT_DELAY_MS: Long = 2500 /** The translation Y the new bubble will animate to. */ const val BUBBLE_ANIMATION_TRANSLATION_Y = -50f } /** An interface for scheduling jobs. */ interface Scheduler { /** Schedule the given [block] to run. */ fun post(block: () -> Unit) /** Schedule the given [block] to start with a delay of [delayMillis]. */ fun postDelayed(delayMillis: Long, block: () -> Unit) } /** A [Scheduler] that uses a Handler to run jobs. */ private class HandlerScheduler(private val view: View) : Scheduler { override fun post(block: () -> Unit) { view.post(block) } override fun postDelayed(delayMillis: Long, block: () -> Unit) { view.postDelayed(block, delayMillis) } } private val springConfig = PhysicsAnimator.SpringConfig( stiffness = SpringForce.STIFFNESS_LOW, dampingRatio = SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY ) /** Animates a bubble for the state where the bubble bar is stashed. */ fun animateBubbleInForStashed(b: BubbleBarBubble) { val bubbleView = b.view val animator = PhysicsAnimator.getInstance(bubbleView) if (animator.isRunning()) animator.cancel() // the animation of a new bubble is divided into 2 parts. The first part shows the bubble // and the second part hides it after a delay. val showAnimation = buildShowAnimation(bubbleView, b.key, animator) val hideAnimation = buildHideAnimation(animator) scheduler.post(showAnimation) scheduler.postDelayed(FLYOUT_DELAY_MS, hideAnimation) } /** Returns a lambda that starts the animation that shows the new bubble. */ private fun buildShowAnimation( bubbleView: BubbleView, key: String, animator: PhysicsAnimator<BubbleView> ): () -> Unit = { bubbleBarView.prepareForAnimatingBubbleWhileStashed(key) animator.setDefaultSpringConfig(springConfig) animator .spring(DynamicAnimation.ALPHA, 1f) .spring(DynamicAnimation.TRANSLATION_Y, BUBBLE_ANIMATION_TRANSLATION_Y) bubbleView.alpha = 0f bubbleView.visibility = VISIBLE animator.start() } /** Returns a lambda that starts the animation that hides the new bubble. */ private fun buildHideAnimation(animator: PhysicsAnimator<BubbleView>): () -> Unit = { animator.setDefaultSpringConfig(springConfig) animator .spring(DynamicAnimation.ALPHA, 0f) .spring(DynamicAnimation.TRANSLATION_Y, 0f) .addEndListener { _, _, _, canceled, _, _, allRelevantPropertyAnimsEnded -> if (!canceled && allRelevantPropertyAnimsEnded) { if (bubbleStashController.isStashed) { bubbleBarView.alpha = 0f } bubbleBarView.onAnimatingBubbleCompleted() } } animator.start() } } Loading
quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarBackground.kt +3 −3 Original line number Diff line number Diff line Loading @@ -15,6 +15,7 @@ */ package com.android.launcher3.taskbar.bubbles import android.content.Context import android.graphics.Canvas import android.graphics.Color import android.graphics.ColorFilter Loading @@ -27,12 +28,10 @@ import com.android.launcher3.R import com.android.launcher3.Utilities import com.android.launcher3.Utilities.mapToRange import com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound import com.android.launcher3.taskbar.TaskbarActivityContext import com.android.wm.shell.common.TriangleShape /** Drawable for the background of the bubble bar. */ class BubbleBarBackground(context: TaskbarActivityContext, private val backgroundHeight: Float) : Drawable() { class BubbleBarBackground(context: Context, private val backgroundHeight: Float) : Drawable() { private val DARK_THEME_SHADOW_ALPHA = 51f private val LIGHT_THEME_SHADOW_ALPHA = 25f Loading @@ -46,6 +45,7 @@ class BubbleBarBackground(context: TaskbarActivityContext, private val backgroun var arrowPositionX: Float = 0f private set private var showingArrow: Boolean = false private var arrowDrawable: ShapeDrawable Loading
quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java +31 −5 Original line number Diff line number Diff line Loading @@ -39,8 +39,6 @@ import androidx.dynamicanimation.animation.SpringForce; import com.android.launcher3.R; import com.android.launcher3.anim.SpringAnimationBuilder; import com.android.launcher3.taskbar.TaskbarActivityContext; import com.android.launcher3.views.ActivityContext; import com.android.wm.shell.common.bubbles.BubbleBarLocation; import java.util.List; Loading Loading @@ -159,8 +157,6 @@ public class BubbleBarView extends FrameLayout { public BubbleBarView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); TaskbarActivityContext activityContext = ActivityContext.lookupContext(context); setAlpha(0); setVisibility(INVISIBLE); mIconOverlapAmount = getResources().getDimensionPixelSize(R.dimen.bubblebar_icon_overlap); Loading @@ -171,7 +167,7 @@ public class BubbleBarView extends FrameLayout { setClipToPadding(false); mBubbleBarBackground = new BubbleBarBackground(activityContext, mBubbleBarBackground = new BubbleBarBackground(context, getResources().getDimensionPixelSize(R.dimen.bubblebar_size)); setBackgroundDrawable(mBubbleBarBackground); Loading Loading @@ -379,6 +375,36 @@ public class BubbleBarView extends FrameLayout { return mRelativePivotY; } /** Prepares for animating a bubble while being stashed. */ public void prepareForAnimatingBubbleWhileStashed(String bubbleKey) { // we're about to animate the new bubble in. the new bubble has already been added to this // view, but we're currently stashed, so before we can start the animation we need make // everything else in the bubble bar invisible, except for the bubble that's being animated. setBackground(null); for (int i = 0; i < getChildCount(); i++) { final BubbleView view = (BubbleView) getChildAt(i); final String key = view.getBubble().getKey(); if (!bubbleKey.equals(key)) { view.setVisibility(INVISIBLE); } } setVisibility(VISIBLE); setAlpha(1); setTranslationY(0); setScaleX(1); setScaleY(1); } /** Resets the state after the bubble animation completed. */ public void onAnimatingBubbleCompleted() { setBackground(mBubbleBarBackground); for (int i = 0; i < getChildCount(); i++) { final BubbleView view = (BubbleView) getChildAt(i); view.setVisibility(VISIBLE); view.setAlpha(1f); } } // TODO: (b/280605790) animate it @Override public void addView(View child, int index, ViewGroup.LayoutParams params) { Loading
quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java +11 −0 Original line number Diff line number Diff line Loading @@ -34,6 +34,7 @@ import com.android.launcher3.taskbar.TaskbarActivityContext; import com.android.launcher3.taskbar.TaskbarControllers; import com.android.launcher3.taskbar.TaskbarInsetsController; import com.android.launcher3.taskbar.TaskbarStashController; import com.android.launcher3.taskbar.bubbles.animation.BubbleBarViewAnimator; import com.android.launcher3.util.MultiPropertyFactory; import com.android.launcher3.util.MultiValueAlpha; import com.android.quickstep.SystemUiProxy; Loading Loading @@ -81,6 +82,8 @@ public class BubbleBarViewController { private boolean mHiddenForNoBubbles = true; private boolean mShouldShowEducation; private BubbleBarViewAnimator mBubbleBarViewAnimator; public BubbleBarViewController(TaskbarActivityContext activity, BubbleBarView barView) { mActivity = activity; mBarView = barView; Loading Loading @@ -113,6 +116,8 @@ public class BubbleBarViewController { mBarView.addOnLayoutChangeListener((view, i, i1, i2, i3, i4, i5, i6, i7) -> mTaskbarInsetsController.onTaskbarOrBubblebarWindowHeightOrInsetsChanged() ); mBubbleBarViewAnimator = new BubbleBarViewAnimator(mBarView, mBubbleStashController); } private void onBubbleClicked(View v) { Loading Loading @@ -316,6 +321,12 @@ public class BubbleBarViewController { new FrameLayout.LayoutParams(mIconSize, mIconSize, Gravity.LEFT)); b.getView().setOnClickListener(mBubbleClickListener); mBubbleDragController.setupBubbleView(b.getView()); boolean isStashedOrGone = mBubbleStashController.isStashed() || mBarView.getVisibility() != VISIBLE; if (b instanceof BubbleBarBubble && isStashedOrGone) { mBubbleBarViewAnimator.animateBubbleInForStashed((BubbleBarBubble) b); } } else { Log.w(TAG, "addBubble, bubble was null!"); } Loading
quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleView.java +3 −3 Original line number Diff line number Diff line Loading @@ -145,7 +145,7 @@ public class BubbleView extends ConstraintLayout { } /** Sets the bubble being rendered in this view. */ void setBubble(BubbleBarBubble bubble) { public void setBubble(BubbleBarBubble bubble) { mBubble = bubble; mBubbleIcon.setImageBitmap(bubble.getIcon()); mAppIcon.setImageBitmap(bubble.getBadge()); Loading @@ -159,7 +159,7 @@ public class BubbleView extends ConstraintLayout { * the list of bubbles. It doesn't show an app icon because it is part of system UI / doesn't * come from an app. */ void setOverflow(BubbleBarOverflow overflow, Bitmap bitmap) { public void setOverflow(BubbleBarOverflow overflow, Bitmap bitmap) { mBubble = overflow; mBubbleIcon.setImageBitmap(bitmap); mAppIcon.setVisibility(GONE); // Overflow doesn't show the app badge Loading @@ -168,7 +168,7 @@ public class BubbleView extends ConstraintLayout { /** Returns the bubble being rendered in this view. */ @Nullable BubbleBarItem getBubble() { public BubbleBarItem getBubble() { return mBubble; } Loading
quickstep/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimator.kt 0 → 100644 +118 −0 Original line number 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.launcher3.taskbar.bubbles.animation import android.view.View import android.view.View.VISIBLE import androidx.dynamicanimation.animation.DynamicAnimation import androidx.dynamicanimation.animation.SpringForce import com.android.launcher3.taskbar.bubbles.BubbleBarBubble import com.android.launcher3.taskbar.bubbles.BubbleBarView import com.android.launcher3.taskbar.bubbles.BubbleStashController import com.android.launcher3.taskbar.bubbles.BubbleView import com.android.wm.shell.shared.animation.PhysicsAnimator /** Handles animations for bubble bar bubbles. */ class BubbleBarViewAnimator @JvmOverloads constructor( private val bubbleBarView: BubbleBarView, private val bubbleStashController: BubbleStashController, private val scheduler: Scheduler = HandlerScheduler(bubbleBarView) ) { private companion object { /** The time to show the flyout. */ const val FLYOUT_DELAY_MS: Long = 2500 /** The translation Y the new bubble will animate to. */ const val BUBBLE_ANIMATION_TRANSLATION_Y = -50f } /** An interface for scheduling jobs. */ interface Scheduler { /** Schedule the given [block] to run. */ fun post(block: () -> Unit) /** Schedule the given [block] to start with a delay of [delayMillis]. */ fun postDelayed(delayMillis: Long, block: () -> Unit) } /** A [Scheduler] that uses a Handler to run jobs. */ private class HandlerScheduler(private val view: View) : Scheduler { override fun post(block: () -> Unit) { view.post(block) } override fun postDelayed(delayMillis: Long, block: () -> Unit) { view.postDelayed(block, delayMillis) } } private val springConfig = PhysicsAnimator.SpringConfig( stiffness = SpringForce.STIFFNESS_LOW, dampingRatio = SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY ) /** Animates a bubble for the state where the bubble bar is stashed. */ fun animateBubbleInForStashed(b: BubbleBarBubble) { val bubbleView = b.view val animator = PhysicsAnimator.getInstance(bubbleView) if (animator.isRunning()) animator.cancel() // the animation of a new bubble is divided into 2 parts. The first part shows the bubble // and the second part hides it after a delay. val showAnimation = buildShowAnimation(bubbleView, b.key, animator) val hideAnimation = buildHideAnimation(animator) scheduler.post(showAnimation) scheduler.postDelayed(FLYOUT_DELAY_MS, hideAnimation) } /** Returns a lambda that starts the animation that shows the new bubble. */ private fun buildShowAnimation( bubbleView: BubbleView, key: String, animator: PhysicsAnimator<BubbleView> ): () -> Unit = { bubbleBarView.prepareForAnimatingBubbleWhileStashed(key) animator.setDefaultSpringConfig(springConfig) animator .spring(DynamicAnimation.ALPHA, 1f) .spring(DynamicAnimation.TRANSLATION_Y, BUBBLE_ANIMATION_TRANSLATION_Y) bubbleView.alpha = 0f bubbleView.visibility = VISIBLE animator.start() } /** Returns a lambda that starts the animation that hides the new bubble. */ private fun buildHideAnimation(animator: PhysicsAnimator<BubbleView>): () -> Unit = { animator.setDefaultSpringConfig(springConfig) animator .spring(DynamicAnimation.ALPHA, 0f) .spring(DynamicAnimation.TRANSLATION_Y, 0f) .addEndListener { _, _, _, canceled, _, _, allRelevantPropertyAnimsEnded -> if (!canceled && allRelevantPropertyAnimsEnded) { if (bubbleStashController.isStashed) { bubbleBarView.alpha = 0f } bubbleBarView.onAnimatingBubbleCompleted() } } animator.start() } }