@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.
- package/CapgoCapacitorPrettyToast.podspec +17 -0
- package/LICENSE +373 -0
- package/Package.swift +28 -0
- package/README.md +341 -0
- package/android/build.gradle +71 -0
- package/android/src/main/AndroidManifest.xml +6 -0
- package/android/src/main/java/com/toast/PrettyToastPlugin.kt +197 -0
- package/android/src/main/java/com/toast/ToastOverlay.kt +495 -0
- package/android/src/main/java/com/toast/anim/CutoutMorphAnimator.kt +235 -0
- package/android/src/main/java/com/toast/anim/SlideAnimator.kt +64 -0
- package/android/src/main/java/com/toast/anim/ToastAnimator.kt +23 -0
- package/android/src/main/java/com/toast/backdrop/BackdropSampler.kt +142 -0
- package/android/src/main/java/com/toast/backdrop/OutlineController.kt +100 -0
- package/android/src/main/java/com/toast/cutout/CutoutDetector.kt +88 -0
- package/android/src/main/java/com/toast/cutout/CutoutInfo.kt +28 -0
- package/android/src/main/java/com/toast/gesture/ToastGestureHandler.kt +68 -0
- package/android/src/main/java/com/toast/ui/IconMapper.kt +26 -0
- package/android/src/main/java/com/toast/ui/PassThroughFrameLayout.kt +53 -0
- package/android/src/main/java/com/toast/ui/ToastViewFactory.kt +224 -0
- package/android/src/main/java/com/toast/util/Density.kt +17 -0
- package/android/src/main/java/com/toast/util/StatusBarController.kt +24 -0
- package/android/src/main/java/com/toast/util/ToastConstants.kt +36 -0
- package/android/src/main/res/.gitkeep +0 -0
- package/android/src/main/res/drawable/ic_arrow_downward.xml +9 -0
- package/android/src/main/res/drawable/ic_arrow_upward.xml +9 -0
- package/android/src/main/res/drawable/ic_cancel.xml +9 -0
- package/android/src/main/res/drawable/ic_check_circle.xml +9 -0
- package/android/src/main/res/drawable/ic_favorite.xml +9 -0
- package/android/src/main/res/drawable/ic_info.xml +9 -0
- package/android/src/main/res/drawable/ic_mail.xml +9 -0
- package/android/src/main/res/drawable/ic_notifications.xml +9 -0
- package/android/src/main/res/drawable/ic_touch_app.xml +9 -0
- package/android/src/main/res/drawable/ic_warning.xml +9 -0
- package/android/src/main/res/drawable/ic_wifi.xml +9 -0
- package/android/src/main/res/values/colors.xml +3 -0
- package/android/src/main/res/values/strings.xml +3 -0
- package/android/src/main/res/values/styles.xml +3 -0
- package/android/src/test/java/com/toast/PrettyToastPluginTest.kt +26 -0
- package/dist/docs.json +459 -0
- package/dist/esm/controller.d.ts +30 -0
- package/dist/esm/controller.js +271 -0
- package/dist/esm/controller.js.map +1 -0
- package/dist/esm/definitions.d.ts +144 -0
- package/dist/esm/definitions.js +2 -0
- package/dist/esm/definitions.js.map +1 -0
- package/dist/esm/driver.d.ts +19 -0
- package/dist/esm/driver.js +24 -0
- package/dist/esm/driver.js.map +1 -0
- package/dist/esm/icons.d.ts +14 -0
- package/dist/esm/icons.js +138 -0
- package/dist/esm/icons.js.map +1 -0
- package/dist/esm/index.d.ts +2 -0
- package/dist/esm/index.js +2 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/internal-plugin.d.ts +2 -0
- package/dist/esm/internal-plugin.js +5 -0
- package/dist/esm/internal-plugin.js.map +1 -0
- package/dist/esm/internal-types.d.ts +31 -0
- package/dist/esm/internal-types.js +2 -0
- package/dist/esm/internal-types.js.map +1 -0
- package/dist/esm/toast.d.ts +1 -0
- package/dist/esm/toast.js +5 -0
- package/dist/esm/toast.js.map +1 -0
- package/dist/esm/web-renderer.d.ts +36 -0
- package/dist/esm/web-renderer.js +296 -0
- package/dist/esm/web-renderer.js.map +1 -0
- package/dist/esm/web.d.ts +10 -0
- package/dist/esm/web.js +28 -0
- package/dist/esm/web.js.map +1 -0
- package/dist/plugin.cjs.js +770 -0
- package/dist/plugin.cjs.js.map +1 -0
- package/dist/plugin.js +773 -0
- package/dist/plugin.js.map +1 -0
- package/ios/Sources/PrettyToastPlugin/CustomHostingView.swift +13 -0
- package/ios/Sources/PrettyToastPlugin/PassThroughWindow.swift +143 -0
- package/ios/Sources/PrettyToastPlugin/PrettyToastColorParser.swift +94 -0
- package/ios/Sources/PrettyToastPlugin/PrettyToastPlugin.swift +138 -0
- package/ios/Sources/PrettyToastPlugin/PrettyToastView.swift +267 -0
- package/ios/Sources/PrettyToastPlugin/Toast.swift +29 -0
- package/ios/Sources/PrettyToastPlugin/ToastManager.swift +392 -0
- package/ios/Tests/PrettyToastPluginTests/PrettyToastPluginTests.swift +21 -0
- package/package.json +98 -0
- package/scripts/check-capacitor-plugin-wiring.mjs +254 -0
- package/scripts/deploy-example-capgo.mjs +86 -0
- 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
|
+

|
|
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<T></code> |
|
|
215
|
+
| **`messages`** | <code><a href="#promisemessages">PromiseMessages</a><T></code> |
|
|
216
|
+
|
|
217
|
+
**Returns:** <code>Promise<T></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 `<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>(() => void)</code> | Called when the toast body is pressed. |
|
|
269
|
+
| **`onShow`** | <code>(() => void)</code> | Called when the toast becomes visible. |
|
|
270
|
+
| **`onHide`** | <code>(() => void)</code> | Called when the toast is dismissed. |
|
|
271
|
+
| **`onAutoDismiss`** | <code>(() => 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>() => 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) => string | <a href="#toastconfig">ToastConfig</a>); error: string | ((error: unknown) => 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,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
|
+
)
|