@capgo/capacitor-pretty-toast 8.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/CapgoCapacitorPrettyToast.podspec +17 -0
  2. package/LICENSE +373 -0
  3. package/Package.swift +28 -0
  4. package/README.md +341 -0
  5. package/android/build.gradle +71 -0
  6. package/android/src/main/AndroidManifest.xml +6 -0
  7. package/android/src/main/java/com/toast/PrettyToastPlugin.kt +197 -0
  8. package/android/src/main/java/com/toast/ToastOverlay.kt +495 -0
  9. package/android/src/main/java/com/toast/anim/CutoutMorphAnimator.kt +235 -0
  10. package/android/src/main/java/com/toast/anim/SlideAnimator.kt +64 -0
  11. package/android/src/main/java/com/toast/anim/ToastAnimator.kt +23 -0
  12. package/android/src/main/java/com/toast/backdrop/BackdropSampler.kt +142 -0
  13. package/android/src/main/java/com/toast/backdrop/OutlineController.kt +100 -0
  14. package/android/src/main/java/com/toast/cutout/CutoutDetector.kt +88 -0
  15. package/android/src/main/java/com/toast/cutout/CutoutInfo.kt +28 -0
  16. package/android/src/main/java/com/toast/gesture/ToastGestureHandler.kt +68 -0
  17. package/android/src/main/java/com/toast/ui/IconMapper.kt +26 -0
  18. package/android/src/main/java/com/toast/ui/PassThroughFrameLayout.kt +53 -0
  19. package/android/src/main/java/com/toast/ui/ToastViewFactory.kt +224 -0
  20. package/android/src/main/java/com/toast/util/Density.kt +17 -0
  21. package/android/src/main/java/com/toast/util/StatusBarController.kt +24 -0
  22. package/android/src/main/java/com/toast/util/ToastConstants.kt +36 -0
  23. package/android/src/main/res/.gitkeep +0 -0
  24. package/android/src/main/res/drawable/ic_arrow_downward.xml +9 -0
  25. package/android/src/main/res/drawable/ic_arrow_upward.xml +9 -0
  26. package/android/src/main/res/drawable/ic_cancel.xml +9 -0
  27. package/android/src/main/res/drawable/ic_check_circle.xml +9 -0
  28. package/android/src/main/res/drawable/ic_favorite.xml +9 -0
  29. package/android/src/main/res/drawable/ic_info.xml +9 -0
  30. package/android/src/main/res/drawable/ic_mail.xml +9 -0
  31. package/android/src/main/res/drawable/ic_notifications.xml +9 -0
  32. package/android/src/main/res/drawable/ic_touch_app.xml +9 -0
  33. package/android/src/main/res/drawable/ic_warning.xml +9 -0
  34. package/android/src/main/res/drawable/ic_wifi.xml +9 -0
  35. package/android/src/main/res/values/colors.xml +3 -0
  36. package/android/src/main/res/values/strings.xml +3 -0
  37. package/android/src/main/res/values/styles.xml +3 -0
  38. package/android/src/test/java/com/toast/PrettyToastPluginTest.kt +26 -0
  39. package/dist/docs.json +459 -0
  40. package/dist/esm/controller.d.ts +30 -0
  41. package/dist/esm/controller.js +271 -0
  42. package/dist/esm/controller.js.map +1 -0
  43. package/dist/esm/definitions.d.ts +144 -0
  44. package/dist/esm/definitions.js +2 -0
  45. package/dist/esm/definitions.js.map +1 -0
  46. package/dist/esm/driver.d.ts +19 -0
  47. package/dist/esm/driver.js +24 -0
  48. package/dist/esm/driver.js.map +1 -0
  49. package/dist/esm/icons.d.ts +14 -0
  50. package/dist/esm/icons.js +138 -0
  51. package/dist/esm/icons.js.map +1 -0
  52. package/dist/esm/index.d.ts +2 -0
  53. package/dist/esm/index.js +2 -0
  54. package/dist/esm/index.js.map +1 -0
  55. package/dist/esm/internal-plugin.d.ts +2 -0
  56. package/dist/esm/internal-plugin.js +5 -0
  57. package/dist/esm/internal-plugin.js.map +1 -0
  58. package/dist/esm/internal-types.d.ts +31 -0
  59. package/dist/esm/internal-types.js +2 -0
  60. package/dist/esm/internal-types.js.map +1 -0
  61. package/dist/esm/toast.d.ts +1 -0
  62. package/dist/esm/toast.js +5 -0
  63. package/dist/esm/toast.js.map +1 -0
  64. package/dist/esm/web-renderer.d.ts +36 -0
  65. package/dist/esm/web-renderer.js +296 -0
  66. package/dist/esm/web-renderer.js.map +1 -0
  67. package/dist/esm/web.d.ts +10 -0
  68. package/dist/esm/web.js +28 -0
  69. package/dist/esm/web.js.map +1 -0
  70. package/dist/plugin.cjs.js +770 -0
  71. package/dist/plugin.cjs.js.map +1 -0
  72. package/dist/plugin.js +773 -0
  73. package/dist/plugin.js.map +1 -0
  74. package/ios/Sources/PrettyToastPlugin/CustomHostingView.swift +13 -0
  75. package/ios/Sources/PrettyToastPlugin/PassThroughWindow.swift +143 -0
  76. package/ios/Sources/PrettyToastPlugin/PrettyToastColorParser.swift +94 -0
  77. package/ios/Sources/PrettyToastPlugin/PrettyToastPlugin.swift +138 -0
  78. package/ios/Sources/PrettyToastPlugin/PrettyToastView.swift +267 -0
  79. package/ios/Sources/PrettyToastPlugin/Toast.swift +29 -0
  80. package/ios/Sources/PrettyToastPlugin/ToastManager.swift +392 -0
  81. package/ios/Tests/PrettyToastPluginTests/PrettyToastPluginTests.swift +21 -0
  82. package/package.json +98 -0
  83. package/scripts/check-capacitor-plugin-wiring.mjs +254 -0
  84. package/scripts/deploy-example-capgo.mjs +86 -0
  85. package/scripts/test-ios.sh +14 -0
package/README.md ADDED
@@ -0,0 +1,341 @@
1
+ # @capgo/capacitor-pretty-toast
2
+
3
+ Native-first pretty toast notifications for Capacitor and the web.
4
+
5
+ capacitor-pretty-toast.mov
6
+
7
+ ![Pretty Toast Hero](./media/pretty-toast-hero.png)
8
+
9
+ This package keeps the familiar `toast.*` surface from `react-native-pretty-toast`, but ships as a Capacitor plugin with:
10
+
11
+ - native overlays on iOS and Android.
12
+ - a DOM renderer on web.
13
+ - queueing, `force`, `update`, `dismiss`, `dismissAll`, and `promise`
14
+ - symbol icons, raw SVG through `icon`, and URI-based images through `iconSource`
15
+
16
+ ## Install
17
+
18
+ ```bash
19
+ bun add @capgo/capacitor-pretty-toast
20
+ ```
21
+
22
+ Then sync native platforms:
23
+
24
+ ```bash
25
+ bunx cap sync
26
+ ```
27
+
28
+ ## Usage
29
+
30
+ ```ts
31
+ import { toast } from '@capgo/capacitor-pretty-toast';
32
+
33
+ toast.success('Saved', {
34
+ message: 'Your changes are already on disk.',
35
+ });
36
+
37
+ const id = toast.loading('Uploading', {
38
+ message: 'This toast stays visible until you update it.',
39
+ });
40
+
41
+ setTimeout(() => {
42
+ toast.update(id, {
43
+ title: 'Upload complete',
44
+ message: 'Updated in place without replaying the enter animation.',
45
+ icon: 'checkmark.circle.fill',
46
+ autoDismiss: true,
47
+ });
48
+ }, 1500);
49
+ ```
50
+
51
+ ## API
52
+
53
+ <docgen-index>
54
+
55
+ * [`show(...)`](#show)
56
+ * [`success(...)`](#success)
57
+ * [`error(...)`](#error)
58
+ * [`info(...)`](#info)
59
+ * [`warning(...)`](#warning)
60
+ * [`loading(...)`](#loading)
61
+ * [`update(...)`](#update)
62
+ * [`promise(...)`](#promise)
63
+ * [`dismiss(...)`](#dismiss)
64
+ * [`dismissAll()`](#dismissall)
65
+ * [Interfaces](#interfaces)
66
+ * [Type Aliases](#type-aliases)
67
+
68
+ </docgen-index>
69
+
70
+ <docgen-api>
71
+ <!--Update the source file JSDoc comments and rerun docgen to update the docs below-->
72
+
73
+ Public toast controller exposed as `toast`.
74
+
75
+ ### show(...)
76
+
77
+ ```typescript
78
+ show(config: ToastConfig, options?: ShowOptions | undefined) => string
79
+ ```
80
+
81
+ Show a custom toast and return its id.
82
+
83
+ | Param | Type |
84
+ | ------------- | --------------------------------------------------- |
85
+ | **`config`** | <code><a href="#toastconfig">ToastConfig</a></code> |
86
+ | **`options`** | <code><a href="#showoptions">ShowOptions</a></code> |
87
+
88
+ **Returns:** <code>string</code>
89
+
90
+ --------------------
91
+
92
+
93
+ ### success(...)
94
+
95
+ ```typescript
96
+ success(title: string, config?: ToastConfig | undefined, options?: ShowOptions | undefined) => string
97
+ ```
98
+
99
+ Show a success toast.
100
+
101
+ | Param | Type |
102
+ | ------------- | --------------------------------------------------- |
103
+ | **`title`** | <code>string</code> |
104
+ | **`config`** | <code><a href="#toastconfig">ToastConfig</a></code> |
105
+ | **`options`** | <code><a href="#showoptions">ShowOptions</a></code> |
106
+
107
+ **Returns:** <code>string</code>
108
+
109
+ --------------------
110
+
111
+
112
+ ### error(...)
113
+
114
+ ```typescript
115
+ error(title: string, config?: ToastConfig | undefined, options?: ShowOptions | undefined) => string
116
+ ```
117
+
118
+ Show an error toast.
119
+
120
+ | Param | Type |
121
+ | ------------- | --------------------------------------------------- |
122
+ | **`title`** | <code>string</code> |
123
+ | **`config`** | <code><a href="#toastconfig">ToastConfig</a></code> |
124
+ | **`options`** | <code><a href="#showoptions">ShowOptions</a></code> |
125
+
126
+ **Returns:** <code>string</code>
127
+
128
+ --------------------
129
+
130
+
131
+ ### info(...)
132
+
133
+ ```typescript
134
+ info(title: string, config?: ToastConfig | undefined, options?: ShowOptions | undefined) => string
135
+ ```
136
+
137
+ Show an informational toast.
138
+
139
+ | Param | Type |
140
+ | ------------- | --------------------------------------------------- |
141
+ | **`title`** | <code>string</code> |
142
+ | **`config`** | <code><a href="#toastconfig">ToastConfig</a></code> |
143
+ | **`options`** | <code><a href="#showoptions">ShowOptions</a></code> |
144
+
145
+ **Returns:** <code>string</code>
146
+
147
+ --------------------
148
+
149
+
150
+ ### warning(...)
151
+
152
+ ```typescript
153
+ warning(title: string, config?: ToastConfig | undefined, options?: ShowOptions | undefined) => string
154
+ ```
155
+
156
+ Show a warning toast.
157
+
158
+ | Param | Type |
159
+ | ------------- | --------------------------------------------------- |
160
+ | **`title`** | <code>string</code> |
161
+ | **`config`** | <code><a href="#toastconfig">ToastConfig</a></code> |
162
+ | **`options`** | <code><a href="#showoptions">ShowOptions</a></code> |
163
+
164
+ **Returns:** <code>string</code>
165
+
166
+ --------------------
167
+
168
+
169
+ ### loading(...)
170
+
171
+ ```typescript
172
+ loading(title: string, config?: ToastConfig | undefined, options?: ShowOptions | undefined) => string
173
+ ```
174
+
175
+ Show a loading toast. Loading toasts do not auto-dismiss by default.
176
+
177
+ | Param | Type |
178
+ | ------------- | --------------------------------------------------- |
179
+ | **`title`** | <code>string</code> |
180
+ | **`config`** | <code><a href="#toastconfig">ToastConfig</a></code> |
181
+ | **`options`** | <code><a href="#showoptions">ShowOptions</a></code> |
182
+
183
+ **Returns:** <code>string</code>
184
+
185
+ --------------------
186
+
187
+
188
+ ### update(...)
189
+
190
+ ```typescript
191
+ update(id: string, partial: ToastConfig) => void
192
+ ```
193
+
194
+ Update an existing toast by id.
195
+
196
+ | Param | Type |
197
+ | ------------- | --------------------------------------------------- |
198
+ | **`id`** | <code>string</code> |
199
+ | **`partial`** | <code><a href="#toastconfig">ToastConfig</a></code> |
200
+
201
+ --------------------
202
+
203
+
204
+ ### promise(...)
205
+
206
+ ```typescript
207
+ promise<T>(promise: Promise<T>, messages: PromiseMessages<T>) => Promise<T>
208
+ ```
209
+
210
+ Show a loading toast while a promise is pending, then update it for success or error.
211
+
212
+ | Param | Type |
213
+ | -------------- | -------------------------------------------------------------------- |
214
+ | **`promise`** | <code>Promise&lt;T&gt;</code> |
215
+ | **`messages`** | <code><a href="#promisemessages">PromiseMessages</a>&lt;T&gt;</code> |
216
+
217
+ **Returns:** <code>Promise&lt;T&gt;</code>
218
+
219
+ --------------------
220
+
221
+
222
+ ### dismiss(...)
223
+
224
+ ```typescript
225
+ dismiss(id?: string | undefined) => void
226
+ ```
227
+
228
+ Dismiss one toast by id, or the current toast when no id is provided.
229
+
230
+ | Param | Type |
231
+ | -------- | ------------------- |
232
+ | **`id`** | <code>string</code> |
233
+
234
+ --------------------
235
+
236
+
237
+ ### dismissAll()
238
+
239
+ ```typescript
240
+ dismissAll() => void
241
+ ```
242
+
243
+ Dismiss the current toast and clear the queue.
244
+
245
+ --------------------
246
+
247
+
248
+ ### Interfaces
249
+
250
+
251
+ #### ToastConfig
252
+
253
+ | Prop | Type | Description |
254
+ | ------------------------------- | --------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
255
+ | **`id`** | <code>string</code> | Optional stable toast id. A generated id is returned when omitted. |
256
+ | **`icon`** | <code>string</code> | Accepts either an SF-symbol-like identifier or raw SVG markup. SVG mode is enabled only when the string starts with `&lt;svg` after trim. |
257
+ | **`iconSource`** | <code><a href="#iconsource">IconSource</a></code> | URI-like image source. Supports `https://`, `http://`, `file://`, absolute file paths, `data:` URLs, `blob:` URLs, or `{ uri }`. `iconSource` always wins over `icon`. |
258
+ | **`title`** | <code>string</code> | Main toast title. |
259
+ | **`message`** | <code>string</code> | Secondary toast message. |
260
+ | **`duration`** | <code>number</code> | Auto-dismiss delay in milliseconds. |
261
+ | **`autoDismiss`** | <code>boolean</code> | Whether the toast dismisses itself after `duration`. |
262
+ | **`enableSwipeDismiss`** | <code>boolean</code> | Whether swipe-to-dismiss is enabled on native overlays. |
263
+ | **`accentColor`** | <code>string</code> | CSS-style accent color used by native/web renderers. |
264
+ | **`strokeColor`** | <code>string</code> | CSS-style border/stroke color. |
265
+ | **`disableBackdropSampling`** | <code>boolean</code> | Disable Android/iOS backdrop sampling behind the toast. |
266
+ | **`action`** | <code><a href="#toastaction">ToastAction</a></code> | Optional action button configuration. |
267
+ | **`accessibilityAnnouncement`** | <code>string</code> | Text announced to assistive technologies when the toast is shown. |
268
+ | **`onPress`** | <code>(() =&gt; void)</code> | Called when the toast body is pressed. |
269
+ | **`onShow`** | <code>(() =&gt; void)</code> | Called when the toast becomes visible. |
270
+ | **`onHide`** | <code>(() =&gt; void)</code> | Called when the toast is dismissed. |
271
+ | **`onAutoDismiss`** | <code>(() =&gt; void)</code> | Called when the toast is dismissed by its timer. |
272
+
273
+
274
+ #### ToastAction
275
+
276
+ | Prop | Type | Description |
277
+ | ------------- | -------------------------- | -------------------------------------------- |
278
+ | **`label`** | <code>string</code> | Text shown for the native/web action button. |
279
+ | **`onPress`** | <code>() =&gt; void</code> | Called when the action button is pressed. |
280
+
281
+
282
+ #### ShowOptions
283
+
284
+ | Prop | Type | Description |
285
+ | ----------- | -------------------- | -------------------------------------------------------- |
286
+ | **`force`** | <code>boolean</code> | Dismiss the current toast and show this one immediately. |
287
+
288
+
289
+ ### Type Aliases
290
+
291
+
292
+ #### IconSource
293
+
294
+ <code>string | { uri: string }</code>
295
+
296
+
297
+ #### PromiseMessages
298
+
299
+ <code>{ loading: string | <a href="#toastconfig">ToastConfig</a>; success: string | ((value: T) =&gt; string | <a href="#toastconfig">ToastConfig</a>); error: string | ((error: unknown) =&gt; string | <a href="#toastconfig">ToastConfig</a>); }</code>
300
+
301
+ </docgen-api>
302
+
303
+ Notes:
304
+
305
+ - Raw SVG is accepted only through `icon`.
306
+ - `iconSource` always takes precedence over `icon`.
307
+ - `toast.loading()` defaults `autoDismiss` to `false`.
308
+
309
+ ## Example App
310
+
311
+ The repo includes [`example-app/`](./example-app), a Vite-based Capacitor app with demos for:
312
+
313
+ - every `toast.*` method
314
+ - queueing and `force`
315
+ - live `update`
316
+ - `dismiss` and `dismissAll`
317
+ - `promise`
318
+ - symbol icons
319
+ - raw SVG icons
320
+ - remote and data-URL `iconSource` values
321
+ - repeatable capture modes with `?demo=hero`, `?demo=flow`, and `?demo=update`
322
+
323
+ `?demo=flow` is the promo enter/exit morph used in the README video.
324
+ The shipped video shows both the Android cutout path and the centered island-style path side by side.
325
+
326
+ Run it locally:
327
+
328
+ ```bash
329
+ cd example-app
330
+ bun install
331
+ bun run start
332
+ ```
333
+
334
+ ## Development
335
+
336
+ ```bash
337
+ bun install
338
+ bun run build
339
+ bun test
340
+ bun run verify
341
+ ```
@@ -0,0 +1,71 @@
1
+ import org.jetbrains.kotlin.gradle.dsl.JvmTarget
2
+
3
+ ext {
4
+ junitVersion = project.hasProperty('junitVersion') ? rootProject.ext.junitVersion : '4.13.2'
5
+ androidxAppCompatVersion = project.hasProperty('androidxAppCompatVersion') ? rootProject.ext.androidxAppCompatVersion : '1.7.1'
6
+ androidxJunitVersion = project.hasProperty('androidxJunitVersion') ? rootProject.ext.androidxJunitVersion : '1.3.0'
7
+ androidxEspressoCoreVersion = project.hasProperty('androidxEspressoCoreVersion') ? rootProject.ext.androidxEspressoCoreVersion : '3.7.0'
8
+ mockitoVersion = project.hasProperty('mockitoVersion') ? rootProject.ext.mockitoVersion : '5.14.2'
9
+ }
10
+
11
+ buildscript {
12
+ repositories {
13
+ google()
14
+ mavenCentral()
15
+ }
16
+ dependencies {
17
+ classpath 'com.android.tools.build:gradle:8.13.0'
18
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:2.3.20"
19
+ }
20
+ }
21
+
22
+ apply plugin: 'com.android.library'
23
+ apply plugin: 'org.jetbrains.kotlin.android'
24
+
25
+ android {
26
+ namespace = "com.toast"
27
+ compileSdk = project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 36
28
+ defaultConfig {
29
+ minSdk project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 24
30
+ targetSdk project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 36
31
+ versionCode 1
32
+ versionName "1.0"
33
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
34
+ }
35
+ buildTypes {
36
+ release {
37
+ minifyEnabled false
38
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
39
+ }
40
+ }
41
+ lintOptions {
42
+ abortOnError = false
43
+ }
44
+ compileOptions {
45
+ sourceCompatibility JavaVersion.VERSION_21
46
+ targetCompatibility JavaVersion.VERSION_21
47
+ }
48
+ }
49
+
50
+ kotlin {
51
+ compilerOptions {
52
+ jvmTarget = JvmTarget.JVM_21
53
+ }
54
+ }
55
+
56
+ repositories {
57
+ google()
58
+ mavenCentral()
59
+ }
60
+
61
+
62
+ dependencies {
63
+ implementation project(':capacitor-android')
64
+ implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion"
65
+ implementation "androidx.dynamicanimation:dynamicanimation:1.0.0"
66
+
67
+ testImplementation "junit:junit:$junitVersion"
68
+ testImplementation "org.mockito:mockito-core:$mockitoVersion"
69
+ androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion"
70
+ androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion"
71
+ }
@@ -0,0 +1,6 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
3
+
4
+ <uses-permission android:name="android.permission.INTERNET" />
5
+
6
+ </manifest>
@@ -0,0 +1,197 @@
1
+ package com.toast
2
+
3
+ import android.app.Activity
4
+ import android.graphics.Color
5
+ import com.getcapacitor.JSObject
6
+ import com.getcapacitor.Plugin
7
+ import com.getcapacitor.PluginCall
8
+ import com.getcapacitor.PluginMethod
9
+ import com.getcapacitor.annotation.CapacitorPlugin
10
+
11
+ @CapacitorPlugin(name = "PrettyToast")
12
+ class PrettyToastPlugin : Plugin() {
13
+ private var overlay: ToastOverlay? = null
14
+ private var overlayActivity: Activity? = null
15
+ private var currentToastId: String? = null
16
+
17
+ @PluginMethod
18
+ fun showCurrentToast(call: PluginCall) {
19
+ val payload = parsePayload(call) ?: return
20
+ val overlay = ensureOverlay() ?: run {
21
+ call.reject("Activity is unavailable")
22
+ return
23
+ }
24
+
25
+ currentToastId = payload.id
26
+
27
+ activity?.runOnUiThread {
28
+ overlay.show(
29
+ payload.icon,
30
+ payload.iconUri,
31
+ payload.title,
32
+ payload.message,
33
+ payload.duration,
34
+ payload.autoDismiss,
35
+ payload.enableSwipeDismiss,
36
+ payload.useDynamicIsland,
37
+ payload.accentColor,
38
+ payload.strokeColor,
39
+ payload.disableBackdropSampling,
40
+ payload.actionLabel,
41
+ payload.accessibilityAnnouncement,
42
+ )
43
+ call.resolve()
44
+ }
45
+ }
46
+
47
+ @PluginMethod
48
+ fun updateCurrentToast(call: PluginCall) {
49
+ val payload = parsePayload(call) ?: return
50
+ if (currentToastId != null && currentToastId != payload.id) {
51
+ call.resolve()
52
+ return
53
+ }
54
+
55
+ val overlay = ensureOverlay() ?: run {
56
+ call.reject("Activity is unavailable")
57
+ return
58
+ }
59
+
60
+ activity?.runOnUiThread {
61
+ overlay.update(
62
+ payload.icon,
63
+ payload.iconUri,
64
+ payload.title,
65
+ payload.message,
66
+ payload.duration,
67
+ payload.autoDismiss,
68
+ payload.accentColor,
69
+ payload.strokeColor,
70
+ payload.disableBackdropSampling,
71
+ payload.actionLabel,
72
+ )
73
+ call.resolve()
74
+ }
75
+ }
76
+
77
+ @PluginMethod
78
+ fun dismissCurrentToast(call: PluginCall) {
79
+ val requestedId = call.getString("id")
80
+ if (requestedId != null && currentToastId != null && requestedId != currentToastId) {
81
+ call.resolve()
82
+ return
83
+ }
84
+
85
+ activity?.runOnUiThread {
86
+ overlay?.dismiss()
87
+ call.resolve()
88
+ } ?: call.resolve()
89
+ }
90
+
91
+ override fun handleOnDestroy() {
92
+ overlay?.destroy()
93
+ overlay = null
94
+ overlayActivity = null
95
+ currentToastId = null
96
+ }
97
+
98
+ private fun ensureOverlay(): ToastOverlay? {
99
+ val activity = activity ?: return null
100
+ if (overlay == null || overlayActivity !== activity) {
101
+ overlay?.destroy()
102
+ overlayActivity = activity
103
+ overlay = ToastOverlay(activity).apply {
104
+ onDismiss = {
105
+ val toastId = currentToastId
106
+ if (toastId != null) {
107
+ notifyListeners("toastDismiss", JSObject().put("id", toastId))
108
+ currentToastId = null
109
+ }
110
+ }
111
+ onPress = {
112
+ val toastId = currentToastId
113
+ if (toastId != null) {
114
+ notifyListeners("toastPress", JSObject().put("id", toastId))
115
+ }
116
+ }
117
+ onActionPress = {
118
+ val toastId = currentToastId
119
+ if (toastId != null) {
120
+ notifyListeners("toastActionPress", JSObject().put("id", toastId))
121
+ }
122
+ }
123
+ }
124
+ }
125
+
126
+ return overlay
127
+ }
128
+
129
+ private fun parsePayload(call: PluginCall): ToastPayload? {
130
+ val id = call.getString("id")
131
+ if (id.isNullOrBlank()) {
132
+ call.reject("Missing required parameter: id")
133
+ return null
134
+ }
135
+
136
+ return ToastPayload(
137
+ id = id,
138
+ icon = call.getString("icon") ?: "",
139
+ iconUri = call.getString("iconUri") ?: "",
140
+ title = call.getString("title") ?: "",
141
+ message = call.getString("message") ?: "",
142
+ duration = call.getInt("duration") ?: 3000,
143
+ autoDismiss = call.getBoolean("autoDismiss") ?: true,
144
+ enableSwipeDismiss = call.getBoolean("enableSwipeDismiss") ?: true,
145
+ useDynamicIsland = call.getBoolean("useDynamicIsland") ?: true,
146
+ accentColor = parseColor(call.getString("accentColor")),
147
+ strokeColor = parseColor(call.getString("strokeColor")),
148
+ disableBackdropSampling = call.getBoolean("disableBackdropSampling") ?: false,
149
+ actionLabel = call.getString("actionLabel") ?: "",
150
+ accessibilityAnnouncement = call.getString("accessibilityAnnouncement") ?: "",
151
+ )
152
+ }
153
+
154
+ private fun parseColor(value: String?): Int? {
155
+ if (value.isNullOrBlank()) return null
156
+
157
+ return try {
158
+ when {
159
+ value.startsWith("rgb(", ignoreCase = true) -> parseRgbColor(value)
160
+ value.startsWith("rgba(", ignoreCase = true) -> parseRgbaColor(value)
161
+ else -> Color.parseColor(value)
162
+ }
163
+ } catch (_: IllegalArgumentException) {
164
+ null
165
+ }
166
+ }
167
+
168
+ private fun parseRgbColor(value: String): Int {
169
+ val content = value.substringAfter("(").substringBeforeLast(")")
170
+ val components = content.split(",").map { it.trim().toInt() }
171
+ return Color.rgb(components[0], components[1], components[2])
172
+ }
173
+
174
+ private fun parseRgbaColor(value: String): Int {
175
+ val content = value.substringAfter("(").substringBeforeLast(")")
176
+ val components = content.split(",").map { it.trim() }
177
+ val alpha = (components[3].toFloat() * 255).toInt().coerceIn(0, 255)
178
+ return Color.argb(alpha, components[0].toInt(), components[1].toInt(), components[2].toInt())
179
+ }
180
+ }
181
+
182
+ private data class ToastPayload(
183
+ val id: String,
184
+ val icon: String,
185
+ val iconUri: String,
186
+ val title: String,
187
+ val message: String,
188
+ val duration: Int,
189
+ val autoDismiss: Boolean,
190
+ val enableSwipeDismiss: Boolean,
191
+ val useDynamicIsland: Boolean,
192
+ val accentColor: Int?,
193
+ val strokeColor: Int?,
194
+ val disableBackdropSampling: Boolean,
195
+ val actionLabel: String,
196
+ val accessibilityAnnouncement: String,
197
+ )