UI уведомления новый
This commit is contained in:
@@ -26,8 +26,9 @@ import kotlin.math.roundToInt
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Single foreground notification (one [NotificationManager.notify] id) that accumulates all task
|
* Single foreground notification (one [NotificationManager.notify] id) that accumulates all task
|
||||||
* states. Multiple progress bars are implemented via [RemoteViews] — the standard
|
* states. Each row: [task title], then a horizontal strip with a smaller **label** (ellipsis)
|
||||||
* [NotificationCompat.Builder.setProgress] API only supports one bar per notification.
|
* beside a horizontal [ProgressBar] (fixed width). Indeterminate tasks hide the bar; the label
|
||||||
|
* line cycles suffix `""` → `.` → `..` → `...` by wall clock between [notify] calls.
|
||||||
*/
|
*/
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class TaskPipelineForegroundService : Service() {
|
class TaskPipelineForegroundService : Service() {
|
||||||
@@ -153,6 +154,7 @@ class TaskPipelineForegroundService : Service() {
|
|||||||
val color = notificationTemplateTextColor()
|
val color = notificationTemplateTextColor()
|
||||||
for (i in 0 until MAX_TASK_ROWS) {
|
for (i in 0 until MAX_TASK_ROWS) {
|
||||||
remoteViews.setTextColor(TASK_TITLE_IDS[i], color)
|
remoteViews.setTextColor(TASK_TITLE_IDS[i], color)
|
||||||
|
remoteViews.setTextColor(TASK_SUBTITLE_IDS[i], color)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -195,18 +197,34 @@ class TaskPipelineForegroundService : Service() {
|
|||||||
|
|
||||||
private fun hideRow(remoteViews: RemoteViews, index: Int) {
|
private fun hideRow(remoteViews: RemoteViews, index: Int) {
|
||||||
remoteViews.setViewVisibility(TASK_ROW_IDS[index], View.GONE)
|
remoteViews.setViewVisibility(TASK_ROW_IDS[index], View.GONE)
|
||||||
|
remoteViews.setViewVisibility(TASK_LABEL_BAR_ROW_IDS[index], View.GONE)
|
||||||
|
remoteViews.setTextViewText(TASK_SUBTITLE_IDS[index], "")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun showTaskRow(remoteViews: RemoteViews, index: Int, task: TaskForegroundItem) {
|
private fun showTaskRow(remoteViews: RemoteViews, index: Int, task: TaskForegroundItem) {
|
||||||
remoteViews.setViewVisibility(TASK_ROW_IDS[index], View.VISIBLE)
|
remoteViews.setViewVisibility(TASK_ROW_IDS[index], View.VISIBLE)
|
||||||
remoteViews.setTextViewText(TASK_TITLE_IDS[index], taskTitleText(task))
|
remoteViews.setViewVisibility(TASK_LABEL_BAR_ROW_IDS[index], View.VISIBLE)
|
||||||
remoteViews.setViewVisibility(TASK_PROGRESS_IDS[index], View.VISIBLE)
|
remoteViews.setTextViewText(TASK_TITLE_IDS[index], task.title)
|
||||||
|
val label = task.progress?.label?.trim().orEmpty()
|
||||||
val fraction = task.progress?.fraction
|
val fraction = task.progress?.fraction
|
||||||
if (fraction != null) {
|
if (fraction != null) {
|
||||||
|
if (label.isNotEmpty()) {
|
||||||
|
remoteViews.setViewVisibility(TASK_SUBTITLE_IDS[index], View.VISIBLE)
|
||||||
|
remoteViews.setTextViewText(TASK_SUBTITLE_IDS[index], label)
|
||||||
|
} else {
|
||||||
|
remoteViews.setViewVisibility(TASK_SUBTITLE_IDS[index], View.GONE)
|
||||||
|
remoteViews.setTextViewText(TASK_SUBTITLE_IDS[index], "")
|
||||||
|
}
|
||||||
|
remoteViews.setViewVisibility(TASK_PROGRESS_IDS[index], View.VISIBLE)
|
||||||
val pct = (fraction.coerceIn(0f, 1f) * 100).roundToInt()
|
val pct = (fraction.coerceIn(0f, 1f) * 100).roundToInt()
|
||||||
remoteViews.setProgressBar(TASK_PROGRESS_IDS[index], 100, pct, false)
|
remoteViews.setProgressBar(TASK_PROGRESS_IDS[index], 100, pct, false)
|
||||||
} else {
|
} else {
|
||||||
remoteViews.setProgressBar(TASK_PROGRESS_IDS[index], 0, 0, true)
|
remoteViews.setViewVisibility(TASK_SUBTITLE_IDS[index], View.VISIBLE)
|
||||||
|
remoteViews.setTextViewText(
|
||||||
|
TASK_SUBTITLE_IDS[index],
|
||||||
|
indeterminateSubtitleWithDots(label),
|
||||||
|
)
|
||||||
|
remoteViews.setViewVisibility(TASK_PROGRESS_IDS[index], View.GONE)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -216,13 +234,22 @@ class TaskPipelineForegroundService : Service() {
|
|||||||
TASK_TITLE_IDS[index],
|
TASK_TITLE_IDS[index],
|
||||||
getString(R.string.task_notification_more_tasks, remainingCount),
|
getString(R.string.task_notification_more_tasks, remainingCount),
|
||||||
)
|
)
|
||||||
remoteViews.setViewVisibility(TASK_PROGRESS_IDS[index], View.GONE)
|
remoteViews.setViewVisibility(TASK_LABEL_BAR_ROW_IDS[index], View.GONE)
|
||||||
|
remoteViews.setTextViewText(TASK_SUBTITLE_IDS[index], "")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun taskTitleText(task: TaskForegroundItem): String {
|
/**
|
||||||
val label = task.progress?.label?.trim().orEmpty()
|
* Cycles `""` → `.` → `..` → `...` (wall clock) so each [notify] can advance the suffix after
|
||||||
val title = task.title
|
* the optional base label.
|
||||||
return if (label.isNotEmpty()) "$title — $label" else title
|
*/
|
||||||
|
private fun indeterminateSubtitleWithDots(baseLabel: String): String {
|
||||||
|
val dots = when (((System.currentTimeMillis() / DOTS_ANIMATION_STEP_MS) % 4L).toInt()) {
|
||||||
|
0 -> ""
|
||||||
|
1 -> "."
|
||||||
|
2 -> ".."
|
||||||
|
else -> "..."
|
||||||
|
}
|
||||||
|
return if (baseLabel.isEmpty()) dots else baseLabel + dots
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@@ -230,6 +257,8 @@ class TaskPipelineForegroundService : Service() {
|
|||||||
private const val FOREGROUND_NOTIFICATION_ID = 1001
|
private const val FOREGROUND_NOTIFICATION_ID = 1001
|
||||||
private const val MIN_NOTIFICATION_UPDATE_INTERVAL_MS = 500L
|
private const val MIN_NOTIFICATION_UPDATE_INTERVAL_MS = 500L
|
||||||
|
|
||||||
|
private const val DOTS_ANIMATION_STEP_MS = 400L
|
||||||
|
|
||||||
/** Must match [R.layout.notification_wallenc_tasks_big] row count. */
|
/** Must match [R.layout.notification_wallenc_tasks_big] row count. */
|
||||||
private const val MAX_TASK_ROWS = 8
|
private const val MAX_TASK_ROWS = 8
|
||||||
|
|
||||||
@@ -265,5 +294,27 @@ class TaskPipelineForegroundService : Service() {
|
|||||||
R.id.task_progress_6,
|
R.id.task_progress_6,
|
||||||
R.id.task_progress_7,
|
R.id.task_progress_7,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
private val TASK_SUBTITLE_IDS = intArrayOf(
|
||||||
|
R.id.task_subtitle_0,
|
||||||
|
R.id.task_subtitle_1,
|
||||||
|
R.id.task_subtitle_2,
|
||||||
|
R.id.task_subtitle_3,
|
||||||
|
R.id.task_subtitle_4,
|
||||||
|
R.id.task_subtitle_5,
|
||||||
|
R.id.task_subtitle_6,
|
||||||
|
R.id.task_subtitle_7,
|
||||||
|
)
|
||||||
|
|
||||||
|
private val TASK_LABEL_BAR_ROW_IDS = intArrayOf(
|
||||||
|
R.id.task_label_bar_row_0,
|
||||||
|
R.id.task_label_bar_row_1,
|
||||||
|
R.id.task_label_bar_row_2,
|
||||||
|
R.id.task_label_bar_row_3,
|
||||||
|
R.id.task_label_bar_row_4,
|
||||||
|
R.id.task_label_bar_row_5,
|
||||||
|
R.id.task_label_bar_row_6,
|
||||||
|
R.id.task_label_bar_row_7,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,12 +23,30 @@
|
|||||||
android:maxLines="2"
|
android:maxLines="2"
|
||||||
android:textSize="13sp" />
|
android:textSize="13sp" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/task_label_bar_row_0"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="2dp"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/task_subtitle_0"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="2"
|
||||||
|
android:textSize="11sp" />
|
||||||
|
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
android:id="@+id/task_progress_0"
|
android:id="@+id/task_progress_0"
|
||||||
style="?android:attr/progressBarStyleHorizontal"
|
style="?android:attr/progressBarStyleHorizontal"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="112dp"
|
||||||
android:layout_height="8dp"
|
android:layout_height="8dp"
|
||||||
android:layout_marginTop="2dp" />
|
android:layout_marginStart="2dp" />
|
||||||
|
</LinearLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
@@ -47,12 +65,30 @@
|
|||||||
android:maxLines="2"
|
android:maxLines="2"
|
||||||
android:textSize="13sp" />
|
android:textSize="13sp" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/task_label_bar_row_1"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="2dp"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/task_subtitle_1"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="2"
|
||||||
|
android:textSize="11sp" />
|
||||||
|
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
android:id="@+id/task_progress_1"
|
android:id="@+id/task_progress_1"
|
||||||
style="?android:attr/progressBarStyleHorizontal"
|
style="?android:attr/progressBarStyleHorizontal"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="112dp"
|
||||||
android:layout_height="8dp"
|
android:layout_height="8dp"
|
||||||
android:layout_marginTop="2dp" />
|
android:layout_marginStart="2dp" />
|
||||||
|
</LinearLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
@@ -71,12 +107,30 @@
|
|||||||
android:maxLines="2"
|
android:maxLines="2"
|
||||||
android:textSize="13sp" />
|
android:textSize="13sp" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/task_label_bar_row_2"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="2dp"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/task_subtitle_2"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="2"
|
||||||
|
android:textSize="11sp" />
|
||||||
|
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
android:id="@+id/task_progress_2"
|
android:id="@+id/task_progress_2"
|
||||||
style="?android:attr/progressBarStyleHorizontal"
|
style="?android:attr/progressBarStyleHorizontal"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="112dp"
|
||||||
android:layout_height="8dp"
|
android:layout_height="8dp"
|
||||||
android:layout_marginTop="2dp" />
|
android:layout_marginStart="2dp" />
|
||||||
|
</LinearLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
@@ -95,12 +149,30 @@
|
|||||||
android:maxLines="2"
|
android:maxLines="2"
|
||||||
android:textSize="13sp" />
|
android:textSize="13sp" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/task_label_bar_row_3"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="2dp"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/task_subtitle_3"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="2"
|
||||||
|
android:textSize="11sp" />
|
||||||
|
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
android:id="@+id/task_progress_3"
|
android:id="@+id/task_progress_3"
|
||||||
style="?android:attr/progressBarStyleHorizontal"
|
style="?android:attr/progressBarStyleHorizontal"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="112dp"
|
||||||
android:layout_height="8dp"
|
android:layout_height="8dp"
|
||||||
android:layout_marginTop="2dp" />
|
android:layout_marginStart="2dp" />
|
||||||
|
</LinearLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
@@ -119,12 +191,30 @@
|
|||||||
android:maxLines="2"
|
android:maxLines="2"
|
||||||
android:textSize="13sp" />
|
android:textSize="13sp" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/task_label_bar_row_4"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="2dp"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/task_subtitle_4"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="2"
|
||||||
|
android:textSize="11sp" />
|
||||||
|
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
android:id="@+id/task_progress_4"
|
android:id="@+id/task_progress_4"
|
||||||
style="?android:attr/progressBarStyleHorizontal"
|
style="?android:attr/progressBarStyleHorizontal"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="112dp"
|
||||||
android:layout_height="8dp"
|
android:layout_height="8dp"
|
||||||
android:layout_marginTop="2dp" />
|
android:layout_marginStart="2dp" />
|
||||||
|
</LinearLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
@@ -143,12 +233,30 @@
|
|||||||
android:maxLines="2"
|
android:maxLines="2"
|
||||||
android:textSize="13sp" />
|
android:textSize="13sp" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/task_label_bar_row_5"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="2dp"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/task_subtitle_5"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="2"
|
||||||
|
android:textSize="11sp" />
|
||||||
|
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
android:id="@+id/task_progress_5"
|
android:id="@+id/task_progress_5"
|
||||||
style="?android:attr/progressBarStyleHorizontal"
|
style="?android:attr/progressBarStyleHorizontal"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="112dp"
|
||||||
android:layout_height="8dp"
|
android:layout_height="8dp"
|
||||||
android:layout_marginTop="2dp" />
|
android:layout_marginStart="2dp" />
|
||||||
|
</LinearLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
@@ -167,12 +275,30 @@
|
|||||||
android:maxLines="2"
|
android:maxLines="2"
|
||||||
android:textSize="13sp" />
|
android:textSize="13sp" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/task_label_bar_row_6"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="2dp"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/task_subtitle_6"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="2"
|
||||||
|
android:textSize="11sp" />
|
||||||
|
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
android:id="@+id/task_progress_6"
|
android:id="@+id/task_progress_6"
|
||||||
style="?android:attr/progressBarStyleHorizontal"
|
style="?android:attr/progressBarStyleHorizontal"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="112dp"
|
||||||
android:layout_height="8dp"
|
android:layout_height="8dp"
|
||||||
android:layout_marginTop="2dp" />
|
android:layout_marginStart="2dp" />
|
||||||
|
</LinearLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
@@ -191,11 +317,29 @@
|
|||||||
android:maxLines="2"
|
android:maxLines="2"
|
||||||
android:textSize="13sp" />
|
android:textSize="13sp" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/task_label_bar_row_7"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="2dp"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/task_subtitle_7"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="2"
|
||||||
|
android:textSize="11sp" />
|
||||||
|
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
android:id="@+id/task_progress_7"
|
android:id="@+id/task_progress_7"
|
||||||
style="?android:attr/progressBarStyleHorizontal"
|
style="?android:attr/progressBarStyleHorizontal"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="112dp"
|
||||||
android:layout_height="8dp"
|
android:layout_height="8dp"
|
||||||
android:layout_marginTop="2dp" />
|
android:layout_marginStart="2dp" />
|
||||||
|
</LinearLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|||||||
Reference in New Issue
Block a user