diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index d04a0fb..b5cbd54 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -35,5 +35,16 @@
android:name="android.appwidget.provider"
android:resource="@xml/agenda_widget_info" />
+
+
+
+
+
+
+
diff --git a/app/src/main/java/space/darkest/nova/android/widget/ClockWidget.kt b/app/src/main/java/space/darkest/nova/android/widget/ClockWidget.kt
new file mode 100644
index 0000000..1fbdadc
--- /dev/null
+++ b/app/src/main/java/space/darkest/nova/android/widget/ClockWidget.kt
@@ -0,0 +1,156 @@
+package space.darkest.nova.android.widget
+
+import android.app.AlarmManager
+import android.content.Context
+import android.content.Intent
+import android.provider.AlarmClock
+import android.provider.Settings
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import androidx.glance.GlanceId
+import androidx.glance.GlanceModifier
+import androidx.glance.GlanceTheme
+import androidx.glance.action.clickable
+import androidx.glance.appwidget.GlanceAppWidget
+import androidx.glance.appwidget.GlanceAppWidgetReceiver
+import androidx.glance.appwidget.action.actionStartActivity
+import androidx.glance.appwidget.provideContent
+import androidx.glance.background
+import androidx.glance.color.DynamicThemeColorProviders
+import androidx.glance.layout.Alignment
+import androidx.glance.layout.Box
+import androidx.glance.layout.Column
+import androidx.glance.layout.Row
+import androidx.glance.layout.Spacer
+import androidx.glance.layout.fillMaxSize
+import androidx.glance.layout.height
+import androidx.glance.layout.padding
+import androidx.glance.layout.width
+import androidx.glance.text.FontWeight
+import androidx.glance.text.Text
+import androidx.glance.text.TextStyle
+import java.time.Instant
+import java.time.LocalDate
+import java.time.LocalTime
+import java.time.ZoneId
+import java.time.format.DateTimeFormatter
+import java.time.format.TextStyle as DateTextStyle
+import java.util.Locale
+
+class ClockWidget : GlanceAppWidget() {
+
+ override suspend fun provideGlance(context: Context, id: GlanceId) {
+ val alarmManager = context.getSystemService(AlarmManager::class.java)
+ val nextAlarm = alarmManager.nextAlarmClock?.let { alarm ->
+ Instant.ofEpochMilli(alarm.triggerTime)
+ .atZone(ZoneId.systemDefault())
+ .toLocalDateTime()
+ }
+
+ provideContent {
+ GlanceTheme(colors = DynamicThemeColorProviders) {
+ ClockContent(nextAlarm?.toLocalTime(), nextAlarm?.toLocalDate())
+ }
+ }
+ }
+
+ @Composable
+ private fun ClockContent(alarmTime: LocalTime?, alarmDate: LocalDate?) {
+ val now = LocalTime.now()
+ val today = LocalDate.now()
+ val hours = now.format(DateTimeFormatter.ofPattern("HH"))
+ val minutes = now.format(DateTimeFormatter.ofPattern("mm"))
+ val dateStr = today.let {
+ val day = it.dayOfWeek.getDisplayName(DateTextStyle.SHORT, Locale.getDefault())
+ val date = it.format(DateTimeFormatter.ofPattern("d MMM"))
+ "$day, $date"
+ }
+
+ Box(
+ modifier = GlanceModifier
+ .fillMaxSize()
+ .background(GlanceTheme.colors.widgetBackground)
+ .padding(16.dp)
+ .clickable(actionStartActivity(Intent(AlarmClock.ACTION_SHOW_ALARMS))),
+ contentAlignment = Alignment.CenterStart,
+ ) {
+ Column {
+ // time row: hours (primary) : minutes (tertiary)
+ Row(verticalAlignment = Alignment.Bottom) {
+ Text(
+ text = hours,
+ style = TextStyle(
+ color = GlanceTheme.colors.primary,
+ fontSize = 48.sp,
+ fontWeight = FontWeight.Bold,
+ ),
+ )
+ Text(
+ text = ":",
+ style = TextStyle(
+ color = GlanceTheme.colors.onSurfaceVariant,
+ fontSize = 48.sp,
+ fontWeight = FontWeight.Bold,
+ ),
+ )
+ Text(
+ text = minutes,
+ style = TextStyle(
+ color = GlanceTheme.colors.tertiary,
+ fontSize = 48.sp,
+ fontWeight = FontWeight.Bold,
+ ),
+ )
+ }
+
+ Spacer(GlanceModifier.height(2.dp))
+
+ // date
+ Text(
+ text = dateStr,
+ style = TextStyle(
+ color = GlanceTheme.colors.onSurfaceVariant,
+ fontSize = 14.sp,
+ ),
+ )
+
+ // next alarm
+ if (alarmTime != null && alarmDate != null) {
+ Spacer(GlanceModifier.height(4.dp))
+ Row(verticalAlignment = Alignment.CenterVertically) {
+ Text(
+ text = "\u23F0",
+ style = TextStyle(fontSize = 12.sp),
+ )
+ Spacer(GlanceModifier.width(4.dp))
+ Text(
+ text = formatAlarm(alarmTime, alarmDate),
+ style = TextStyle(
+ color = GlanceTheme.colors.secondary,
+ fontSize = 12.sp,
+ ),
+ )
+ }
+ }
+ }
+ }
+ }
+
+ private fun formatAlarm(time: LocalTime, date: LocalDate): String {
+ val today = LocalDate.now()
+ val timeStr = time.format(DateTimeFormatter.ofPattern("HH:mm"))
+ return when (date) {
+ today -> timeStr
+ today.plusDays(1) -> "Tomorrow $timeStr"
+ else -> {
+ val dayName = date.dayOfWeek.getDisplayName(DateTextStyle.SHORT, Locale.getDefault())
+ "$dayName $timeStr"
+ }
+ }
+ }
+}
+
+class ClockWidgetReceiver : GlanceAppWidgetReceiver() {
+ override val glanceAppWidget: GlanceAppWidget = ClockWidget()
+}
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index b70a0ee..142c6b1 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1,5 +1,6 @@
Nova Shell
Calendar agenda showing upcoming events
+ Clock with next alarm
Loading…
diff --git a/app/src/main/res/xml/clock_widget_info.xml b/app/src/main/res/xml/clock_widget_info.xml
new file mode 100644
index 0000000..c29ca70
--- /dev/null
+++ b/app/src/main/res/xml/clock_widget_info.xml
@@ -0,0 +1,15 @@
+
+