From 84f9cfabb9a71b776717cb14ff6c7c5bad16dfb6 Mon Sep 17 00:00:00 2001 From: mpodolian Date: Tue, 11 Jun 2024 01:15:20 +0100 Subject: [PATCH] Add outline to the bubble bar background view. Added outline to the bubble bar background. Updated pointer appearing/disappearing animation - now pointer moves into the bubble bar background. Demo: http://recall/-/gx8ASgewUeUS3QYohfrd1J/e1wZMrFZTILq73ik8wwrwx Fixes: 345489039 Flag: ACONFIG com.android.wm.shell.enable_bubble_bar DEVELOPMENT Test: Manual. Expand and collapse bubble bar with the light and the dark theme. Change-Id: I095fdc12337955aa21ee0eb622a924ad424ef186 --- quickstep/res/values/dimens.xml | 1 + .../taskbar/bubbles/BubbleBarBackground.kt | 150 +++++++++++------- .../taskbar/bubbles/BubbleBarView.java | 5 +- res/color-night-v31/taskbar_stroke.xml | 19 +++ res/color/taskbar_stroke.xml | 19 +++ .../launcher3/popup/RoundedArrowDrawable.java | 5 +- 6 files changed, 135 insertions(+), 64 deletions(-) create mode 100644 res/color-night-v31/taskbar_stroke.xml create mode 100644 res/color/taskbar_stroke.xml diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml index 7f09b6e8ed..c1acaa0219 100644 --- a/quickstep/res/values/dimens.xml +++ b/quickstep/res/values/dimens.xml @@ -364,6 +364,7 @@ 150dp 24dp 40dp + 1dp 10dp 32dp 8dp diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarBackground.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarBackground.kt index a59e81b68e..bc13c89855 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarBackground.kt +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarBackground.kt @@ -19,7 +19,9 @@ import android.content.Context import android.graphics.Canvas import android.graphics.Color import android.graphics.ColorFilter +import android.graphics.Matrix import android.graphics.Paint +import android.graphics.Path import android.graphics.PixelFormat import android.graphics.drawable.Drawable import com.android.app.animation.Interpolators @@ -28,28 +30,28 @@ import com.android.launcher3.Utilities import com.android.launcher3.Utilities.mapToRange import com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound import com.android.launcher3.popup.RoundedArrowDrawable +import kotlin.math.max +import kotlin.math.min /** Drawable for the background of the bubble bar. */ class BubbleBarBackground(context: Context, private var backgroundHeight: Float) : Drawable() { - private val DARK_THEME_SHADOW_ALPHA = 51f - private val LIGHT_THEME_SHADOW_ALPHA = 25f - - private val paint: Paint = Paint() - private val pointerWidth: Float - private val pointerHeight: Float - private val pointerTipRadius: Float - private val pointerVisibleHeight: Float + private val fillPaint: Paint = Paint() + private val strokePaint: Paint = Paint() + private val arrowWidth: Float + private val arrowHeight: Float + private val arrowTipRadius: Float + private val arrowVisibleHeight: Float private val shadowAlpha: Float private var shadowBlur = 0f private var keyShadowDistance = 0f + private var arrowHeightFraction = 1f var arrowPositionX: Float = 0f private set private var showingArrow: Boolean = false - private var arrowDrawable: RoundedArrowDrawable var width: Float = 0f @@ -70,34 +72,31 @@ class BubbleBarBackground(context: Context, private var backgroundHeight: Float) } init { - paint.color = context.getColor(R.color.taskbar_background) - paint.flags = Paint.ANTI_ALIAS_FLAG - paint.style = Paint.Style.FILL - val res = context.resources + // configure fill paint + fillPaint.color = context.getColor(R.color.taskbar_background) + fillPaint.flags = Paint.ANTI_ALIAS_FLAG + fillPaint.style = Paint.Style.FILL + // configure stroke paint + strokePaint.color = context.getColor(R.color.taskbar_stroke) + strokePaint.flags = Paint.ANTI_ALIAS_FLAG + strokePaint.style = Paint.Style.STROKE + strokePaint.strokeWidth = res.getDimension(R.dimen.transient_taskbar_stroke_width) + // apply theme alpha attributes + if (Utilities.isDarkTheme(context)) { + strokePaint.alpha = DARK_THEME_STROKE_ALPHA + shadowAlpha = DARK_THEME_SHADOW_ALPHA + } else { + strokePaint.alpha = LIGHT_THEME_STROKE_ALPHA + shadowAlpha = LIGHT_THEME_SHADOW_ALPHA + } + shadowBlur = res.getDimension(R.dimen.transient_taskbar_shadow_blur) keyShadowDistance = res.getDimension(R.dimen.transient_taskbar_key_shadow_distance) - pointerWidth = res.getDimension(R.dimen.bubblebar_pointer_width) - pointerHeight = res.getDimension(R.dimen.bubblebar_pointer_height) - pointerVisibleHeight = res.getDimension(R.dimen.bubblebar_pointer_visible_size) - pointerTipRadius = res.getDimension(R.dimen.bubblebar_pointer_radius) - - shadowAlpha = - if (Utilities.isDarkTheme(context)) { - DARK_THEME_SHADOW_ALPHA - } else { - LIGHT_THEME_SHADOW_ALPHA - } - - arrowDrawable = - RoundedArrowDrawable.createVerticalRoundedArrow( - pointerWidth, - pointerHeight, - pointerTipRadius, - /* isPointingUp= */ true, - context.getColor(R.color.taskbar_background) - ) - arrowDrawable.setBounds(0, 0, pointerWidth.toInt(), pointerHeight.toInt()) + arrowWidth = res.getDimension(R.dimen.bubblebar_pointer_width) + arrowHeight = res.getDimension(R.dimen.bubblebar_pointer_height) + arrowVisibleHeight = res.getDimension(R.dimen.bubblebar_pointer_visible_size) + arrowTipRadius = res.getDimension(R.dimen.bubblebar_pointer_radius) } fun showArrow(show: Boolean) { @@ -115,40 +114,53 @@ class BubbleBarBackground(context: Context, private var backgroundHeight: Float) // TODO (b/277359345): Should animate the alpha similar to taskbar (see TaskbarDragLayer) // Draw shadows. val newShadowAlpha = - mapToRange(paint.alpha.toFloat(), 0f, 255f, 0f, shadowAlpha, Interpolators.LINEAR) - paint.setShadowLayer( + mapToRange(fillPaint.alpha.toFloat(), 0f, 255f, 0f, shadowAlpha, Interpolators.LINEAR) + fillPaint.setShadowLayer( shadowBlur, 0f, keyShadowDistance, setColorAlphaBound(Color.BLACK, Math.round(newShadowAlpha)) ) - arrowDrawable.setShadowLayer( - shadowBlur, - 0f, - keyShadowDistance, - setColorAlphaBound(Color.BLACK, Math.round(newShadowAlpha)) - ) - - // Draw background. + // Create background path + val backgroundPath = Path() val radius = backgroundHeight / 2f val left = bounds.left + (if (anchorLeft) 0f else bounds.width().toFloat() - width) val right = bounds.left + (if (anchorLeft) width else bounds.width().toFloat()) - val top = bounds.top + pointerVisibleHeight + val top = bounds.top + arrowVisibleHeight val bottom = bounds.top + bounds.height().toFloat() - canvas.drawRoundRect(left, top, right, bottom, radius, radius, paint) + backgroundPath.addRoundRect(left, top, right, bottom, radius, radius, Path.Direction.CW) + addArrowPathIfNeeded(backgroundPath) - if (showingArrow) { - // Draw arrow. - val transX = bounds.left + arrowPositionX - pointerWidth / 2f - canvas.translate(transX, 0f) - arrowDrawable.draw(canvas) - } + // Draw background. + canvas.drawPath(backgroundPath, fillPaint) + canvas.drawPath(backgroundPath, strokePaint) canvas.restore() } + private fun addArrowPathIfNeeded(sourcePath: Path) { + if (!showingArrow || arrowHeightFraction <= 0) return + val arrowPath = Path() + RoundedArrowDrawable.addDownPointingRoundedTriangleToPath( + arrowWidth, + arrowHeight, + arrowTipRadius, + arrowPath + ) + // flip it horizontally + val pathTransform = Matrix() + pathTransform.setRotate(180f, arrowWidth * 0.5f, arrowHeight * 0.5f) + arrowPath.transform(pathTransform) + // shift to arrow position + val arrowStart = bounds.left + arrowPositionX - (arrowWidth / 2f) + val arrowTop = (1 - arrowHeightFraction) * arrowVisibleHeight + arrowPath.offset(arrowStart, arrowTop) + // union with rectangle + sourcePath.op(arrowPath, Path.Op.UNION) + } + override fun getOpacity(): Int { - return when (paint.alpha) { + return when (fillPaint.alpha) { 255 -> PixelFormat.OPAQUE 0 -> PixelFormat.TRANSPARENT else -> PixelFormat.TRANSLUCENT @@ -156,24 +168,40 @@ class BubbleBarBackground(context: Context, private var backgroundHeight: Float) } override fun setAlpha(alpha: Int) { - paint.alpha = alpha - arrowDrawable.alpha = alpha + fillPaint.alpha = alpha invalidateSelf() } override fun getAlpha(): Int { - return paint.alpha + return fillPaint.alpha } override fun setColorFilter(colorFilter: ColorFilter?) { - paint.colorFilter = colorFilter - } - - fun setArrowAlpha(alpha: Int) { - arrowDrawable.alpha = alpha + fillPaint.colorFilter = colorFilter } fun setHeight(newHeight: Float) { backgroundHeight = newHeight } + + /** + * Set fraction of the arrow height that should be displayed. Allowed values range are [0..1]. + * If value passed is out of range it will be converted to the closest value in tha allowed + * range. + */ + fun setArrowHeightFraction(arrowHeightFraction: Float) { + var newHeightFraction = arrowHeightFraction + if (newHeightFraction !in 0f..1f) { + newHeightFraction = min(max(newHeightFraction, 0f), 1f) + } + this.arrowHeightFraction = newHeightFraction + invalidateSelf() + } + + companion object { + private const val DARK_THEME_STROKE_ALPHA = 51 + private const val LIGHT_THEME_STROKE_ALPHA = 41 + private const val DARK_THEME_SHADOW_ALPHA = 51f + private const val LIGHT_THEME_SHADOW_ALPHA = 25f + } } diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java index f3ac1e44ba..5f9a7527b6 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java @@ -729,7 +729,7 @@ public class BubbleBarView extends FrameLayout { } } mBubbleBarBackground.setArrowPosition(arrowPosition); - mBubbleBarBackground.setArrowAlpha((int) (255 * widthState)); + mBubbleBarBackground.setArrowHeightFraction(widthState); mBubbleBarBackground.setWidth(interpolatedWidth); } @@ -882,7 +882,8 @@ public class BubbleBarView extends FrameLayout { // bubbles than the current bubble and overflow. bubblePosition = index == 0 && getChildCount() > MAX_VISIBLE_BUBBLES_COLLAPSED ? 1 : 0; } else { - bubblePosition = index; + bubblePosition = index >= MAX_VISIBLE_BUBBLES_COLLAPSED + ? MAX_VISIBLE_BUBBLES_COLLAPSED - 1 : index; } return getPaddingStart() + bubblePosition * (mIconOverlapAmount) + mIconSize / 2f; } diff --git a/res/color-night-v31/taskbar_stroke.xml b/res/color-night-v31/taskbar_stroke.xml new file mode 100644 index 0000000000..db7a510620 --- /dev/null +++ b/res/color-night-v31/taskbar_stroke.xml @@ -0,0 +1,19 @@ + + + + + \ No newline at end of file diff --git a/res/color/taskbar_stroke.xml b/res/color/taskbar_stroke.xml new file mode 100644 index 0000000000..b691082c98 --- /dev/null +++ b/res/color/taskbar_stroke.xml @@ -0,0 +1,19 @@ + + + + + \ No newline at end of file diff --git a/src/com/android/launcher3/popup/RoundedArrowDrawable.java b/src/com/android/launcher3/popup/RoundedArrowDrawable.java index 575052ccc5..2610bd67c3 100644 --- a/src/com/android/launcher3/popup/RoundedArrowDrawable.java +++ b/src/com/android/launcher3/popup/RoundedArrowDrawable.java @@ -175,7 +175,10 @@ public class RoundedArrowDrawable extends Drawable { mPaint.setShadowLayer(shadowBlur, dx, dy, shadowColor); } - private static void addDownPointingRoundedTriangleToPath(float width, float height, + /** + * Adds rounded triangle pointing down to the provided {@link Path path} argument + */ + public static void addDownPointingRoundedTriangleToPath(float width, float height, float radius, Path path) { // Calculated for the arrow pointing down, will be flipped later if needed.