mirror of
https://github.com/LawnchairLauncher/lawnchair.git
synced 2026-02-27 15:26:58 +00:00
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
This commit is contained in:
@@ -364,6 +364,7 @@
|
||||
<dimen name="transient_taskbar_min_width">150dp</dimen>
|
||||
<dimen name="transient_taskbar_bottom_margin">24dp</dimen>
|
||||
<dimen name="transient_taskbar_shadow_blur">40dp</dimen>
|
||||
<dimen name="transient_taskbar_stroke_width">1dp</dimen>
|
||||
<dimen name="transient_taskbar_key_shadow_distance">10dp</dimen>
|
||||
<dimen name="transient_taskbar_stashed_height">32dp</dimen>
|
||||
<dimen name="transient_taskbar_all_apps_button_translation_x_offset">8dp</dimen>
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
19
res/color-night-v31/taskbar_stroke.xml
Normal file
19
res/color-night-v31/taskbar_stroke.xml
Normal file
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ 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.
|
||||
-->
|
||||
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:color="#9AA0A6" />
|
||||
</selector>
|
||||
19
res/color/taskbar_stroke.xml
Normal file
19
res/color/taskbar_stroke.xml
Normal file
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ 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.
|
||||
-->
|
||||
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:color="#BDC1C6" />
|
||||
</selector>
|
||||
@@ -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.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user