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 @@ + +