firka: rewrite Android widget and fix refresh

This commit is contained in:
2026-02-28 15:45:04 +01:00
parent 1e7dceb995
commit c58f34f499
6 changed files with 114 additions and 41 deletions

View File

@@ -18,7 +18,7 @@ import androidx.glance.text.FontWeight
import androidx.glance.text.Text
import androidx.glance.text.TextStyle
import app.firka.naplo.model.Colors
import app.firka.naplo.model.Lesson
import app.firka.naplo.glance.WidgetLesson
import java.time.format.DateTimeFormatterBuilder
val hhmm = DateTimeFormatterBuilder()
@@ -26,7 +26,7 @@ val hhmm = DateTimeFormatterBuilder()
.toFormatter()
@Composable
fun LessonCard(lesson: Lesson, colors: Colors,
fun LessonCard(lesson: WidgetLesson, colors: Colors,
modifier: GlanceModifier = GlanceModifier) {
Box(modifier =
modifier
@@ -38,7 +38,7 @@ fun LessonCard(lesson: Lesson, colors: Colors,
var bgColor = colors.a15p
var fgColor = colors.textSecondary
if (lesson.substituteTeacher == null) {
if (lesson.substituteTeacher != null) {
bgColor = colors.warning15p
fgColor = colors.warningText
}

View File

@@ -26,7 +26,7 @@ import androidx.glance.text.FontWeight
import androidx.glance.text.Text
import androidx.glance.text.TextStyle
import app.firka.naplo.model.Colors
import app.firka.naplo.model.Lesson
import app.firka.naplo.glance.WidgetLesson
import org.json.JSONObject
import java.io.File
import java.time.LocalDate
@@ -73,17 +73,28 @@ class TimetableWidget : GlanceAppWidget() {
val colors = Colors(widgetState)
val tt = widgetState.getJSONArray("timetable")
var lessons = mutableListOf<Lesson>()
var lessons = mutableListOf<WidgetLesson>()
for (i in 0..<tt.length()) {
lessons.add(Lesson(tt.getJSONObject(i)))
lessons.add(WidgetLesson(tt.getJSONObject(i)))
}
val now = LocalDate.now()
val start = LocalDateTime.of(now.year, now.month, now.dayOfMonth, 0, 0)
val displayDateStr = widgetState.optString("displayDate", "")
val targetDate = if (displayDateStr.isNotEmpty()) {
try {
LocalDate.parse(displayDateStr)
} catch (_: Exception) {
LocalDate.now()
}
} else {
LocalDate.now()
}
val start = LocalDateTime.of(targetDate.year, targetDate.month, targetDate.dayOfMonth, 0, 0)
val end = start.plusHours(23)
lessons = lessons.filter { lesson -> lesson.start.isAfter(start) && lesson.end.isBefore(end) }.toMutableList()
val headerText = if (displayDateStr.isNotEmpty()) displayDateStr else "Mai órarend"
Box(modifier =
GlanceModifier
.background(colors.background)
@@ -92,7 +103,7 @@ class TimetableWidget : GlanceAppWidget() {
) {
Column {
Text(
"Mai órarend",
headerText,
style = TextStyle(
color = ColorProvider(colors.textSecondary, colors.textSecondary),
fontSize = 12.sp,

View File

@@ -0,0 +1,23 @@
package app.firka.naplo.glance
import app.firka.naplo.getIntOrNull
import app.firka.naplo.getStringOrNull
import org.json.JSONObject
import java.time.LocalDateTime
import java.time.format.DateTimeFormatterBuilder
class WidgetLesson(data: JSONObject) {
val formatter = DateTimeFormatterBuilder()
.appendPattern("yyyy-MM-dd'T'HH:mm:ss.SSS")
.optionalStart()
.appendLiteral('Z')
.optionalEnd()
.toFormatter()
val name: String = data.getString("Nev")
val start: LocalDateTime = LocalDateTime.parse(data.getString("KezdetIdopont"), formatter)
val end: LocalDateTime = LocalDateTime.parse(data.getString("VegIdopont"), formatter)
val lessonNumber: Int? = data.getIntOrNull("Oraszam")
val roomName: String? = data.getStringOrNull("TeremNeve")
val substituteTeacher: String? = data.getStringOrNull("HelyettesTanarNeve")
}

View File

@@ -3,5 +3,5 @@
android:minWidth="300dp"
android:minHeight="100dp"
android:resizeMode="horizontal|vertical"
android:updatePeriodMillis="10000">
android:updatePeriodMillis="1800000">
</appwidget-provider>

View File

@@ -22,37 +22,56 @@ class WidgetCacheHelper {
timetableJson.add(lesson.toJson());
}
return {'colors': _colorsMap(style), 'timetable': timetableJson};
}
static Map<String, dynamic> toAndroidWidgetJson(
FirkaStyle style,
List<Lesson> timetable,
) {
final timetableJson = <Map<String, dynamic>>[];
for (var lesson in timetable) {
timetableJson.add({
'Nev': lesson.name,
'KezdetIdopont': lesson.start.toUtc().toIso8601String(),
'VegIdopont': lesson.end.toUtc().toIso8601String(),
'Oraszam': lesson.lessonNumber,
'TeremNeve': lesson.roomName,
'HelyettesTanarNeve': lesson.substituteTeacher,
});
}
return {'colors': _colorsMap(style), 'timetable': timetableJson};
}
static Map<String, dynamic> _colorsMap(FirkaStyle style) {
return {
'colors': {
'background': style.colors.background.toARGB32(),
'backgroundAmoled': style.colors.backgroundAmoled.toARGB32(),
'background0p': style.colors.background0p.toARGB32(),
'success': style.colors.success.toARGB32(),
'textPrimary': style.colors.textPrimary.toARGB32(),
'textSecondary': style.colors.textSecondary.toARGB32(),
'textTertiary': style.colors.textTertiary.toARGB32(),
'card': style.colors.card.toARGB32(),
'cardTranslucent': style.colors.cardTranslucent.toARGB32(),
'buttonSecondaryFill': style.colors.buttonSecondaryFill.toARGB32(),
'accent': style.colors.accent.toARGB32(),
'secondary': style.colors.secondary.toARGB32(),
'shadowColor': style.colors.shadowColor.toARGB32(),
'a15p': style.colors.a15p.toARGB32(),
'warningAccent': style.colors.warningAccent.toARGB32(),
'warningText': style.colors.warningText.toARGB32(),
'warning15p': style.colors.warning15p.toARGB32(),
'warningCard': style.colors.warningCard.toARGB32(),
'errorAccent': style.colors.errorAccent.toARGB32(),
'errorText': style.colors.errorText.toARGB32(),
'error15p': style.colors.error15p.toARGB32(),
'errorCard': style.colors.errorCard.toARGB32(),
'grade5': style.colors.grade5.toARGB32(),
'grade4': style.colors.grade4.toARGB32(),
'grade3': style.colors.grade3.toARGB32(),
'grade2': style.colors.grade2.toARGB32(),
'grade1': style.colors.grade1.toARGB32(),
},
'timetable': timetableJson,
'background': style.colors.background.toARGB32(),
'backgroundAmoled': style.colors.backgroundAmoled.toARGB32(),
'background0p': style.colors.background0p.toARGB32(),
'success': style.colors.success.toARGB32(),
'textPrimary': style.colors.textPrimary.toARGB32(),
'textSecondary': style.colors.textSecondary.toARGB32(),
'textTertiary': style.colors.textTertiary.toARGB32(),
'card': style.colors.card.toARGB32(),
'cardTranslucent': style.colors.cardTranslucent.toARGB32(),
'buttonSecondaryFill': style.colors.buttonSecondaryFill.toARGB32(),
'accent': style.colors.accent.toARGB32(),
'secondary': style.colors.secondary.toARGB32(),
'shadowColor': style.colors.shadowColor.toARGB32(),
'a15p': style.colors.a15p.toARGB32(),
'warningAccent': style.colors.warningAccent.toARGB32(),
'warningText': style.colors.warningText.toARGB32(),
'warning15p': style.colors.warning15p.toARGB32(),
'warningCard': style.colors.warningCard.toARGB32(),
'errorAccent': style.colors.errorAccent.toARGB32(),
'errorText': style.colors.errorText.toARGB32(),
'error15p': style.colors.error15p.toARGB32(),
'errorCard': style.colors.errorCard.toARGB32(),
'grade5': style.colors.grade5.toARGB32(),
'grade4': style.colors.grade4.toARGB32(),
'grade3': style.colors.grade3.toARGB32(),
'grade2': style.colors.grade2.toARGB32(),
'grade1': style.colors.grade1.toARGB32(),
};
}
@@ -82,6 +101,25 @@ class WidgetCacheHelper {
}
}
static Future<void> generateWidgetStateForDate(
DateTime date,
FirkaStyle style,
KretaClient client,
) async {
final dataDir = await getApplicationDocumentsDirectory();
final dayStart = DateTime(date.year, date.month, date.day);
final dayEnd = dayStart.add(Duration(hours: 23, minutes: 59));
final lessons = await client.getTimeTable(dayStart, dayEnd, forceCache: false);
final dayLessons = lessons.response ?? [];
final json = toJson(style, dayLessons);
json['displayDate'] =
'${date.year.toString().padLeft(4, '0')}-${date.month.toString().padLeft(2, '0')}-${date.day.toString().padLeft(2, '0')}';
final widgetFile = File(p.join(dataDir.path, "widget_state.json"));
await widgetFile.writeAsString(jsonEncode(json));
}
static Future<void> updateIOSWidgets({
required String locale,
required String theme,

View File

@@ -198,7 +198,8 @@ class _HomeScreenState extends FirkaState<HomeScreen>
if (Platform.isAndroid) {
await WidgetCacheHelper.updateWidgetCache(appStyle, initData.client);
await HomeWidget.updateWidget(
qualifiedAndroidName: "app.firka.naplo.glance.TimetableWidget",
qualifiedAndroidName:
"app.firka.naplo.glance.TimetableWidgetReceiver",
);
}