fix: redraw the next-alarm widget at midnight
The widget worked out "Today" when it was drawn, and nothing redrew it when the date changed, so a widget drawn on Monday night still named Tuesday on Tuesday morning. It now shares the analog faces' midnight redraw, renamed to MidnightRefresh, and redraws on a clock or zone change.
This commit is contained in:
1 parent
9af3d2b515
commit
85c12811b0
7 files changed
+55
-23
No files matched your search
@@ -367,7 +367,7 @@
|
||||
updatePeriodMillis is 0 in every provider-info XML — WidgetSync
|
||||
is the one refresh trigger for the data widgets, and the analog
|
||||
widget's date is redrawn at midnight and on a clock or zone
|
||||
change (widget/AnalogDateRefresh.kt). -->
|
||||
change (widget/MidnightRefresh.kt). -->
|
||||
<receiver
|
||||
android:name=".widget.NextAlarmWidgetReceiver"
|
||||
android:label="@string/widget_next_alarm_label"
|
||||
|
||||
@@ -8,7 +8,8 @@ import de.jeanlucmakiola.clockula.alarm.AlarmEngine
|
||||
import de.jeanlucmakiola.clockula.stopwatch.StopwatchEngine
|
||||
import de.jeanlucmakiola.clockula.timer.TimerEngine
|
||||
import de.jeanlucmakiola.clockula.widget.AnalogClockWidget
|
||||
import de.jeanlucmakiola.clockula.widget.AnalogDateRefresh
|
||||
import de.jeanlucmakiola.clockula.widget.MidnightRefresh
|
||||
import de.jeanlucmakiola.clockula.widget.NextAlarmWidget
|
||||
import de.jeanlucmakiola.floret.di.ApplicationScope
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
@@ -92,7 +93,8 @@ class SystemEventReceiver : HiltBroadcastReceiver() {
|
||||
// The analog faces' date is drawn for the old day or
|
||||
// zone, and the midnight redraw armed for the old one.
|
||||
AnalogClockWidget().updateAll(context)
|
||||
AnalogDateRefresh.schedule(context)
|
||||
NextAlarmWidget().updateAll(context)
|
||||
MidnightRefresh.schedule(context)
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
|
||||
@@ -264,7 +264,7 @@ private fun RemoteViews.recolor(context: Context, style: AnalogClockStyle, overl
|
||||
* live in a vector drawable, so this hand is a bitmap — an alpha mask, tinted by
|
||||
* `setSecondHandTintList` in the launcher so it still follows day/night.
|
||||
* It is redrawn at midnight and on a clock or zone change
|
||||
* (`widget/AnalogDateRefresh.kt`).
|
||||
* (`widget/MidnightRefresh.kt`).
|
||||
*/
|
||||
private fun dateHand(context: Context): Bitmap {
|
||||
val px = 360
|
||||
|
||||
@@ -39,10 +39,11 @@ class AnalogClockWidgetReceiver : GlanceAppWidgetReceiver() {
|
||||
override val glanceAppWidget: GlanceAppWidget = AnalogClockWidget()
|
||||
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
// Midnight: redraw every analog widget, as the platform's own update
|
||||
// Midnight: redraw every dated widget, as the platform's own update
|
||||
// would, which re-arms the next one in `onUpdate`.
|
||||
if (intent.action == AnalogDateRefresh.ACTION_REFRESH) {
|
||||
onUpdate(context, AppWidgetManager.getInstance(context), AnalogDateRefresh.placedIds(context))
|
||||
if (intent.action == MidnightRefresh.ACTION_REFRESH) {
|
||||
onUpdate(context, AppWidgetManager.getInstance(context), MidnightRefresh.placedIds(context))
|
||||
MidnightRefresh.updateNextAlarm(context)
|
||||
return
|
||||
}
|
||||
super.onReceive(context, intent)
|
||||
@@ -50,11 +51,12 @@ class AnalogClockWidgetReceiver : GlanceAppWidgetReceiver() {
|
||||
|
||||
override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray) {
|
||||
super.onUpdate(context, appWidgetManager, appWidgetIds)
|
||||
AnalogDateRefresh.schedule(context)
|
||||
MidnightRefresh.schedule(context)
|
||||
}
|
||||
|
||||
override fun onDisabled(context: Context) {
|
||||
super.onDisabled(context)
|
||||
AnalogDateRefresh.cancel(context)
|
||||
// Disarmed only if no next-alarm widget still needs it.
|
||||
MidnightRefresh.schedule(context)
|
||||
}
|
||||
}
|
||||
+28
-13
@@ -11,23 +11,27 @@ import java.time.Instant
|
||||
import java.time.ZonedDateTime
|
||||
|
||||
/**
|
||||
* Keeps the analog faces' date current. The dated faces draw today's date
|
||||
* into a bitmap hand (`ui/widget/AnalogClockWidgetContent.kt`), which only
|
||||
* changes when the widget is re-rendered — and nothing the app writes
|
||||
* re-renders it. So the analog widget is redrawn at each local midnight (a
|
||||
* non-waking, windowed alarm: a dark screen has no date to show, and a
|
||||
* minute's slack lets the platform batch it) and on a clock or zone change
|
||||
* Keeps the widgets that show a date current. The dated analog faces draw
|
||||
* today's date into a bitmap hand (`ui/widget/AnalogClockWidgetContent.kt`),
|
||||
* and the next-alarm widget says "Today" or names the day, and both only
|
||||
* change when the widget is re-rendered — which nothing the app writes does
|
||||
* at midnight. So both are redrawn at each local midnight (a non-waking,
|
||||
* windowed alarm: a dark screen has no date to show, and a minute's slack
|
||||
* lets the platform batch it) and on a clock or zone change
|
||||
* (`SystemEventReceiver`).
|
||||
*
|
||||
* The alarm is delivered to the analog receiver, which redraws its own
|
||||
* widgets and hands the next-alarm widget a standard update broadcast.
|
||||
*/
|
||||
object AnalogDateRefresh {
|
||||
object MidnightRefresh {
|
||||
|
||||
const val ACTION_REFRESH = "de.jeanlucmakiola.clockula.widget.action.REFRESH_ANALOG_DATE"
|
||||
|
||||
/** Arms the next midnight redraw while any analog widget is placed, and disarms it otherwise. */
|
||||
/** Arms the next midnight redraw while any dated widget is placed, and disarms it otherwise. */
|
||||
fun schedule(context: Context) {
|
||||
val manager = context.getSystemService(AlarmManager::class.java) ?: return
|
||||
val pendingIntent = pendingIntent(context)
|
||||
if (placedIds(context).isEmpty()) {
|
||||
if (placedIds(context).isEmpty() && nextAlarmIds(context).isEmpty()) {
|
||||
manager.cancel(pendingIntent)
|
||||
return
|
||||
}
|
||||
@@ -38,15 +42,26 @@ object AnalogDateRefresh {
|
||||
manager.setWindow(AlarmManager.RTC, midnight, WINDOW_MILLIS, pendingIntent)
|
||||
}
|
||||
|
||||
fun cancel(context: Context) {
|
||||
context.getSystemService(AlarmManager::class.java)?.cancel(pendingIntent(context))
|
||||
}
|
||||
|
||||
/** The placed analog widgets' ids — what an [ACTION_REFRESH] hands to `onUpdate`. */
|
||||
fun placedIds(context: Context): IntArray =
|
||||
AppWidgetManager.getInstance(context)
|
||||
.getAppWidgetIds(ComponentName(context, AnalogClockWidgetReceiver::class.java))
|
||||
|
||||
/** Redraws the placed next-alarm widgets the way the platform's own update would. */
|
||||
fun updateNextAlarm(context: Context) {
|
||||
val ids = nextAlarmIds(context)
|
||||
if (ids.isEmpty()) return
|
||||
context.sendBroadcast(
|
||||
Intent(context, NextAlarmWidgetReceiver::class.java)
|
||||
.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE)
|
||||
.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, ids),
|
||||
)
|
||||
}
|
||||
|
||||
private fun nextAlarmIds(context: Context): IntArray =
|
||||
AppWidgetManager.getInstance(context)
|
||||
.getAppWidgetIds(ComponentName(context, NextAlarmWidgetReceiver::class.java))
|
||||
|
||||
private fun pendingIntent(context: Context): PendingIntent =
|
||||
PendingIntent.getBroadcast(
|
||||
context,
|
||||
@@ -1,5 +1,6 @@
|
||||
package de.jeanlucmakiola.clockula.widget
|
||||
|
||||
import android.appwidget.AppWidgetManager
|
||||
import android.content.Context
|
||||
import androidx.glance.GlanceId
|
||||
import androidx.glance.appwidget.GlanceAppWidget
|
||||
@@ -27,4 +28,16 @@ class NextAlarmWidget : GlanceAppWidget() {
|
||||
|
||||
class NextAlarmWidgetReceiver : GlanceAppWidgetReceiver() {
|
||||
override val glanceAppWidget: GlanceAppWidget = NextAlarmWidget()
|
||||
|
||||
// "Today" and the day's name go stale at midnight, so this widget shares
|
||||
// the analog faces' midnight redraw.
|
||||
override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray) {
|
||||
super.onUpdate(context, appWidgetManager, appWidgetIds)
|
||||
MidnightRefresh.schedule(context)
|
||||
}
|
||||
|
||||
override fun onDisabled(context: Context) {
|
||||
super.onDisabled(context)
|
||||
MidnightRefresh.schedule(context)
|
||||
}
|
||||
}
|
||||
@@ -33,7 +33,7 @@ import kotlin.time.Duration.Companion.milliseconds
|
||||
* the stopwatch, not the clocks. The quick-timer widget follows the timers,
|
||||
* since it shows the countdown of the timer it started
|
||||
* (`widget/QuickTimerWidget.kt`). The analog clock reads none of them; its
|
||||
* date is kept current by [AnalogDateRefresh].
|
||||
* date is kept current by [MidnightRefresh].
|
||||
*
|
||||
* Started once, from [de.jeanlucmakiola.clockula.ClockulaApp.onCreate], in the
|
||||
* app's `@ApplicationScope` — never driven by a `Ticker`, so an idle home
|
||||
|
||||
Reference in new issue
Block a user