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

Commit 7b0c3256 authored by Liran Binyamin's avatar Liran Binyamin Committed by Android (Google) Code Review
Browse files

Merge "Update bubble bar flyout according to spec" into main

parents f0123c91 19a43fb7
Loading
Loading
Loading
Loading
+3 −2
Original line number Diff line number Diff line
@@ -20,11 +20,12 @@

    <ImageView
        android:id="@+id/bubble_flyout_avatar"
        android:layout_width="36dp"
        android:layout_width="50dp"
        android:layout_height="36dp"
        android:padding="@dimen/bubblebar_flyout_avatar_message_space"
        android:paddingEnd="@dimen/bubblebar_flyout_avatar_message_space"
        android:scaleType="centerInside"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        tools:src="#ff0000"/>

+8 −4
Original line number Diff line number Diff line
@@ -480,11 +480,15 @@
    <dimen name="bubble_expanded_view_drop_target_margin">16dp</dimen>

    <!-- Bubble bar flyout view -->
    <dimen name="bubblebar_flyout_padding_horizontal">14dp</dimen>
    <dimen name="bubblebar_flyout_padding_vertical">10dp</dimen>
    <dimen name="bubblebar_flyout_padding">16dp</dimen>
    <dimen name="bubblebar_flyout_elevation">4dp</dimen>
    <dimen name="bubblebar_flyout_avatar_message_space">6dp</dimen>
    <dimen name="bubblebar_flyout_max_width">96dp</dimen>
    <dimen name="bubblebar_flyout_avatar_message_space">14dp</dimen>
    <dimen name="bubblebar_flyout_min_width">238dp</dimen>
    <dimen name="bubblebar_flyout_max_width">276dp</dimen>
    <dimen name="bubblebar_flyout_triangle_width">12dp</dimen>
    <dimen name="bubblebar_flyout_triangle_height">10dp</dimen>
    <dimen name="bubblebar_flyout_triangle_overlap_amount">1dp</dimen>
    <dimen name="bubblebar_flyout_triangle_radius">2dp</dimen>

    <!-- Launcher splash screen -->
    <!-- Note: keep this value in sync with the WindowManager/Shell dimens.xml -->
+2 −2
Original line number Diff line number Diff line
@@ -28,12 +28,12 @@ class BubbleBarFlyoutController(
) {

    private var flyout: BubbleBarFlyoutView? = null
    val horizontalMargin =
    private val horizontalMargin =
        container.context.resources.getDimensionPixelSize(R.dimen.transient_taskbar_bottom_margin)

    fun setUpFlyout(message: BubbleBarFlyoutMessage) {
        flyout?.let(container::removeView)
        val flyout = BubbleBarFlyoutView(container.context)
        val flyout = BubbleBarFlyoutView(container.context, onLeft = positioner.isOnLeft)

        flyout.translationY = positioner.targetTy

+69 −10
Original line number Diff line number Diff line
@@ -21,14 +21,17 @@ import android.content.res.Configuration
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.Path
import android.view.LayoutInflater
import android.widget.ImageView
import android.widget.TextView
import androidx.constraintlayout.widget.ConstraintLayout
import com.android.launcher3.R
import com.android.launcher3.popup.RoundedArrowDrawable

/** The flyout view used to notify the user of a new bubble notification. */
class BubbleBarFlyoutView(context: Context) : ConstraintLayout(context) {
class BubbleBarFlyoutView(context: Context, private val onLeft: Boolean) :
    ConstraintLayout(context) {

    private val sender: TextView by
        lazy(LazyThreadSafetyMode.NONE) { findViewById(R.id.bubble_flyout_name) }
@@ -39,9 +42,36 @@ class BubbleBarFlyoutView(context: Context) : ConstraintLayout(context) {
    private val message: TextView by
        lazy(LazyThreadSafetyMode.NONE) { findViewById(R.id.bubble_flyout_text) }

    private val flyoutHorizontalPadding by
    private val flyoutPadding by
        lazy(LazyThreadSafetyMode.NONE) {
            context.resources.getDimensionPixelSize(R.dimen.bubblebar_flyout_padding_horizontal)
            context.resources.getDimensionPixelSize(R.dimen.bubblebar_flyout_padding)
        }

    private val triangleHeight by
        lazy(LazyThreadSafetyMode.NONE) {
            context.resources.getDimensionPixelSize(R.dimen.bubblebar_flyout_triangle_height)
        }

    private val triangleOverlap by
        lazy(LazyThreadSafetyMode.NONE) {
            context.resources.getDimensionPixelSize(
                R.dimen.bubblebar_flyout_triangle_overlap_amount
            )
        }

    private val triangleWidth by
        lazy(LazyThreadSafetyMode.NONE) {
            context.resources.getDimensionPixelSize(R.dimen.bubblebar_flyout_triangle_width)
        }

    private val triangleRadius by
        lazy(LazyThreadSafetyMode.NONE) {
            context.resources.getDimensionPixelSize(R.dimen.bubblebar_flyout_triangle_radius)
        }

    private val minFlyoutWidth by
        lazy(LazyThreadSafetyMode.NONE) {
            context.resources.getDimensionPixelSize(R.dimen.bubblebar_flyout_min_width)
        }

    private val maxFlyoutWidth by
@@ -50,6 +80,7 @@ class BubbleBarFlyoutView(context: Context) : ConstraintLayout(context) {
        }

    private val cornerRadius: Float
    private val triangle: Path = Path()
    private var backgroundColor = Color.BLACK

    /**
@@ -69,13 +100,19 @@ class BubbleBarFlyoutView(context: Context) : ConstraintLayout(context) {
        clipChildren = false
        clipToPadding = false

        val horizontalPadding =
            context.resources.getDimensionPixelSize(R.dimen.bubblebar_flyout_padding_horizontal)
        val verticalPadding =
            context.resources.getDimensionPixelSize(R.dimen.bubblebar_flyout_padding_vertical)
        setPadding(horizontalPadding, verticalPadding, horizontalPadding, verticalPadding)
        val padding = context.resources.getDimensionPixelSize(R.dimen.bubblebar_flyout_padding)
        // add extra padding to the bottom of the view to include the triangle
        setPadding(padding, padding, padding, padding + triangleHeight - triangleOverlap)
        translationZ =
            context.resources.getDimensionPixelSize(R.dimen.bubblebar_flyout_elevation).toFloat()

        RoundedArrowDrawable.addDownPointingRoundedTriangleToPath(
            triangleWidth.toFloat(),
            triangleHeight.toFloat(),
            triangleRadius.toFloat(),
            triangle,
        )

        applyConfigurationColors(resources.configuration)
    }

@@ -88,15 +125,28 @@ class BubbleBarFlyoutView(context: Context) : ConstraintLayout(context) {
            avatar.visibility = GONE
        }

        val maxTextViewWidth = maxFlyoutWidth - flyoutHorizontalPadding * 2
        val minTextViewWidth: Int
        val maxTextViewWidth: Int
        if (avatar.visibility == VISIBLE) {
            minTextViewWidth = minFlyoutWidth - avatar.width - flyoutPadding * 2
            maxTextViewWidth = maxFlyoutWidth - avatar.width - flyoutPadding * 2
        } else {
            // when there's no avatar, the width of the text view is constant, so we're setting the
            // min and max to the same value
            minTextViewWidth = minFlyoutWidth - flyoutPadding * 2
            maxTextViewWidth = minTextViewWidth
        }

        if (flyoutMessage.senderName.isEmpty()) {
            sender.visibility = GONE
        } else {
            sender.minWidth = minTextViewWidth
            sender.maxWidth = maxTextViewWidth
            sender.text = flyoutMessage.senderName
            sender.visibility = VISIBLE
        }

        message.minWidth = minTextViewWidth
        message.maxWidth = maxTextViewWidth
        message.text = flyoutMessage.message
    }
@@ -106,14 +156,23 @@ class BubbleBarFlyoutView(context: Context) : ConstraintLayout(context) {
            0f,
            0f,
            width.toFloat(),
            height.toFloat(),
            height.toFloat() - triangleHeight + triangleOverlap,
            cornerRadius,
            cornerRadius,
            backgroundPaint,
        )
        drawTriangle(canvas)
        super.onDraw(canvas)
    }

    private fun drawTriangle(canvas: Canvas) {
        canvas.save()
        val triangleX = if (onLeft) cornerRadius else width - cornerRadius - triangleWidth
        canvas.translate(triangleX, (height - triangleHeight).toFloat())
        canvas.drawPath(triangle, backgroundPaint)
        canvas.restore()
    }

    private fun applyConfigurationColors(configuration: Configuration) {
        val nightModeFlags = configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
        val isNightModeOn = nightModeFlags == Configuration.UI_MODE_NIGHT_YES
+74 −6
Original line number Diff line number Diff line
@@ -56,10 +56,10 @@ class BubbleBarFlyoutViewScreenshotTest(emulationSpec: DeviceEmulationSpec) {
        )

    @Test
    fun bubbleBarFlyoutView_noAvatar() {
        screenshotRule.screenshotTest("bubbleBarFlyoutView_noAvatar") { activity ->
    fun bubbleBarFlyoutView_noAvatar_onRight() {
        screenshotRule.screenshotTest("bubbleBarFlyoutView_noAvatar_onRight") { activity ->
            activity.actionBar?.hide()
            val flyout = BubbleBarFlyoutView(context)
            val flyout = BubbleBarFlyoutView(context, onLeft = false)
            flyout.setData(
                BubbleBarFlyoutMessage(
                    senderAvatar = null,
@@ -73,10 +73,44 @@ class BubbleBarFlyoutViewScreenshotTest(emulationSpec: DeviceEmulationSpec) {
    }

    @Test
    fun bubbleBarFlyoutView_avatar() {
        screenshotRule.screenshotTest("bubbleBarFlyoutView_avatar") { activity ->
    fun bubbleBarFlyoutView_noAvatar_onLeft() {
        screenshotRule.screenshotTest("bubbleBarFlyoutView_noAvatar_onLeft") { activity ->
            activity.actionBar?.hide()
            val flyout = BubbleBarFlyoutView(context)
            val flyout = BubbleBarFlyoutView(context, onLeft = true)
            flyout.setData(
                BubbleBarFlyoutMessage(
                    senderAvatar = null,
                    senderName = "sender",
                    message = "message",
                    isGroupChat = false,
                )
            )
            flyout
        }
    }

    @Test
    fun bubbleBarFlyoutView_noAvatar_longMessage() {
        screenshotRule.screenshotTest("bubbleBarFlyoutView_noAvatar_longMessage") { activity ->
            activity.actionBar?.hide()
            val flyout = BubbleBarFlyoutView(context, onLeft = true)
            flyout.setData(
                BubbleBarFlyoutMessage(
                    senderAvatar = null,
                    senderName = "sender",
                    message = "really, really, really, really, really long message. like really.",
                    isGroupChat = false,
                )
            )
            flyout
        }
    }

    @Test
    fun bubbleBarFlyoutView_avatar_onRight() {
        screenshotRule.screenshotTest("bubbleBarFlyoutView_avatar_onRight") { activity ->
            activity.actionBar?.hide()
            val flyout = BubbleBarFlyoutView(context, onLeft = false)
            flyout.setData(
                BubbleBarFlyoutMessage(
                    senderAvatar = ColorDrawable(Color.RED),
@@ -88,4 +122,38 @@ class BubbleBarFlyoutViewScreenshotTest(emulationSpec: DeviceEmulationSpec) {
            flyout
        }
    }

    @Test
    fun bubbleBarFlyoutView_avatar_onLeft() {
        screenshotRule.screenshotTest("bubbleBarFlyoutView_avatar_onLeft") { activity ->
            activity.actionBar?.hide()
            val flyout = BubbleBarFlyoutView(context, onLeft = true)
            flyout.setData(
                BubbleBarFlyoutMessage(
                    senderAvatar = ColorDrawable(Color.RED),
                    senderName = "sender",
                    message = "message",
                    isGroupChat = true,
                )
            )
            flyout
        }
    }

    @Test
    fun bubbleBarFlyoutView_avatar_longMessage() {
        screenshotRule.screenshotTest("bubbleBarFlyoutView_avatar_longMessage") { activity ->
            activity.actionBar?.hide()
            val flyout = BubbleBarFlyoutView(context, onLeft = true)
            flyout.setData(
                BubbleBarFlyoutMessage(
                    senderAvatar = ColorDrawable(Color.RED),
                    senderName = "sender",
                    message = "really, really, really, really, really long message. like really.",
                    isGroupChat = true,
                )
            )
            flyout
        }
    }
}