capacitor-extended-dialog 0.0.13 → 0.0.15

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.
@@ -11,7 +11,8 @@ Pod::Spec.new do |s|
11
11
  s.author = package['author']
12
12
  s.source = { :git => package['repository']['url'], :tag => s.version.to_s }
13
13
  s.source_files = 'ios/Sources/**/*.{swift,h,m,c,cc,mm,cpp}'
14
- s.ios.deployment_target = '14.0'
14
+ s.ios.deployment_target = '15.0'
15
15
  s.dependency 'Capacitor'
16
+ s.dependency 'SVGKit', '~> 3.0'
16
17
  s.swift_version = '5.1'
17
18
  end
package/Package.swift CHANGED
@@ -10,14 +10,16 @@ let package = Package(
10
10
  targets: ["ExtendedDialogPlugin"])
11
11
  ],
12
12
  dependencies: [
13
- .package(url: "https://github.com/ionic-team/capacitor-swift-pm.git", from: "8.0.0")
13
+ .package(url: "https://github.com/ionic-team/capacitor-swift-pm.git", from: "8.0.0"),
14
+ .package(url: "https://github.com/SVGKit/SVGKit.git", from: "3.0.0")
14
15
  ],
15
16
  targets: [
16
17
  .target(
17
18
  name: "ExtendedDialogPlugin",
18
19
  dependencies: [
19
20
  .product(name: "Capacitor", package: "capacitor-swift-pm"),
20
- .product(name: "Cordova", package: "capacitor-swift-pm")
21
+ .product(name: "Cordova", package: "capacitor-swift-pm"),
22
+ .product(name: "SVGKit", package: "SVGKit")
21
23
  ],
22
24
  path: "ios/Sources/ExtendedDialogPlugin"),
23
25
  .testTarget(
package/README.md CHANGED
@@ -38,7 +38,7 @@ npx cap sync
38
38
  ### alert(...)
39
39
 
40
40
  ```typescript
41
- alert(options: AlertOptions) => any
41
+ alert(options: AlertOptions) => Promise<void>
42
42
  ```
43
43
 
44
44
  Show an alert dialog with a single dismiss button.
@@ -47,15 +47,13 @@ Show an alert dialog with a single dismiss button.
47
47
  | ------------- | ----------------------------------------------------- | ------------------------------ |
48
48
  | **`options`** | <code><a href="#alertoptions">AlertOptions</a></code> | - Alert configuration options. |
49
49
 
50
- **Returns:** <code>any</code>
51
-
52
50
  --------------------
53
51
 
54
52
 
55
53
  ### confirm(...)
56
54
 
57
55
  ```typescript
58
- confirm(options: ConfirmOptions) => any
56
+ confirm(options: ConfirmOptions) => Promise<ConfirmResult>
59
57
  ```
60
58
 
61
59
  Show a confirmation dialog with OK and Cancel buttons.
@@ -64,7 +62,7 @@ Show a confirmation dialog with OK and Cancel buttons.
64
62
  | ------------- | --------------------------------------------------------- | -------------------------------- |
65
63
  | **`options`** | <code><a href="#confirmoptions">ConfirmOptions</a></code> | - Confirm configuration options. |
66
64
 
67
- **Returns:** <code>any</code>
65
+ **Returns:** <code>Promise&lt;<a href="#confirmresult">ConfirmResult</a>&gt;</code>
68
66
 
69
67
  --------------------
70
68
 
@@ -72,7 +70,7 @@ Show a confirmation dialog with OK and Cancel buttons.
72
70
  ### prompt(...)
73
71
 
74
72
  ```typescript
75
- prompt(options: PromptOptions) => any
73
+ prompt(options: PromptOptions) => Promise<PromptResult>
76
74
  ```
77
75
 
78
76
  Show a prompt dialog with a text input field.
@@ -81,7 +79,7 @@ Show a prompt dialog with a text input field.
81
79
  | ------------- | ------------------------------------------------------- | ------------------------------- |
82
80
  | **`options`** | <code><a href="#promptoptions">PromptOptions</a></code> | - Prompt configuration options. |
83
81
 
84
- **Returns:** <code>any</code>
82
+ **Returns:** <code>Promise&lt;<a href="#promptresult">PromptResult</a>&gt;</code>
85
83
 
86
84
  --------------------
87
85
 
@@ -89,7 +87,7 @@ Show a prompt dialog with a text input field.
89
87
  ### singleSelect(...)
90
88
 
91
89
  ```typescript
92
- singleSelect(options: SingleSelectOptions) => any
90
+ singleSelect(options: SingleSelectOptions) => Promise<SingleSelectResult>
93
91
  ```
94
92
 
95
93
  Show a single-select dialog where the user picks one option.
@@ -98,7 +96,7 @@ Show a single-select dialog where the user picks one option.
98
96
  | ------------- | ------------------------------------------------------------------- | -------------------------------------- |
99
97
  | **`options`** | <code><a href="#singleselectoptions">SingleSelectOptions</a></code> | - Single select configuration options. |
100
98
 
101
- **Returns:** <code>any</code>
99
+ **Returns:** <code>Promise&lt;<a href="#singleselectresult">SingleSelectResult</a>&gt;</code>
102
100
 
103
101
  --------------------
104
102
 
@@ -106,7 +104,7 @@ Show a single-select dialog where the user picks one option.
106
104
  ### multiSelect(...)
107
105
 
108
106
  ```typescript
109
- multiSelect(options: MultiSelectOptions) => any
107
+ multiSelect(options: MultiSelectOptions) => Promise<MultiSelectResult>
110
108
  ```
111
109
 
112
110
  Show a multi-select dialog where the user picks one or more options.
@@ -115,7 +113,7 @@ Show a multi-select dialog where the user picks one or more options.
115
113
  | ------------- | ----------------------------------------------------------------- | ------------------------------------- |
116
114
  | **`options`** | <code><a href="#multiselectoptions">MultiSelectOptions</a></code> | - Multi select configuration options. |
117
115
 
118
- **Returns:** <code>any</code>
116
+ **Returns:** <code>Promise&lt;<a href="#multiselectresult">MultiSelectResult</a>&gt;</code>
119
117
 
120
118
  --------------------
121
119
 
@@ -123,7 +121,7 @@ Show a multi-select dialog where the user picks one or more options.
123
121
  ### sheet(...)
124
122
 
125
123
  ```typescript
126
- sheet(options: SheetOptions) => any
124
+ sheet(options: SheetOptions) => Promise<SheetResult>
127
125
  ```
128
126
 
129
127
  Show a sheet dialog with a header, title, and structured data rows.
@@ -132,7 +130,7 @@ Show a sheet dialog with a header, title, and structured data rows.
132
130
  | ------------- | ----------------------------------------------------- | ------------------------------ |
133
131
  | **`options`** | <code><a href="#sheetoptions">SheetOptions</a></code> | - Sheet configuration options. |
134
132
 
135
- **Returns:** <code>any</code>
133
+ **Returns:** <code>Promise&lt;<a href="#sheetresult">SheetResult</a>&gt;</code>
136
134
 
137
135
  --------------------
138
136
 
@@ -140,7 +138,7 @@ Show a sheet dialog with a header, title, and structured data rows.
140
138
  ### messageSheet(...)
141
139
 
142
140
  ```typescript
143
- messageSheet(options: MessageSheetOptions) => any
141
+ messageSheet(options: MessageSheetOptions) => Promise<MessageSheetResult>
144
142
  ```
145
143
 
146
144
  Show a message sheet dialog with an optional header logo, title, and message.
@@ -149,7 +147,7 @@ Show a message sheet dialog with an optional header logo, title, and message.
149
147
  | ------------- | ------------------------------------------------------------------- | -------------------------------------- |
150
148
  | **`options`** | <code><a href="#messagesheetoptions">MessageSheetOptions</a></code> | - Message sheet configuration options. |
151
149
 
152
- **Returns:** <code>any</code>
150
+ **Returns:** <code>Promise&lt;<a href="#messagesheetresult">MessageSheetResult</a>&gt;</code>
153
151
 
154
152
  --------------------
155
153
 
@@ -164,6 +162,13 @@ Show a message sheet dialog with an optional header logo, title, and message.
164
162
  | **`buttonTitle`** | <code>string</code> | Title for the dismiss button. | <code>"OK"</code> |
165
163
 
166
164
 
165
+ #### ConfirmResult
166
+
167
+ | Prop | Type | Description |
168
+ | ----------- | -------------------- | ----------------------------------------------------------- |
169
+ | **`value`** | <code>boolean</code> | Whether the user confirmed (`true`) or cancelled (`false`). |
170
+
171
+
167
172
  #### ConfirmOptions
168
173
 
169
174
  | Prop | Type | Description | Default |
@@ -172,11 +177,12 @@ Show a message sheet dialog with an optional header logo, title, and message.
172
177
  | **`cancelButtonTitle`** | <code>string</code> | Title for the cancel button. | <code>"Cancel"</code> |
173
178
 
174
179
 
175
- #### ConfirmResult
180
+ #### PromptResult
176
181
 
177
- | Prop | Type | Description |
178
- | ----------- | -------------------- | ----------------------------------------------------------- |
179
- | **`value`** | <code>boolean</code> | Whether the user confirmed (`true`) or cancelled (`false`). |
182
+ | Prop | Type | Description |
183
+ | --------------- | -------------------- | -------------------------------------------------------- |
184
+ | **`value`** | <code>string</code> | The text entered by the user. Empty string if cancelled. |
185
+ | **`cancelled`** | <code>boolean</code> | Whether the user cancelled the dialog. |
180
186
 
181
187
 
182
188
  #### PromptOptions
@@ -190,22 +196,22 @@ Show a message sheet dialog with an optional header logo, title, and message.
190
196
  | **`focusInput`** | <code>boolean</code> | Whether to automatically focus the input field and open the keyboard when the dialog appears. Note: On iOS, basic (non-fullscreen) dialogs always auto-focus the input field due to UIAlertController's built-in behavior. This option only takes effect on iOS when using fullscreen mode (`mode: 'fullscreen'`). On Android and web, this works for all modes. | <code>false</code> |
191
197
 
192
198
 
193
- #### PromptResult
199
+ #### SingleSelectResult
194
200
 
195
- | Prop | Type | Description |
196
- | --------------- | -------------------- | -------------------------------------------------------- |
197
- | **`value`** | <code>string</code> | The text entered by the user. Empty string if cancelled. |
198
- | **`cancelled`** | <code>boolean</code> | Whether the user cancelled the dialog. |
201
+ | Prop | Type | Description |
202
+ | --------------- | --------------------------- | --------------------------------------------------------- |
203
+ | **`value`** | <code>string \| null</code> | The value of the selected option, or `null` if cancelled. |
204
+ | **`cancelled`** | <code>boolean</code> | Whether the user cancelled the dialog. |
199
205
 
200
206
 
201
207
  #### SingleSelectOptions
202
208
 
203
- | Prop | Type | Description | Default |
204
- | ----------------------- | ------------------- | ----------------------------------------- | --------------------- |
205
- | **`options`** | <code>{}</code> | List of options to display for selection. | |
206
- | **`selectedValue`** | <code>string</code> | Value of the initially selected option. | |
207
- | **`okButtonTitle`** | <code>string</code> | Title for the confirmation button. | <code>"OK"</code> |
208
- | **`cancelButtonTitle`** | <code>string</code> | Title for the cancel button. | <code>"Cancel"</code> |
209
+ | Prop | Type | Description | Default |
210
+ | ----------------------- | --------------------------- | ----------------------------------------- | --------------------- |
211
+ | **`options`** | <code>SelectOption[]</code> | List of options to display for selection. | |
212
+ | **`selectedValue`** | <code>string</code> | Value of the initially selected option. | |
213
+ | **`okButtonTitle`** | <code>string</code> | Title for the confirmation button. | <code>"OK"</code> |
214
+ | **`cancelButtonTitle`** | <code>string</code> | Title for the cancel button. | <code>"Cancel"</code> |
209
215
 
210
216
 
211
217
  #### SelectOption
@@ -216,55 +222,54 @@ Show a message sheet dialog with an optional header logo, title, and message.
216
222
  | **`value`** | <code>string</code> | Value returned when the option is selected. |
217
223
 
218
224
 
219
- #### SingleSelectResult
225
+ #### MultiSelectResult
220
226
 
221
- | Prop | Type | Description |
222
- | --------------- | --------------------------- | --------------------------------------------------------- |
223
- | **`value`** | <code>string \| null</code> | The value of the selected option, or `null` if cancelled. |
224
- | **`cancelled`** | <code>boolean</code> | Whether the user cancelled the dialog. |
227
+ | Prop | Type | Description |
228
+ | --------------- | --------------------- | ------------------------------------------------------------- |
229
+ | **`values`** | <code>string[]</code> | The values of the selected options. Empty array if cancelled. |
230
+ | **`cancelled`** | <code>boolean</code> | Whether the user cancelled the dialog. |
225
231
 
226
232
 
227
233
  #### MultiSelectOptions
228
234
 
229
- | Prop | Type | Description | Default |
230
- | ----------------------- | ------------------- | ----------------------------------------- | --------------------- |
231
- | **`options`** | <code>{}</code> | List of options to display for selection. | |
232
- | **`selectedValues`** | <code>{}</code> | Values of the initially selected options. | |
233
- | **`okButtonTitle`** | <code>string</code> | Title for the confirmation button. | <code>"OK"</code> |
234
- | **`cancelButtonTitle`** | <code>string</code> | Title for the cancel button. | <code>"Cancel"</code> |
235
+ | Prop | Type | Description | Default |
236
+ | ----------------------- | --------------------------- | ----------------------------------------- | --------------------- |
237
+ | **`options`** | <code>SelectOption[]</code> | List of options to display for selection. | |
238
+ | **`selectedValues`** | <code>string[]</code> | Values of the initially selected options. | |
239
+ | **`okButtonTitle`** | <code>string</code> | Title for the confirmation button. | <code>"OK"</code> |
240
+ | **`cancelButtonTitle`** | <code>string</code> | Title for the cancel button. | <code>"Cancel"</code> |
235
241
 
236
242
 
237
- #### MultiSelectResult
243
+ #### SheetResult
238
244
 
239
- | Prop | Type | Description |
240
- | --------------- | -------------------- | ------------------------------------------------------------- |
241
- | **`values`** | <code>{}</code> | The values of the selected options. Empty array if cancelled. |
242
- | **`cancelled`** | <code>boolean</code> | Whether the user cancelled the dialog. |
245
+ | Prop | Type | Description |
246
+ | --------------- | -------------------- | ------------------------------------- |
247
+ | **`confirmed`** | <code>boolean</code> | True if confirmed, false if cancelled |
243
248
 
244
249
 
245
250
  #### SheetOptions
246
251
 
247
- | Prop | Type | Description |
248
- | ------------------------ | ------------------------------------------------- | -------------------------------------------------------- |
249
- | **`headerLogo`** | <code>string</code> | Header logo - supports base64 data URL or HTTP/HTTPS URL |
250
- | **`title`** | <code>string</code> | Sheet title |
251
- | **`subtitle`** | <code>string</code> | Optional subtitle displayed between title and rows |
252
- | **`rows`** | <code>{}</code> | Description rows |
253
- | **`confirmButtonTitle`** | <code>string</code> | Confirm button title |
254
- | **`cancelButtonTitle`** | <code>string</code> | Cancel button title |
255
- | **`mode`** | <code><a href="#dialogmode">DialogMode</a></code> | Dialog mode |
252
+ | Prop | Type | Description |
253
+ | ------------------------ | ------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
254
+ | **`headerLogo`** | <code>string</code> | Header logo. Supports: - PNG/JPEG/WebP/GIF as base64 data URL or HTTP/HTTPS URL - SVG as `data:image/svg+xml;base64,...`, `data:image/svg+xml,&lt;svg ...&gt;...` (URL-encoded or plain), or any HTTP/HTTPS URL ending in `.svg` |
255
+ | **`title`** | <code>string</code> | Sheet title |
256
+ | **`subtitle`** | <code>string</code> | Optional subtitle displayed between title and rows |
257
+ | **`rows`** | <code>SheetRow[]</code> | Description rows |
258
+ | **`confirmButtonTitle`** | <code>string</code> | Confirm button title |
259
+ | **`cancelButtonTitle`** | <code>string</code> | Cancel button title |
260
+ | **`mode`** | <code><a href="#dialogmode">DialogMode</a></code> | Dialog mode |
256
261
 
257
262
 
258
263
  #### SheetRow
259
264
 
260
- | Prop | Type | Description |
261
- | ----------- | ------------------- | --------------------------------------------------------------- |
262
- | **`title`** | <code>string</code> | Row title (required) |
263
- | **`logo`** | <code>string</code> | Optional logo/icon - supports base64 data URL or HTTP/HTTPS URL |
264
- | **`value`** | <code>string</code> | Optional value displayed on the right |
265
+ | Prop | Type | Description |
266
+ | ----------- | ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
267
+ | **`title`** | <code>string</code> | Row title (required) |
268
+ | **`logo`** | <code>string</code> | Optional logo/icon. Supports: - PNG/JPEG/WebP/GIF as base64 data URL or HTTP/HTTPS URL - SVG as `data:image/svg+xml;base64,...`, `data:image/svg+xml,&lt;svg ...&gt;...` (URL-encoded or plain), or any HTTP/HTTPS URL ending in `.svg` |
269
+ | **`value`** | <code>string</code> | Optional value displayed on the right |
265
270
 
266
271
 
267
- #### SheetResult
272
+ #### MessageSheetResult
268
273
 
269
274
  | Prop | Type | Description |
270
275
  | --------------- | -------------------- | ------------------------------------- |
@@ -273,22 +278,15 @@ Show a message sheet dialog with an optional header logo, title, and message.
273
278
 
274
279
  #### MessageSheetOptions
275
280
 
276
- | Prop | Type | Description |
277
- | ------------------------ | ------------------------------------------------- | -------------------------------------------------------- |
278
- | **`headerLogo`** | <code>string</code> | Header logo - supports base64 data URL or HTTP/HTTPS URL |
279
- | **`title`** | <code>string</code> | Sheet title |
280
- | **`subtitle`** | <code>string</code> | Optional subtitle displayed between title and message |
281
- | **`message`** | <code>string</code> | Message body displayed in the sheet |
282
- | **`confirmButtonTitle`** | <code>string</code> | Confirm button title |
283
- | **`cancelButtonTitle`** | <code>string</code> | Cancel button title |
284
- | **`mode`** | <code><a href="#dialogmode">DialogMode</a></code> | Dialog mode |
285
-
286
-
287
- #### MessageSheetResult
288
-
289
- | Prop | Type | Description |
290
- | --------------- | -------------------- | ------------------------------------- |
291
- | **`confirmed`** | <code>boolean</code> | True if confirmed, false if cancelled |
281
+ | Prop | Type | Description |
282
+ | ------------------------ | ------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
283
+ | **`headerLogo`** | <code>string</code> | Header logo. Supports: - PNG/JPEG/WebP/GIF as base64 data URL or HTTP/HTTPS URL - SVG as `data:image/svg+xml;base64,...`, `data:image/svg+xml,&lt;svg ...&gt;...` (URL-encoded or plain), or any HTTP/HTTPS URL ending in `.svg` |
284
+ | **`title`** | <code>string</code> | Sheet title |
285
+ | **`subtitle`** | <code>string</code> | Optional subtitle displayed between title and message |
286
+ | **`message`** | <code>string</code> | Message body displayed in the sheet |
287
+ | **`confirmButtonTitle`** | <code>string</code> | Confirm button title |
288
+ | **`cancelButtonTitle`** | <code>string</code> | Cancel button title |
289
+ | **`mode`** | <code><a href="#dialogmode">DialogMode</a></code> | Dialog mode |
292
290
 
293
291
 
294
292
  ### Type Aliases
@@ -1,8 +1,8 @@
1
1
  ext {
2
2
  junitVersion = project.hasProperty('junitVersion') ? rootProject.ext.junitVersion : '4.13.2'
3
- androidxAppCompatVersion = project.hasProperty('androidxAppCompatVersion') ? rootProject.ext.androidxAppCompatVersion : '1.7.0'
4
- androidxJunitVersion = project.hasProperty('androidxJunitVersion') ? rootProject.ext.androidxJunitVersion : '1.2.1'
5
- androidxEspressoCoreVersion = project.hasProperty('androidxEspressoCoreVersion') ? rootProject.ext.androidxEspressoCoreVersion : '3.6.1'
3
+ androidxAppCompatVersion = project.hasProperty('androidxAppCompatVersion') ? rootProject.ext.androidxAppCompatVersion : '1.7.1'
4
+ androidxJunitVersion = project.hasProperty('androidxJunitVersion') ? rootProject.ext.androidxJunitVersion : '1.3.0'
5
+ androidxEspressoCoreVersion = project.hasProperty('androidxEspressoCoreVersion') ? rootProject.ext.androidxEspressoCoreVersion : '3.7.0'
6
6
  }
7
7
 
8
8
  buildscript {
@@ -11,18 +11,18 @@ buildscript {
11
11
  mavenCentral()
12
12
  }
13
13
  dependencies {
14
- classpath 'com.android.tools.build:gradle:8.7.2'
14
+ classpath 'com.android.tools.build:gradle:8.13.0'
15
15
  }
16
16
  }
17
17
 
18
18
  apply plugin: 'com.android.library'
19
19
 
20
20
  android {
21
- namespace "com.albermonte.extendeddialog"
22
- compileSdk project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 35
21
+ namespace = "com.albermonte.extendeddialog"
22
+ compileSdk = project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 36
23
23
  defaultConfig {
24
- minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 23
25
- targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 35
24
+ minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 24
25
+ targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 36
26
26
  versionCode 1
27
27
  versionName "1.0"
28
28
  testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
@@ -34,7 +34,7 @@ android {
34
34
  }
35
35
  }
36
36
  lintOptions {
37
- abortOnError false
37
+ abortOnError = false
38
38
  }
39
39
  compileOptions {
40
40
  sourceCompatibility JavaVersion.VERSION_21
@@ -53,6 +53,7 @@ dependencies {
53
53
  implementation project(':capacitor-android')
54
54
  implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion"
55
55
  implementation 'com.google.android.material:material:1.12.0'
56
+ implementation 'com.caverock:androidsvg-aar:1.4'
56
57
  testImplementation "junit:junit:$junitVersion"
57
58
  androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion"
58
59
  androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion"
@@ -7,7 +7,6 @@ import android.content.res.ColorStateList;
7
7
  import android.graphics.Bitmap;
8
8
  import android.graphics.BitmapFactory;
9
9
  import android.os.Bundle;
10
- import android.util.Base64;
11
10
  import android.util.TypedValue;
12
11
  import android.view.ContextThemeWrapper;
13
12
  import android.view.Gravity;
@@ -52,6 +51,8 @@ public class SheetBottomDialogFragment extends BottomSheetDialogFragment {
52
51
  private static final String ARG_SUBTITLE = "subtitle";
53
52
  private static final String ARG_IS_MESSAGE_SHEET = "isMessageSheet";
54
53
 
54
+ private static final Executor IMAGE_EXECUTOR = Executors.newCachedThreadPool();
55
+
55
56
  private ExtendedDialog.SheetCallback sheetCallback;
56
57
  private boolean dismissed = false;
57
58
  private Context themedContext;
@@ -706,28 +707,32 @@ public class SheetBottomDialogFragment extends BottomSheetDialogFragment {
706
707
  }
707
708
 
708
709
  private void loadImageAsync(ImageView imageView, String imageUrl) {
709
- Executor executor = Executors.newSingleThreadExecutor();
710
- executor.execute(() -> {
710
+ if (imageUrl == null || imageUrl.isEmpty()) return;
711
+ IMAGE_EXECUTOR.execute(() -> {
711
712
  try {
712
- Bitmap bitmap;
713
- if (imageUrl.startsWith("data:")) {
714
- String base64Data = imageUrl.substring(imageUrl.indexOf(",") + 1);
715
- byte[] decodedBytes = Base64.decode(base64Data, Base64.DEFAULT);
716
- bitmap = BitmapFactory.decodeByteArray(decodedBytes, 0, decodedBytes.length);
717
- } else {
718
- InputStream inputStream = new URL(imageUrl).openStream();
719
- bitmap = BitmapFactory.decodeStream(inputStream);
720
- inputStream.close();
721
- }
713
+ Bitmap bitmap = decodeImage(imageUrl, imageView);
722
714
  if (bitmap != null) {
723
715
  imageView.post(() -> imageView.setImageBitmap(bitmap));
724
716
  }
725
717
  } catch (Exception e) {
726
- // Silently fail - image won't be displayed
718
+ android.util.Log.w("ExtendedDialog", "Failed to load image: " + e.getMessage());
727
719
  }
728
720
  });
729
721
  }
730
722
 
723
+ private static Bitmap decodeImage(String imageUrl, ImageView imageView) throws Exception {
724
+ if (SvgImageLoader.isSvgSource(imageUrl)) {
725
+ return SvgImageLoader.render(imageUrl, imageView);
726
+ }
727
+ if (SvgImageLoader.isDataUrl(imageUrl)) {
728
+ byte[] decodedBytes = SvgImageLoader.decodeDataUrl(imageUrl);
729
+ return BitmapFactory.decodeByteArray(decodedBytes, 0, decodedBytes.length);
730
+ }
731
+ try (InputStream inputStream = new URL(imageUrl).openStream()) {
732
+ return BitmapFactory.decodeStream(inputStream);
733
+ }
734
+ }
735
+
731
736
  private void handleConfirm() {
732
737
  if (dismissed) return;
733
738
  dismissed = true;
@@ -0,0 +1,90 @@
1
+ package com.albermonte.extendeddialog;
2
+
3
+ import android.graphics.Bitmap;
4
+ import android.graphics.Canvas;
5
+ import android.net.Uri;
6
+ import android.util.Base64;
7
+ import android.view.ViewGroup;
8
+ import android.widget.ImageView;
9
+ import com.caverock.androidsvg.SVG;
10
+ import com.caverock.androidsvg.SVGParseException;
11
+ import java.io.ByteArrayInputStream;
12
+ import java.io.IOException;
13
+ import java.io.InputStream;
14
+ import java.net.URL;
15
+ import java.net.URLDecoder;
16
+ import java.nio.charset.StandardCharsets;
17
+ import java.util.Locale;
18
+
19
+ final class SvgImageLoader {
20
+
21
+ private static final String SVG_DATA_URL_PREFIX = "data:image/svg+xml";
22
+ private static final String DATA_URL_PREFIX = "data:";
23
+ private static final String SVG_SUFFIX = ".svg";
24
+
25
+ private SvgImageLoader() {}
26
+
27
+ static boolean isDataUrl(String source) {
28
+ return source != null && source.regionMatches(true, 0, DATA_URL_PREFIX, 0, DATA_URL_PREFIX.length());
29
+ }
30
+
31
+ static boolean isSvgSource(String source) {
32
+ if (source == null) return false;
33
+ if (source.regionMatches(true, 0, SVG_DATA_URL_PREFIX, 0, SVG_DATA_URL_PREFIX.length())) return true;
34
+ Uri uri = Uri.parse(source);
35
+ String scheme = uri.getScheme();
36
+ if (scheme == null || (!scheme.equalsIgnoreCase("http") && !scheme.equalsIgnoreCase("https"))) {
37
+ return false;
38
+ }
39
+ String path = uri.getPath();
40
+ return path != null && path.toLowerCase(Locale.ROOT).endsWith(SVG_SUFFIX);
41
+ }
42
+
43
+ /** Supports `;base64` and percent-encoded payloads. */
44
+ static byte[] decodeDataUrl(String source) throws IOException {
45
+ int comma = source.indexOf(',');
46
+ if (comma < 0) throw new IOException("Malformed data URL");
47
+ String header = source.substring(0, comma);
48
+ String payload = source.substring(comma + 1);
49
+ if (header.toLowerCase(Locale.ROOT).contains(";base64")) {
50
+ return Base64.decode(payload, Base64.DEFAULT);
51
+ }
52
+ return URLDecoder.decode(payload, StandardCharsets.UTF_8.name()).getBytes(StandardCharsets.UTF_8);
53
+ }
54
+
55
+ /** Sizes Bitmap to {@code targetView}'s layout params (or 64dp default). */
56
+ static Bitmap render(String source, ImageView targetView) throws SVGParseException, IOException {
57
+ SVG svg = parse(source);
58
+ ViewGroup.LayoutParams params = targetView.getLayoutParams();
59
+ int width = params != null ? params.width : 0;
60
+ int height = params != null ? params.height : 0;
61
+ if (width <= 0 || height <= 0) {
62
+ float density = targetView.getResources().getDisplayMetrics().density;
63
+ int fallback = (int) (64 * density);
64
+ if (width <= 0) width = fallback;
65
+ if (height <= 0) height = fallback;
66
+ }
67
+
68
+ // Rescale via viewBox + preserveAspectRatio so small-viewport SVGs
69
+ // (e.g. width="20") aren't rendered tiny in the top-left corner.
70
+ svg.setDocumentWidth(width);
71
+ svg.setDocumentHeight(height);
72
+
73
+ Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
74
+ Canvas canvas = new Canvas(bitmap);
75
+ svg.renderToCanvas(canvas);
76
+ return bitmap;
77
+ }
78
+
79
+ private static SVG parse(String source) throws SVGParseException, IOException {
80
+ if (isDataUrl(source)) {
81
+ byte[] bytes = decodeDataUrl(source);
82
+ try (InputStream in = new ByteArrayInputStream(bytes)) {
83
+ return SVG.getFromInputStream(in);
84
+ }
85
+ }
86
+ try (InputStream in = new URL(source).openStream()) {
87
+ return SVG.getFromInputStream(in);
88
+ }
89
+ }
90
+ }