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

Commit ede75a98 authored by Xiaowen Lei's avatar Xiaowen Lei
Browse files

Some UI updates to timer RON per the initial UX mock.

- Fixed app icon loading
- Title: removed "\sigma" prefix and appended " Timer".
- Several updates for the action buttons.
  - We can now have up to three action buttions instead of two. (The app
    still needs to send them.)
  - Also now supports either system or custom assets.
  - Updated the style to better match UX mock.
- Also, cleaned up some unused attributes in the layout file.

Note: updated TimerButtonView to extend EmphasizedNotificationButton in
order to use `setButtonBackground` and `setImageIcon`. Long term it's
probably better to use a new class instead, because
EmphasizedNotificationButton has logic not needed for the new code path.

Also see the TODOs for additional notes.

UX mock: https://screenshot.googleplex.com/4kdALZNAPaV38Bj
Before: https://hsv.googleplex.com/5971323116322816
After: https://hsv.googleplex.com/5508296533344256

Flag: android.app.api_rich_ongoing
Flag: com.android.systemui.notification_row_content_binder_refactor
Bug: 337261753
Bug: 343942780
Test: Trigger running timer; try the action buttons too.
Test: TimerViewModelTest
Change-Id: I96deec26c0c9ac6ac8f3bba3e6eaf3179d4e6893
parent eea96688
Loading
Loading
Loading
Loading
+5 −2
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@

package com.android.systemui.statusbar.notification.row.ui.viewmodel

import android.app.Notification
import android.app.PendingIntent
import android.platform.test.annotations.EnableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -90,7 +91,8 @@ class TimerViewModelTest : SysuiTestCase() {
        name: String = "example",
        timeRemaining: Duration = Duration.ofMinutes(3),
        resumeIntent: PendingIntent? = null,
        resetIntent: PendingIntent? = null
        addMinuteAction: Notification.Action? = null,
        resetAction: Notification.Action? = null
    ) =
        TimerContentModel(
            icon = icon,
@@ -99,7 +101,8 @@ class TimerViewModelTest : SysuiTestCase() {
                Paused(
                    timeRemaining = timeRemaining,
                    resumeIntent = resumeIntent,
                    resetIntent = resetIntent,
                    addMinuteAction = addMinuteAction,
                    resetAction = resetAction,
                )
        )
}
+13 −7
Original line number Diff line number Diff line
@@ -33,7 +33,6 @@
        android:id="@+id/icon"
        android:layout_width="24dp"
        android:layout_height="24dp"
        android:src="@drawable/ic_close"
        app:tint="@android:color/white"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toStartOf="@id/label"
@@ -88,11 +87,10 @@
        />

    <com.android.systemui.statusbar.notification.row.ui.view.TimerButtonView
        style="@*android:style/NotificationEmphasizedAction"
        android:id="@+id/mainButton"
        android:layout_width="124dp"
        android:layout_height="wrap_content"
        tools:text="Reset"
        tools:drawableStart="@android:drawable/ic_menu_add"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toStartOf="@id/altButton"
        app:layout_constraintTop_toBottomOf="@id/bottomOfTop"
@@ -101,15 +99,23 @@
        />

    <com.android.systemui.statusbar.notification.row.ui.view.TimerButtonView
        style="@*android:style/NotificationEmphasizedAction"
        android:id="@+id/altButton"
        tools:text="Reset"
        tools:drawableStart="@android:drawable/ic_menu_add"
        android:drawablePadding="2dp"
        android:drawableTint="@android:color/white"
        android:layout_width="124dp"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toBottomOf="@id/bottomOfTop"
        app:layout_constraintStart_toEndOf="@id/mainButton"
        app:layout_constraintEnd_toEndOf="@id/resetButton"
        android:paddingEnd="4dp"
        />

    <com.android.systemui.statusbar.notification.row.ui.view.TimerButtonView
        style="@*android:style/NotificationEmphasizedAction"
        android:id="@+id/resetButton"
        android:layout_width="124dp"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toBottomOf="@id/bottomOfTop"
        app:layout_constraintStart_toEndOf="@id/altButton"
        app:layout_constraintEnd_toEndOf="parent"
        android:paddingEnd="4dp"
        />
+40 −8
Original line number Diff line number Diff line
@@ -118,12 +118,15 @@ class RichOngoingNotificationContentExtractorImpl @Inject constructor() :
                val timeRemaining = parseTimeDelta(remaining)
                TimerContentModel(
                    icon = icon,
                    name = total,
                    // TODO: b/352142761 - define and use a string resource rather than " Timer".
                    // (The UX isn't final so using " Timer" for now).
                    name = total.replace("Σ", "") + " Timer",
                    state =
                        TimerContentModel.TimerState.Paused(
                            timeRemaining = timeRemaining,
                            resumeIntent = notification.findActionWithName("Resume"),
                            resetIntent = notification.findActionWithName("Reset"),
                            resumeIntent = notification.findStartIntent(),
                            addMinuteAction = notification.findAddMinuteAction(),
                            resetAction = notification.findResetAction(),
                        )
                )
            }
@@ -132,12 +135,15 @@ class RichOngoingNotificationContentExtractorImpl @Inject constructor() :
                val finishTime = parseCurrentTime(current) + parseTimeDelta(remaining).toMillis()
                TimerContentModel(
                    icon = icon,
                    name = total,
                    // TODO: b/352142761 - define and use a string resource rather than " Timer".
                    // (The UX isn't final so using " Timer" for now).
                    name = total.replace("Σ", "") + " Timer",
                    state =
                        TimerContentModel.TimerState.Running(
                            finishTime = finishTime,
                            pauseIntent = notification.findActionWithName("Pause"),
                            addOneMinuteIntent = notification.findActionWithName("Add 1 min"),
                            pauseIntent = notification.findPauseIntent(),
                            addMinuteAction = notification.findAddMinuteAction(),
                            resetAction = notification.findResetAction(),
                        )
                )
            }
@@ -145,8 +151,34 @@ class RichOngoingNotificationContentExtractorImpl @Inject constructor() :
        }
    }

    private fun Notification.findActionWithName(name: String): PendingIntent? {
        return actions.firstOrNull { name == it.title?.toString() }?.actionIntent
    private fun Notification.findPauseIntent(): PendingIntent? {
        return actions
            .firstOrNull { it.actionIntent.intent?.action?.endsWith(".PAUSE_TIMER") == true }
            ?.actionIntent
    }

    private fun Notification.findStartIntent(): PendingIntent? {
        return actions
            .firstOrNull { it.actionIntent.intent?.action?.endsWith(".START_TIMER") == true }
            ?.actionIntent
    }

    // TODO: b/352142761 - switch to system attributes for label and icon.
    //   - We probably want a consistent look for the Reset button. (Double check with UX.)
    //   - Using the custom assets now since I couldn't an existing "Reset" icon.
    private fun Notification.findResetAction(): Notification.Action? {
        return actions.firstOrNull {
            it.actionIntent.intent?.action?.endsWith(".RESET_TIMER") == true
        }
    }

    // TODO: b/352142761 - check with UX on whether this should be required.
    //   - Alternative is to allow for optional actions in addition to main and reset.
    //   - For optional actions, we should take the custom label and icon.
    private fun Notification.findAddMinuteAction(): Notification.Action? {
        return actions.firstOrNull {
            it.actionIntent.intent?.action?.endsWith(".ADD_MINUTE_TIMER") == true
        }
    }

    private fun parseCurrentTime(current: String): Long {
+8 −2
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.systemui.statusbar.notification.row.shared

import android.app.Notification
import android.app.PendingIntent
import java.time.Duration

@@ -32,6 +33,9 @@ data class TimerContentModel(
) : RichOngoingContentModel {
    /** The state (paused or running) of the timer, and relevant time */
    sealed interface TimerState {
        val addMinuteAction: Notification.Action?
        val resetAction: Notification.Action?

        /**
         * Indicates a running timer
         *
@@ -41,7 +45,8 @@ data class TimerContentModel(
        data class Running(
            val finishTime: Long,
            val pauseIntent: PendingIntent?,
            val addOneMinuteIntent: PendingIntent?,
            override val addMinuteAction: Notification.Action?,
            override val resetAction: Notification.Action?,
        ) : TimerState

        /**
@@ -53,7 +58,8 @@ data class TimerContentModel(
        data class Paused(
            val timeRemaining: Duration,
            val resumeIntent: PendingIntent?,
            val resetIntent: PendingIntent?,
            override val addMinuteAction: Notification.Action?,
            override val resetAction: Notification.Action?,
        ) : TimerState
    }
}
+8 −2
Original line number Diff line number Diff line
@@ -18,8 +18,9 @@ package com.android.systemui.statusbar.notification.row.ui.view

import android.annotation.DrawableRes
import android.content.Context
import android.graphics.BlendMode
import android.util.AttributeSet
import android.widget.Button
import com.android.internal.widget.EmphasizedNotificationButton

class TimerButtonView
@JvmOverloads
@@ -28,14 +29,19 @@ constructor(
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0,
    defStyleRes: Int = 0,
) : Button(context, attrs, defStyleAttr, defStyleRes) {
) : EmphasizedNotificationButton(context, attrs, defStyleAttr, defStyleRes) {

    private val Int.dp: Int
        get() = (this * context.resources.displayMetrics.density).toInt()

    fun setIcon(@DrawableRes icon: Int) {
        val drawable = context.getDrawable(icon)

        drawable?.mutate()
        drawable?.setTintList(textColors)
        drawable?.setTintBlendMode(BlendMode.SRC_IN)
        drawable?.setBounds(0, 0, 24.dp, 24.dp)

        setCompoundDrawablesRelative(drawable, null, null, null)
    }
}
Loading