From e6b938802da705c2c66f4ec993daa802699528a4 Mon Sep 17 00:00:00 2001 From: Szymon Chmal Date: Wed, 24 Jun 2026 11:54:57 +0200 Subject: [PATCH 1/5] feat(android): add widget instance configuration Add per-instance Android widget configuration support, generated configuration trampolines, and example app wiring for configurable widgets. --- example/app.json | 1 + example/app/voltraui/[activityName].tsx | 93 +++++++++- .../android/AndroidWidgetPinScreen.tsx | 161 +++++++++++++++--- .../src/main/java/voltra/VoltraModule.kt | 85 +++++++++ .../runtime/VoltraConfigurationStore.kt | 63 ++++++- .../voltra/widget/VoltraClientGlanceWidget.kt | 4 +- .../voltra/widget/VoltraWidgetReceiver.kt | 15 ++ .../expo-plugin/src/android/files/index.ts | 28 ++- .../src/android/files/kotlin.node.test.ts | 133 +++++++++++++++ .../expo-plugin/src/android/files/kotlin.ts | 144 ++++++++++++++++ .../src/android/files/xml.node.test.ts | 73 +++++++- .../expo-plugin/src/android/files/xml.ts | 26 ++- .../expo-plugin/src/android/index.ts | 15 +- .../src/android/manifest.node.test.ts | 58 +++++++ .../expo-plugin/src/android/manifest.ts | 147 ++++++++++------ .../android-client/expo-plugin/src/index.ts | 30 ++++ .../android-client/expo-plugin/src/types.ts | 11 ++ packages/android-client/src/index.ts | 3 + .../src/native/NativeVoltraAndroid.ts | 3 + packages/android-client/src/widgets/api.ts | 30 +++- 20 files changed, 1029 insertions(+), 94 deletions(-) create mode 100644 packages/android-client/expo-plugin/src/android/files/kotlin.node.test.ts create mode 100644 packages/android-client/expo-plugin/src/android/manifest.node.test.ts diff --git a/example/app.json b/example/app.json index 445654c6..2f3051a1 100644 --- a/example/app.json +++ b/example/app.json @@ -103,6 +103,7 @@ "@use-voltra/android-client", { "enableNotifications": true, + "widgetConfigurationRoute": "voltraui/android-widget-config", "widgets": [ { "id": "voltra", diff --git a/example/app/voltraui/[activityName].tsx b/example/app/voltraui/[activityName].tsx index 6531ec73..5feb48e5 100644 --- a/example/app/voltraui/[activityName].tsx +++ b/example/app/voltraui/[activityName].tsx @@ -1,13 +1,49 @@ import { useLocalSearchParams, useRouter } from 'expo-router' -import { StyleSheet, Text, View } from 'react-native' +import { useState } from 'react' +import { Alert, StyleSheet, Text, TextInput, View } from 'react-native' +import { + cancelWidgetConfiguration, + completeWidgetConfiguration, + setWidgetInstanceConfiguration, +} from '@use-voltra/android-client' import { Button } from '~/components/Button' export default function DeepLinkIndexScreen() { - const { activityName } = useLocalSearchParams<{ activityName: string }>() + const { activityName, appWidgetId, widgetId } = useLocalSearchParams<{ + activityName: string + appWidgetId?: string + widgetId?: string + }>() const router = useRouter() + const isWidgetConfig = activityName === 'android-widget-config' + const parsedAppWidgetId = typeof appWidgetId === 'string' ? Number(appWidgetId) : Number.NaN + const [label, setLabel] = useState('Hello') + const [saving, setSaving] = useState(false) + + const saveWidgetConfiguration = async () => { + if (!Number.isFinite(parsedAppWidgetId)) { + Alert.alert('Missing widget id', 'The configuration activity did not receive a valid appWidgetId.') + return + } + + setSaving(true) + try { + await setWidgetInstanceConfiguration(parsedAppWidgetId, 'label', label) + await completeWidgetConfiguration(parsedAppWidgetId) + } catch (error: any) { + Alert.alert('Error', error?.message || String(error)) + } finally { + setSaving(false) + } + } const goBack = () => { + if (isWidgetConfig) { + cancelWidgetConfiguration().catch(() => {}) + return + } + if (router.canGoBack()) { router.back() } else { @@ -15,6 +51,34 @@ export default function DeepLinkIndexScreen() { } } + if (isWidgetConfig) { + return ( + + + Configure widget instance + Widget type: {widgetId} + Instance: #{appWidgetId} + + + env.configuration.label + + + + +