add new hourly widget

This commit is contained in:
PranshulGG committed 2026-08-13 15:50:48 +05:30
1 parent 1e150e287b
commit 343c3d4bc2
10 files changed
+221 -9

No files matched your search

@@ -74,10 +74,14 @@ fun widgetWeatherMapper(
)
val icon = it.weatherCondition.toIcon(matchingDaily, it.time)
val precipitationProbability = it.precipitationProbability
WidgetHourlyItem(
time = formattedTime,
temp = "${temperature}°",
conditionIcon = icon
conditionIcon = icon,
precipitationProbability = precipitationProbability
)
}
@@ -10,6 +10,7 @@ import androidx.glance.appwidget.updateAll
import com.pranshulgg.weather_master_app.widgets.WeatherWidgetStateDefinition
import com.pranshulgg.weather_master_app.widgets.config.WidgetConfig
import com.pranshulgg.weather_master_app.widgets.glance.GlanceWidget
import com.pranshulgg.weather_master_app.widgets.hourly.WidgetHourly
import com.pranshulgg.weather_master_app.widgets.pill.WidgetPill
import com.pranshulgg.weather_master_app.widgets.summary.SummaryWidget
import com.pranshulgg.weather_master_app.widgets.weather.WeatherWidget
@@ -31,6 +32,8 @@ class WeatherWidgetUpdater(
private val widgetWeather4 = Weather4Widget()
private val widgetHourly = WidgetHourly()
suspend fun update(json: String) {
val manager = GlanceAppWidgetManager(context)
suspend fun <T : GlanceAppWidget> updateWidgets(
@@ -54,6 +57,7 @@ class WeatherWidgetUpdater(
updateWidgets(widgetGlance, manager.getGlanceIds(GlanceWidget::class.java))
updateWidgets(widgetClockDaily, manager.getGlanceIds(ClockDailyWidget::class.java))
updateWidgets(widgetWeather4, manager.getGlanceIds(Weather4Widget::class.java))
updateWidgets(widgetHourly, manager.getGlanceIds(WidgetHourly::class.java))
}
@@ -20,6 +20,7 @@ data class WidgetConfig(
val dailyCount: Int = 4,
val showDailyInsteadOfCurrent: Boolean = false,
val isWeatherBackground: Boolean = false,
val isDisplayHourly: Boolean = false
val isDisplayHourly: Boolean = false,
val showPrecipitationProbability: Boolean = false,
)
)
@@ -20,6 +20,8 @@ import com.pranshulgg.weather_master_app.core.ui.theme.isThemeDark
import com.pranshulgg.weather_master_app.data.worker.widgets.WeatherWidgetUpdater
import com.pranshulgg.weather_master_app.widgets.glance.GlanceWidgetReceiver
import com.pranshulgg.weather_master_app.widgets.glance.ui.GlanceWidgetConfig
import com.pranshulgg.weather_master_app.widgets.hourly.WidgetHourlyReceiver
import com.pranshulgg.weather_master_app.widgets.hourly.ui.HourlyWidgetConfig
import com.pranshulgg.weather_master_app.widgets.weather.WeatherWidgetReceiver
import com.pranshulgg.weather_master_app.widgets.weather.ui.WeatherWidgetConfig
import com.pranshulgg.weather_master_app.widgets.weather4.Weather4WidgerReceiver
@@ -126,6 +128,10 @@ class WidgetConfigActivity : ComponentActivity() {
Weather4WidgerReceiver::class.java.name -> {
Weather4Config { onDone(it) }
}
WidgetHourlyReceiver::class.java.name -> {
HourlyWidgetConfig { onDone(it) }
}
}
}
}
@@ -0,0 +1,138 @@
package com.pranshulgg.weather_master_app.widgets.hourly
import android.content.Context
import android.os.Build
import androidx.compose.runtime.Composable
import androidx.compose.ui.unit.dp
import androidx.glance.ColorFilter
import androidx.glance.GlanceId
import androidx.glance.GlanceModifier
import androidx.glance.GlanceTheme
import androidx.glance.ImageProvider
import androidx.glance.action.actionStartActivity
import androidx.glance.action.clickable
import androidx.glance.appwidget.GlanceAppWidget
import androidx.glance.appwidget.SizeMode
import androidx.glance.appwidget.cornerRadius
import androidx.glance.appwidget.provideContent
import androidx.glance.background
import androidx.glance.currentState
import androidx.glance.layout.Alignment
import androidx.glance.layout.Box
import androidx.glance.layout.Row
import androidx.glance.layout.fillMaxHeight
import androidx.glance.layout.fillMaxSize
import androidx.glance.layout.fillMaxWidth
import com.pranshulgg.weather_master_app.MainActivity
import com.pranshulgg.weather_master_app.R
import com.pranshulgg.weather_master_app.widgets.WeatherWidgetStateDefinition
import com.pranshulgg.weather_master_app.widgets.WeatherWidgetStateJson
import com.pranshulgg.weather_master_app.widgets.model.WidgetWeather
import com.pranshulgg.weather_master_app.widgets.ui.ReloadButton
import com.pranshulgg.weather_master_app.widgets.ui.colors.WidgetColors
import com.pranshulgg.weather_master_app.widgets.ui.colors.WidgetTheme
import com.pranshulgg.weather_master_app.widgets.weather.components.WidgetHourlyItem
import kotlinx.serialization.json.Json
class WidgetHourly : GlanceAppWidget() {
override val sizeMode = SizeMode.Exact
override val stateDefinition =
WeatherWidgetStateDefinition
override suspend fun provideGlance(
context: Context,
id: GlanceId
) {
provideContent {
val widgetState =
currentState<WeatherWidgetStateJson>()
val widgetColors = WidgetColors()
val json = widgetState.json
val state = json?.let {
Json.decodeFromString<WidgetWeather>(it)
}
val config = widgetState.config
if (state != null) {
val modifier = when (config.widgetTheme) {
WidgetTheme.TRANSPARENT -> GlanceModifier.fillMaxSize()
else -> GlanceModifier.fillMaxSize()
.appWidgetBackgroundShape(
config.widgetTheme,
widgetColors
)
}
val textColor = widgetColors
.getTextColor(config.widgetTextTheme, config.widgetTheme)
?: Pair(GlanceTheme.colors.onSurface, null)
val textColorVariant = widgetColors
.getTextVariantColor(config.widgetTextTheme, config.widgetTheme)
?: GlanceTheme.colors.onSurfaceVariant
Box(
modifier.clickable(actionStartActivity<MainActivity>())
) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalAlignment = Alignment.CenterHorizontally,
modifier = GlanceModifier.fillMaxWidth().fillMaxHeight()
) {
val hourlyIconSize = 22 * config.iconSize
val hourlyTextSize = 14 * config.fontSize
state.hourly.take(config.hourlyCount).forEach {
WidgetHourlyItem(
it.time,
it.temp,
it.conditionIcon,
hourlyTextSize,
hourlyIconSize,
textColor.first,
textColorVariant,
verticalPadding = 10.dp,
precipitationProbability = if (config.showPrecipitationProbability)
it.precipitationProbability else null
)
}
}
}
} else {
ReloadButton()
}
}
}
}
@Composable
private fun GlanceModifier.appWidgetBackgroundShape(
theme: WidgetTheme,
widgetColors: WidgetColors
): GlanceModifier {
val color = widgetColors.getBackgroundColor(
theme
)
return if (Build.VERSION.SDK_INT >= 31) {
this
.cornerRadius(android.R.dimen.system_app_widget_background_radius)
.background(color ?: GlanceTheme.colors.widgetBackground)
} else {
this
.background(
ImageProvider(R.drawable.weather_widget_background),
colorFilter = ColorFilter.tint(color ?: GlanceTheme.colors.widgetBackground)
)
}
}
@@ -0,0 +1,10 @@
package com.pranshulgg.weather_master_app.widgets.hourly
import androidx.glance.appwidget.GlanceAppWidgetReceiver
import com.pranshulgg.weather_master_app.widgets.pill.WidgetPill
class WidgetHourlyReceiver : GlanceAppWidgetReceiver() {
override val glanceAppWidget = WidgetHourly()
}
@@ -2,6 +2,7 @@ package com.pranshulgg.weather_master_app.widgets.weather.components
import android.graphics.Color
import androidx.compose.runtime.Composable
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.glance.GlanceModifier
@@ -28,12 +29,14 @@ fun WidgetHourlyItem(
fontSize: Float,
iconSize: Float,
textColor: ColorProvider,
textColorVariant: ColorProvider
textColorVariant: ColorProvider,
verticalPadding: Dp = 5.dp,
precipitationProbability: Int? = null
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = GlanceModifier.padding(horizontal = 5.dp, vertical = 5.dp)
modifier = GlanceModifier.padding(horizontal = 5.dp, vertical = verticalPadding)
) {
Text(
temp,
@@ -43,6 +46,17 @@ fun WidgetHourlyItem(
fontSize = fontSize.sp
)
)
if (precipitationProbability != null) {
Text(
"${precipitationProbability}%",
style = TextStyle(
color = textColorVariant,
fontSize = (fontSize * 0.9).sp
)
)
}
Spacer(GlanceModifier.height(3.dp))
Image(
provider = ImageProvider(icon),
@@ -72,6 +72,8 @@ fun WeatherWidgetConfig(onDone: (WidgetConfig) -> Unit = {}) {
var widgetTextTheme by remember { mutableStateOf(WidgetTextTheme.AUTO) }
var selectedIconSize by remember { mutableFloatStateOf(1f) }
var showPrecipitationProbability by remember { mutableStateOf(false) }
val widgetThemeOptions =
WidgetTheme.entries.filter { it != WidgetTheme.TRANSPARENT }
.map { DialogOption(it.toString(), stringResource(it.label)) }
@@ -106,7 +108,8 @@ fun WeatherWidgetConfig(onDone: (WidgetConfig) -> Unit = {}) {
selectedFontSize,
selectedIconSize,
widgetTextTheme,
widgetTheme
widgetTheme,
showPrecipitationProbability
)
}
}
@@ -210,6 +213,15 @@ fun WeatherWidgetConfig(onDone: (WidgetConfig) -> Unit = {}) {
widgetTheme = selected
}
),
SettingTile.SwitchTile(
leading = { SettingsTileIcon(R.drawable.rainy_light_24px) },
title = stringResource(R.string.settings_widget_show_precip_probability),
description = stringResource(R.string.settings_widget_show_precip_probability_secondary),
checked = showPrecipitationProbability,
onCheckedChange = {
showPrecipitationProbability = it
}
),
SettingTile.DialogOptionTile(
leading = { SettingsTileIcon(R.drawable.format_paint_24px) },
title = stringResource(R.string.settings_widget_text_color),
@@ -240,7 +252,8 @@ fun WeatherWidgetConfig(onDone: (WidgetConfig) -> Unit = {}) {
fontSize = selectedFontSize,
iconSize = selectedIconSize,
widgetTheme = widgetTheme,
widgetTextTheme = widgetTextTheme
widgetTextTheme = widgetTextTheme,
showPrecipitationProbability = showPrecipitationProbability
)
)
},
@@ -269,6 +282,7 @@ private fun GlanceWidgetPreview(
iconSize: Float,
textTheme: WidgetTextTheme,
widgetTheme: WidgetTheme,
showPrecipitationProbability: Boolean
) {
val textColor = when (textTheme) {
@@ -313,7 +327,8 @@ private fun GlanceWidgetPreview(
widgetColor,
textColor,
textColorSecondary,
hourlyContainerColor
hourlyContainerColor,
showPrecipitationProbability
)
WidgetVariant.COMPACT -> CompactWidgetPreview(
@@ -335,7 +350,8 @@ private fun LargeWidgetPreview(
widgetColor: Color,
textColor: Color,
textColorSecondary: Color,
hourlyContainerColor: Color
hourlyContainerColor: Color,
showPrecipitationProbability: Boolean
) {
@@ -386,6 +402,13 @@ private fun LargeWidgetPreview(
fontWeight = FontWeight.Medium,
color = textColor
)
if (showPrecipitationProbability) {
Text(
"${temp * 2}%",
fontSize = (hourlyTextSize * 0.9).sp,
color = textColorSecondary
)
}
Gap(3.dp)
WeatherIconBox(icon, hourlyIconSize.dp)
Gap(3.dp)
+2
View File
@@ -463,6 +463,8 @@
<string name="unit_precipitation_inch_short">in</string>
<string name="unit_precipitation_cm_short">cm</string>
<string name="settings_widget_show_precip_probability">Show precipitation probability</string>
<string name="settings_widget_show_precip_probability_secondary">Displays the precipitation probability only when the selected source provides it</string>
</resources>
<!--<string name=""></string>-->
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="400dp"
android:minHeight="40dp"
android:minResizeWidth="100dp"
android:minResizeHeight="40dp"
android:resizeMode="horizontal|vertical"
android:widgetCategory="home_screen"
android:configure="com.pranshulgg.weather_master_app.widgets.config.WidgetConfigActivity"
/>