forked from firka/firka
firka(android): widget refresh via broadcast + updateAll, Glance IO off main thread
This commit is contained in:
@@ -1,12 +1,15 @@
|
||||
package app.firka.naplo
|
||||
|
||||
import android.appwidget.AppWidgetManager
|
||||
import android.content.ComponentName
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import androidx.glance.appwidget.updateAll
|
||||
import app.firka.naplo.glance.TimetableWidget
|
||||
import app.firka.naplo.glance.TimetableWidgetReceiver
|
||||
import io.flutter.embedding.android.FlutterActivity
|
||||
import io.flutter.embedding.engine.FlutterEngine
|
||||
import io.flutter.plugin.common.MethodChannel
|
||||
@@ -107,7 +110,19 @@ class MainActivity : FlutterActivity() {
|
||||
"refreshTimetableWidget" -> {
|
||||
CoroutineScope(SupervisorJob() + Dispatchers.Default).launch {
|
||||
try {
|
||||
TimetableWidget().updateAll(context.applicationContext)
|
||||
val appContext = context.applicationContext
|
||||
val appWidgetManager = AppWidgetManager.getInstance(appContext)
|
||||
val componentName = ComponentName(appContext, TimetableWidgetReceiver::class.java)
|
||||
val ids = appWidgetManager.getAppWidgetIds(componentName)
|
||||
if (ids.isNotEmpty()) {
|
||||
val intent = Intent(appContext, TimetableWidgetReceiver::class.java).apply {
|
||||
action = AppWidgetManager.ACTION_APPWIDGET_UPDATE
|
||||
putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, ids)
|
||||
addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
|
||||
}
|
||||
appContext.sendBroadcast(intent)
|
||||
}
|
||||
TimetableWidget().updateAll(appContext)
|
||||
result.success(true)
|
||||
} catch (e: Exception) {
|
||||
result.error("refresh_failed", e.message, null)
|
||||
|
||||
@@ -27,6 +27,8 @@ import androidx.glance.text.Text
|
||||
import androidx.glance.text.TextStyle
|
||||
import app.firka.naplo.model.Colors
|
||||
import app.firka.naplo.glance.WidgetLesson
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.json.JSONObject
|
||||
import java.io.File
|
||||
import java.time.LocalDate
|
||||
@@ -38,19 +40,47 @@ class TimetableWidget : GlanceAppWidget() {
|
||||
get() = HomeWidgetGlanceStateDefinition()
|
||||
|
||||
override suspend fun provideGlance(context: Context, id: GlanceId) {
|
||||
val data = withContext(Dispatchers.IO) {
|
||||
loadWidgetData(context)
|
||||
}
|
||||
provideContent {
|
||||
GlanceContent(context, currentState())
|
||||
GlanceContent(context, currentState(), data)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun GlanceContent(context: Context, currentState: HomeWidgetGlanceState) {
|
||||
private fun loadWidgetData(context: Context): WidgetData? {
|
||||
val appFlutter = File(context.applicationContext.dataDir, "app_flutter")
|
||||
val widgetStateFile = File(appFlutter, "widget_state.json")
|
||||
if (!widgetStateFile.exists()) return null
|
||||
val widgetState = JSONObject(widgetStateFile.readText(Charsets.UTF_8))
|
||||
val colors = Colors(widgetState)
|
||||
val tt = widgetState.getJSONArray("timetable")
|
||||
val lessons = mutableListOf<WidgetLesson>()
|
||||
for (i in 0..<tt.length()) {
|
||||
lessons.add(WidgetLesson(tt.getJSONObject(i)))
|
||||
}
|
||||
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)
|
||||
val filtered = lessons.filter { it.start.isAfter(start) && it.end.isBefore(end) }
|
||||
val headerText = if (displayDateStr.isNotEmpty()) displayDateStr else "Mai órarend"
|
||||
return WidgetData(colors, headerText, filtered.take(4))
|
||||
}
|
||||
|
||||
if (!widgetStateFile.exists()) {
|
||||
Box(modifier =
|
||||
GlanceModifier
|
||||
@Composable
|
||||
private fun GlanceContent(context: Context, currentState: HomeWidgetGlanceState, data: WidgetData?) {
|
||||
if (data == null) {
|
||||
Box(
|
||||
modifier = GlanceModifier
|
||||
.background(Color(0xFFFAFFF0))
|
||||
.padding(16.dp)
|
||||
.fillMaxSize(),
|
||||
@@ -65,58 +95,36 @@ class TimetableWidget : GlanceAppWidget() {
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
val widgetState = JSONObject(widgetStateFile.readText(Charsets.UTF_8))
|
||||
val colors = Colors(widgetState)
|
||||
|
||||
val tt = widgetState.getJSONArray("timetable")
|
||||
var lessons = mutableListOf<WidgetLesson>()
|
||||
|
||||
for (i in 0..<tt.length()) {
|
||||
lessons.add(WidgetLesson(tt.getJSONObject(i)))
|
||||
}
|
||||
|
||||
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)
|
||||
Box(
|
||||
modifier = GlanceModifier
|
||||
.background(data.colors.background)
|
||||
.padding(16.dp)
|
||||
.fillMaxSize()
|
||||
) {
|
||||
Column {
|
||||
Text(
|
||||
headerText,
|
||||
data.headerText,
|
||||
style = TextStyle(
|
||||
color = ColorProvider(colors.textSecondary, colors.textSecondary),
|
||||
color = ColorProvider(data.colors.textSecondary, data.colors.textSecondary),
|
||||
fontSize = 12.sp,
|
||||
fontWeight = FontWeight.Medium
|
||||
)
|
||||
)
|
||||
Spacer(modifier = GlanceModifier.height(4.dp))
|
||||
for (lesson in lessons) {
|
||||
LessonCard(lesson, colors)
|
||||
for (lesson in data.lessons) {
|
||||
LessonCard(lesson, data.colors)
|
||||
Spacer(modifier = GlanceModifier.height(4.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
private data class WidgetData(
|
||||
val colors: Colors,
|
||||
val headerText: String,
|
||||
val lessons: List<WidgetLesson>,
|
||||
)
|
||||
@@ -19,6 +19,7 @@ import 'package:firka/data/widget.dart';
|
||||
import 'package:firka/ui/shared/firka_icon.dart';
|
||||
import 'package:firka/ui/theme/style.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:home_widget/home_widget.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
|
||||
@@ -148,6 +149,10 @@ class _DebugScreen extends FirkaState<DebugScreen> {
|
||||
widget.data.client,
|
||||
);
|
||||
if (Platform.isAndroid) {
|
||||
await HomeWidget.updateWidget(
|
||||
qualifiedAndroidName:
|
||||
'app.firka.naplo.glance.TimetableWidgetReceiver',
|
||||
);
|
||||
await const MethodChannel(
|
||||
'firka.app/main',
|
||||
).invokeMethod<void>('refreshTimetableWidget');
|
||||
|
||||
Reference in New Issue
Block a user