add clock widget with dual-color time and next alarm
This commit is contained in:
parent
1dbd96d961
commit
2335e6d926
4 changed files with 183 additions and 0 deletions
|
|
@ -35,5 +35,16 @@
|
||||||
android:name="android.appwidget.provider"
|
android:name="android.appwidget.provider"
|
||||||
android:resource="@xml/agenda_widget_info" />
|
android:resource="@xml/agenda_widget_info" />
|
||||||
</receiver>
|
</receiver>
|
||||||
|
|
||||||
|
<receiver
|
||||||
|
android:name=".widget.ClockWidgetReceiver"
|
||||||
|
android:exported="false">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
||||||
|
</intent-filter>
|
||||||
|
<meta-data
|
||||||
|
android:name="android.appwidget.provider"
|
||||||
|
android:resource="@xml/clock_widget_info" />
|
||||||
|
</receiver>
|
||||||
</application>
|
</application>
|
||||||
</manifest>
|
</manifest>
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
<resources>
|
<resources>
|
||||||
<string name="app_name">Nova Shell</string>
|
<string name="app_name">Nova Shell</string>
|
||||||
<string name="widget_description">Calendar agenda showing upcoming events</string>
|
<string name="widget_description">Calendar agenda showing upcoming events</string>
|
||||||
|
<string name="clock_widget_description">Clock with next alarm</string>
|
||||||
<string name="widget_loading">Loading…</string>
|
<string name="widget_loading">Loading…</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
||||||
15
app/src/main/res/xml/clock_widget_info.xml
Normal file
15
app/src/main/res/xml/clock_widget_info.xml
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:description="@string/clock_widget_description"
|
||||||
|
android:initialLayout="@layout/glance_default_loading_layout"
|
||||||
|
android:minWidth="180dp"
|
||||||
|
android:minHeight="110dp"
|
||||||
|
android:minResizeWidth="110dp"
|
||||||
|
android:minResizeHeight="80dp"
|
||||||
|
android:maxResizeWidth="530dp"
|
||||||
|
android:maxResizeHeight="200dp"
|
||||||
|
android:resizeMode="horizontal|vertical"
|
||||||
|
android:targetCellWidth="3"
|
||||||
|
android:targetCellHeight="2"
|
||||||
|
android:updatePeriodMillis="60000"
|
||||||
|
android:widgetCategory="home_screen" />
|
||||||
Loading…
Add table
Add a link
Reference in a new issue