git.iko.soy/opencode-android
on 066cded help sign in
opencode-android/app/src/main/java/soy/iko/opencode/platform/SessionsWidgetProvider.kt
83 lines 3.9 kB history raw
1package soy.iko.opencode.platform
3import android.app.PendingIntent
4import android.appwidget.AppWidgetManager
5import android.appwidget.AppWidgetProvider
6import android.content.ComponentName
7import android.content.Context
8import android.content.Intent
9import android.net.Uri
10import android.os.Build
11import android.widget.RemoteViews
12import soy.iko.opencode.MainActivity
13import soy.iko.opencode.R
15/**
16 * Home-screen widget listing recent opencode sessions (fed by
17 * [soy.iko.opencode.data.repo.RecentSessionsStore]) with a "New session" action. Each row deep-links
18 * into its session; the header opens the app. The list is backed by [SessionsWidgetService].
19 */
20class SessionsWidgetProvider : AppWidgetProvider() {
22 override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray) {
23 for (id in appWidgetIds) render(context, appWidgetManager, id)
24 }
26 companion object {
27 /** Re-render all widgets and tell them to reload their list data (call after the
28 * recent-session list changes). No-op when no widget is placed. */
29 fun refresh(context: Context) {
30 val manager = AppWidgetManager.getInstance(context) ?: return
31 val ids = runCatching {
32 manager.getAppWidgetIds(ComponentName(context, SessionsWidgetProvider::class.java))
33 }.getOrNull() ?: return
34 if (ids.isEmpty()) return
35 for (id in ids) render(context, manager, id)
36 manager.notifyAppWidgetViewDataChanged(ids, R.id.widget_list)
37 }
39 private fun render(context: Context, manager: AppWidgetManager, widgetId: Int) {
40 val views = RemoteViews(context.packageName, R.layout.widget_sessions)
42 // Header: title opens the app; the "+" starts a new session.
43 views.setOnClickPendingIntent(
44 R.id.widget_title,
45 activityPending(context, requestCode = 2, Intent(context, MainActivity::class.java)),
46 )
47 views.setOnClickPendingIntent(
48 R.id.widget_new,
49 activityPending(
50 context, requestCode = 1,
51 Intent(context, MainActivity::class.java).apply { action = MainActivity.ACTION_NEW_SESSION },
52 ),
53 )
55 // Bind the list to the RemoteViewsService. A unique data Uri per widget id keeps
56 // the framework from reusing one adapter across widgets.
57 val serviceIntent = Intent(context, SessionsWidgetService::class.java).apply {
58 putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widgetId)
59 data = Uri.parse(toUri(Intent.URI_INTENT_SCHEME))
60 }
61 views.setRemoteAdapter(R.id.widget_list, serviceIntent)
62 views.setEmptyView(R.id.widget_list, R.id.widget_empty)
64 // Template intent filled in per row (fill-in carries EXTRA_SESSION_ID). Must be
65 // mutable on Android 12+ so the row's fill-in can be merged.
66 val templateIntent = Intent(context, MainActivity::class.java).apply { action = Intent.ACTION_VIEW }
67 val mutableFlag = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) PendingIntent.FLAG_MUTABLE else 0
68 val templatePending = PendingIntent.getActivity(
69 context, 3, templateIntent, PendingIntent.FLAG_UPDATE_CURRENT or mutableFlag,
70 )
71 views.setPendingIntentTemplate(R.id.widget_list, templatePending)
73 manager.updateAppWidget(widgetId, views)
74 manager.notifyAppWidgetViewDataChanged(widgetId, R.id.widget_list)
75 }
77 private fun activityPending(context: Context, requestCode: Int, intent: Intent): PendingIntent =
78 PendingIntent.getActivity(
79 context, requestCode, intent,
80 PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
81 )
82 }