@capgo/capacitor-file-picker 8.2.6 → 8.2.8
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/README.md +26 -6
- package/android/build.gradle +2 -0
- package/android/src/main/java/app/capgo/capacitor/filepicker/CapgoFilePickerPlugin.java +119 -73
- package/android/src/main/java/app/capgo/capacitor/filepicker/MediaPickerIntents.java +87 -0
- package/android/src/main/java/app/capgo/capacitor/filepicker/PickerLaunchQueue.java +126 -0
- package/dist/docs.json +3 -3
- package/dist/esm/definitions.d.ts +11 -0
- package/dist/esm/definitions.js.map +1 -1
- package/ios/Sources/CapgoFilePickerPlugin/CapgoFilePickerPlugin.swift +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -16,6 +16,18 @@ Let users pick files, images, videos and folders with the native pickers on iOS
|
|
|
16
16
|
<img src="https://raw.githubusercontent.com/Cap-go/capacitor-file-picker/main/assets/github-social-preview.png" alt="@capgo/capacitor-file-picker for Capacitor apps" width="300" />
|
|
17
17
|
</p>
|
|
18
18
|
|
|
19
|
+
## Built and maintained with AI
|
|
20
|
+
|
|
21
|
+
We build, improve and fix our plugins with heavy use of AI, so issues usually get fixed in under a day and pull requests get feedback the same day. A human still reviews and merges 50+ pull requests daily.
|
|
22
|
+
|
|
23
|
+
- Most plugin issues get a fix pull request the same day, often within a couple of hours, and are usually resolved in one to two days.
|
|
24
|
+
- Every pull request goes through our AI review system, usually within minutes.
|
|
25
|
+
- Our AI pre-checks tests, code review, conventions and security, then a human validates and merges.
|
|
26
|
+
- A human, Martin, reviews and merges 50+ pull requests every day.
|
|
27
|
+
- In the last 90 days, 95% of plugin issues were resolved.
|
|
28
|
+
|
|
29
|
+
[Learn more about how we build with AI](https://capgo.app/docs/plugins/ai-engineering/)
|
|
30
|
+
|
|
19
31
|
## Key features
|
|
20
32
|
|
|
21
33
|
- **Files**: `pickFiles()` picks one or more files of any type.
|
|
@@ -152,6 +164,10 @@ pickImages(options?: PickMediaOptions | undefined) => Promise<PickFilesResult>
|
|
|
152
164
|
|
|
153
165
|
Pick one or more images from the gallery.
|
|
154
166
|
Android/iOS only.
|
|
167
|
+
On Android, `limit` is enforced by the system photo picker when available
|
|
168
|
+
(Android 13+, or Android 11-12 with the Google Play system photo picker extension).
|
|
169
|
+
Older Android versions may allow selecting more items than `limit`; results are
|
|
170
|
+
truncated to `limit` before they are returned.
|
|
155
171
|
|
|
156
172
|
| Param | Type | Description |
|
|
157
173
|
| ------------- | ------------------------------------------------------------- | ---------------------------- |
|
|
@@ -172,6 +188,10 @@ pickVideos(options?: PickMediaOptions | undefined) => Promise<PickFilesResult>
|
|
|
172
188
|
|
|
173
189
|
Pick one or more videos from the gallery.
|
|
174
190
|
Android/iOS only.
|
|
191
|
+
On Android, `limit` is enforced by the system photo picker when available
|
|
192
|
+
(Android 13+, or Android 11-12 with the Google Play system photo picker extension).
|
|
193
|
+
Older Android versions may allow selecting more items than `limit`; results are
|
|
194
|
+
truncated to `limit` before they are returned.
|
|
175
195
|
|
|
176
196
|
| Param | Type | Description |
|
|
177
197
|
| ------------- | ------------------------------------------------------------- | ---------------------------- |
|
|
@@ -390,12 +410,12 @@ Options for picking files.
|
|
|
390
410
|
|
|
391
411
|
Options for picking media (images/videos).
|
|
392
412
|
|
|
393
|
-
| Prop | Type | Description
|
|
394
|
-
| --------------------- | -------------------- |
|
|
395
|
-
| **`limit`** | <code>number</code> | Maximum number of files to pick. Set to 0 for unlimited (platform default).
|
|
396
|
-
| **`readData`** | <code>boolean</code> | Whether to read the file data as base64. Note: Reading large files may cause memory issues.
|
|
397
|
-
| **`skipTranscoding`** | <code>boolean</code> | iOS only: Skip transcoding of videos.
|
|
398
|
-
| **`ordered`** | <code>boolean</code> | iOS 15+ only: Show ordered selection badges.
|
|
413
|
+
| Prop | Type | Description | Default |
|
|
414
|
+
| --------------------- | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------ |
|
|
415
|
+
| **`limit`** | <code>number</code> | Maximum number of files to pick. Set to 0 for unlimited (platform default). On Android, when the system photo picker is available, the platform enforces this limit (capped by the device maximum). On older Android versions the picker may not enforce the limit; the plugin truncates the returned list to this value when needed. | <code>0</code> |
|
|
416
|
+
| **`readData`** | <code>boolean</code> | Whether to read the file data as base64. Note: Reading large files may cause memory issues. | <code>false</code> |
|
|
417
|
+
| **`skipTranscoding`** | <code>boolean</code> | iOS only: Skip transcoding of videos. | <code>false</code> |
|
|
418
|
+
| **`ordered`** | <code>boolean</code> | iOS 15+ only: Show ordered selection badges. | <code>false</code> |
|
|
399
419
|
|
|
400
420
|
|
|
401
421
|
#### PickDirectoryResult
|
package/android/build.gradle
CHANGED
|
@@ -53,6 +53,8 @@ dependencies {
|
|
|
53
53
|
implementation project(':capacitor-android')
|
|
54
54
|
implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion"
|
|
55
55
|
testImplementation "junit:junit:$junitVersion"
|
|
56
|
+
testImplementation "org.mockito:mockito-core:5.14.2"
|
|
57
|
+
testImplementation "org.robolectric:robolectric:4.16"
|
|
56
58
|
androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion"
|
|
57
59
|
androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion"
|
|
58
60
|
}
|
|
@@ -2,6 +2,7 @@ package app.capgo.capacitor.filepicker;
|
|
|
2
2
|
|
|
3
3
|
import android.Manifest;
|
|
4
4
|
import android.app.Activity;
|
|
5
|
+
import android.content.ActivityNotFoundException;
|
|
5
6
|
import android.content.ClipData;
|
|
6
7
|
import android.content.Intent;
|
|
7
8
|
import android.database.Cursor;
|
|
@@ -50,14 +51,30 @@ import org.json.JSONException;
|
|
|
50
51
|
)
|
|
51
52
|
public class CapgoFilePickerPlugin extends Plugin {
|
|
52
53
|
|
|
53
|
-
private final String pluginVersion = "8.2.
|
|
54
|
-
|
|
54
|
+
private final String pluginVersion = "8.2.8";
|
|
55
|
+
|
|
56
|
+
private PickerLaunchQueue pickerLaunchQueue;
|
|
57
|
+
|
|
58
|
+
@Override
|
|
59
|
+
public void load() {
|
|
60
|
+
super.load();
|
|
61
|
+
pickerLaunchQueue = new PickerLaunchQueue((runnable) -> bridge.executeOnMainThread(runnable), this::launchPickerActivity);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
private boolean launchPickerActivity(PickerLaunchQueue.PendingPickerLaunch launch) {
|
|
65
|
+
try {
|
|
66
|
+
startActivityForResult(launch.call, launch.getIntent(), launch.callbackName);
|
|
67
|
+
return true;
|
|
68
|
+
} catch (ActivityNotFoundException e) {
|
|
69
|
+
launch.call.reject("Unable to open file picker", e);
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
55
73
|
|
|
56
74
|
// MARK: - Pick Files
|
|
57
75
|
|
|
58
76
|
@PluginMethod
|
|
59
77
|
public void pickFiles(PluginCall call) {
|
|
60
|
-
readData = call.getBoolean("readData", false);
|
|
61
78
|
int limit = call.getInt("limit", 0);
|
|
62
79
|
|
|
63
80
|
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
|
|
@@ -86,53 +103,49 @@ public class CapgoFilePickerPlugin extends Plugin {
|
|
|
86
103
|
intent.addCategory(Intent.CATEGORY_OPENABLE);
|
|
87
104
|
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, limit != 1);
|
|
88
105
|
|
|
89
|
-
|
|
106
|
+
enqueuePickerLaunch(call, intent, "pickFilesResult", false);
|
|
90
107
|
}
|
|
91
108
|
|
|
92
109
|
// MARK: - Pick Images
|
|
93
110
|
|
|
94
111
|
@PluginMethod
|
|
95
112
|
public void pickImages(PluginCall call) {
|
|
96
|
-
readData = call.getBoolean("readData", false);
|
|
97
113
|
int limit = call.getInt("limit", 0);
|
|
114
|
+
boolean photoPickerAvailable = MediaPickerIntents.isPhotoPickerAvailable();
|
|
115
|
+
boolean truncateResults = MediaPickerIntents.shouldTruncateResultsToLimit(photoPickerAvailable);
|
|
98
116
|
|
|
99
|
-
Intent intent =
|
|
100
|
-
intent
|
|
101
|
-
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, limit != 1);
|
|
102
|
-
|
|
103
|
-
startActivityForResult(call, intent, "pickMediaResult");
|
|
117
|
+
Intent intent = MediaPickerIntents.buildPickImagesIntent(limit, photoPickerAvailable);
|
|
118
|
+
enqueuePickerLaunch(call, intent, "pickMediaResult", truncateResults);
|
|
104
119
|
}
|
|
105
120
|
|
|
106
121
|
// MARK: - Pick Videos
|
|
107
122
|
|
|
108
123
|
@PluginMethod
|
|
109
124
|
public void pickVideos(PluginCall call) {
|
|
110
|
-
readData = call.getBoolean("readData", false);
|
|
111
125
|
int limit = call.getInt("limit", 0);
|
|
126
|
+
boolean photoPickerAvailable = MediaPickerIntents.isPhotoPickerAvailable();
|
|
127
|
+
boolean truncateResults = MediaPickerIntents.shouldTruncateResultsToLimit(photoPickerAvailable);
|
|
112
128
|
|
|
113
|
-
Intent intent =
|
|
114
|
-
intent
|
|
115
|
-
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, limit != 1);
|
|
116
|
-
|
|
117
|
-
startActivityForResult(call, intent, "pickMediaResult");
|
|
129
|
+
Intent intent = MediaPickerIntents.buildPickVideosIntent(limit, photoPickerAvailable);
|
|
130
|
+
enqueuePickerLaunch(call, intent, "pickMediaResult", truncateResults);
|
|
118
131
|
}
|
|
119
132
|
|
|
120
133
|
// MARK: - Pick Media
|
|
121
134
|
|
|
122
135
|
@PluginMethod
|
|
123
136
|
public void pickMedia(PluginCall call) {
|
|
124
|
-
readData = call.getBoolean("readData", false);
|
|
125
137
|
int limit = call.getInt("limit", 0);
|
|
126
138
|
|
|
127
139
|
Intent intent;
|
|
128
|
-
|
|
140
|
+
boolean photoPickerAvailable = MediaPickerIntents.isPhotoPickerAvailable();
|
|
141
|
+
boolean truncateResults = false;
|
|
142
|
+
if (photoPickerAvailable) {
|
|
129
143
|
intent = new Intent(MediaStore.ACTION_PICK_IMAGES);
|
|
130
144
|
intent.setType("*/*");
|
|
131
145
|
intent.putExtra(Intent.EXTRA_MIME_TYPES, new String[] { "image/*", "video/*" });
|
|
132
|
-
|
|
133
|
-
intent.putExtra(MediaStore.EXTRA_PICK_IMAGES_MAX, limit);
|
|
134
|
-
}
|
|
146
|
+
MediaPickerIntents.applyPhotoPickerMaxLimit(intent, limit, MediaStore.getPickImagesMaxLimit());
|
|
135
147
|
} else {
|
|
148
|
+
truncateResults = true;
|
|
136
149
|
intent = new Intent(Intent.ACTION_GET_CONTENT);
|
|
137
150
|
intent.setType("*/*");
|
|
138
151
|
intent.putExtra(Intent.EXTRA_MIME_TYPES, new String[] { "image/*", "video/*" });
|
|
@@ -140,7 +153,7 @@ public class CapgoFilePickerPlugin extends Plugin {
|
|
|
140
153
|
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, limit != 1);
|
|
141
154
|
}
|
|
142
155
|
|
|
143
|
-
|
|
156
|
+
enqueuePickerLaunch(call, intent, "pickMediaResult", truncateResults);
|
|
144
157
|
}
|
|
145
158
|
|
|
146
159
|
// MARK: - Pick Directory
|
|
@@ -150,7 +163,7 @@ public class CapgoFilePickerPlugin extends Plugin {
|
|
|
150
163
|
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
|
|
151
164
|
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
|
|
152
165
|
|
|
153
|
-
|
|
166
|
+
enqueuePickerLaunch(call, intent, "pickDirectoryResult", false);
|
|
154
167
|
}
|
|
155
168
|
|
|
156
169
|
// MARK: - Convert HEIC to JPEG
|
|
@@ -253,89 +266,123 @@ public class CapgoFilePickerPlugin extends Plugin {
|
|
|
253
266
|
|
|
254
267
|
@ActivityCallback
|
|
255
268
|
private void pickFilesResult(PluginCall call, ActivityResult result) {
|
|
256
|
-
handleFilePickerResult(call, result);
|
|
269
|
+
handleFilePickerResult(call, result, false);
|
|
257
270
|
}
|
|
258
271
|
|
|
259
272
|
@ActivityCallback
|
|
260
273
|
private void pickMediaResult(PluginCall call, ActivityResult result) {
|
|
261
|
-
handleFilePickerResult(call, result);
|
|
274
|
+
handleFilePickerResult(call, result, truncateResultsForCall(call));
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
private boolean truncateResultsForCall(PluginCall call) {
|
|
278
|
+
return pickerLaunchQueue.isTruncateResultsForCall(call);
|
|
262
279
|
}
|
|
263
280
|
|
|
264
|
-
private void handleFilePickerResult(PluginCall call, ActivityResult result) {
|
|
281
|
+
private void handleFilePickerResult(PluginCall call, ActivityResult result, boolean truncateResultsToLimit) {
|
|
265
282
|
if (call == null) {
|
|
283
|
+
completeActivePickerLaunch();
|
|
266
284
|
return;
|
|
267
285
|
}
|
|
268
286
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
287
|
+
try {
|
|
288
|
+
if (result.getResultCode() != Activity.RESULT_OK) {
|
|
289
|
+
notifyListeners("pickerDismissed", new JSObject());
|
|
290
|
+
JSObject ret = new JSObject();
|
|
291
|
+
ret.put("files", new JSArray());
|
|
292
|
+
call.resolve(ret);
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
276
295
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
296
|
+
Intent data = result.getData();
|
|
297
|
+
if (data == null) {
|
|
298
|
+
JSObject ret = new JSObject();
|
|
299
|
+
ret.put("files", new JSArray());
|
|
300
|
+
call.resolve(ret);
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
284
303
|
|
|
285
|
-
|
|
304
|
+
List<Uri> uris = new ArrayList<>();
|
|
286
305
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
306
|
+
ClipData clipData = data.getClipData();
|
|
307
|
+
if (clipData != null) {
|
|
308
|
+
for (int i = 0; i < clipData.getItemCount(); i++) {
|
|
309
|
+
uris.add(clipData.getItemAt(i).getUri());
|
|
310
|
+
}
|
|
311
|
+
} else if (data.getData() != null) {
|
|
312
|
+
uris.add(data.getData());
|
|
291
313
|
}
|
|
292
|
-
} else if (data.getData() != null) {
|
|
293
|
-
uris.add(data.getData());
|
|
294
|
-
}
|
|
295
314
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
if (fileInfo != null) {
|
|
300
|
-
files.put(fileInfo);
|
|
315
|
+
if (truncateResultsToLimit) {
|
|
316
|
+
int limit = call.getInt("limit", 0);
|
|
317
|
+
uris = MediaPickerIntents.truncateUris(uris, limit);
|
|
301
318
|
}
|
|
302
|
-
}
|
|
303
319
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
320
|
+
boolean readData = call.getBoolean("readData", false);
|
|
321
|
+
JSArray files = new JSArray();
|
|
322
|
+
for (Uri uri : uris) {
|
|
323
|
+
JSObject fileInfo = getFileInfo(uri, readData);
|
|
324
|
+
if (fileInfo != null) {
|
|
325
|
+
files.put(fileInfo);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
JSObject ret = new JSObject();
|
|
330
|
+
ret.put("files", files);
|
|
331
|
+
call.resolve(ret);
|
|
332
|
+
} finally {
|
|
333
|
+
completeActivePickerLaunch();
|
|
334
|
+
}
|
|
307
335
|
}
|
|
308
336
|
|
|
309
337
|
@ActivityCallback
|
|
310
338
|
private void pickDirectoryResult(PluginCall call, ActivityResult result) {
|
|
311
339
|
if (call == null) {
|
|
340
|
+
completeActivePickerLaunch();
|
|
312
341
|
return;
|
|
313
342
|
}
|
|
314
343
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
344
|
+
try {
|
|
345
|
+
if (result.getResultCode() != Activity.RESULT_OK) {
|
|
346
|
+
notifyListeners("pickerDismissed", new JSObject());
|
|
347
|
+
call.reject("User cancelled");
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
320
350
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
351
|
+
Intent data = result.getData();
|
|
352
|
+
if (data == null || data.getData() == null) {
|
|
353
|
+
call.reject("No directory selected");
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
Uri uri = data.getData();
|
|
358
|
+
String path = getPathFromUri(uri);
|
|
359
|
+
|
|
360
|
+
JSObject ret = new JSObject();
|
|
361
|
+
ret.put("path", path != null ? path : uri.toString());
|
|
362
|
+
call.resolve(ret);
|
|
363
|
+
} finally {
|
|
364
|
+
completeActivePickerLaunch();
|
|
325
365
|
}
|
|
366
|
+
}
|
|
326
367
|
|
|
327
|
-
|
|
328
|
-
|
|
368
|
+
private void enqueuePickerLaunch(PluginCall call, Intent intent, String callbackName, boolean truncateResultsToLimit) {
|
|
369
|
+
PickerLaunchQueue.PendingPickerLaunch launch = new PickerLaunchQueue.PendingPickerLaunch(
|
|
370
|
+
call,
|
|
371
|
+
intent,
|
|
372
|
+
callbackName,
|
|
373
|
+
truncateResultsToLimit
|
|
374
|
+
);
|
|
375
|
+
pickerLaunchQueue.enqueue(launch);
|
|
376
|
+
}
|
|
329
377
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
call.resolve(ret);
|
|
378
|
+
private void completeActivePickerLaunch() {
|
|
379
|
+
pickerLaunchQueue.completeActive();
|
|
333
380
|
}
|
|
334
381
|
|
|
335
382
|
// MARK: - Helpers
|
|
336
383
|
|
|
337
384
|
@Nullable
|
|
338
|
-
private JSObject getFileInfo(Uri uri) {
|
|
385
|
+
private JSObject getFileInfo(Uri uri, boolean readData) {
|
|
339
386
|
try {
|
|
340
387
|
JSObject result = new JSObject();
|
|
341
388
|
|
|
@@ -415,7 +462,6 @@ public class CapgoFilePickerPlugin extends Plugin {
|
|
|
415
462
|
}
|
|
416
463
|
}
|
|
417
464
|
|
|
418
|
-
// Read data if requested
|
|
419
465
|
if (readData) {
|
|
420
466
|
try (InputStream is = getContext().getContentResolver().openInputStream(uri)) {
|
|
421
467
|
if (is != null) {
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
package app.capgo.capacitor.filepicker;
|
|
2
|
+
|
|
3
|
+
import android.content.Intent;
|
|
4
|
+
import android.net.Uri;
|
|
5
|
+
import android.os.Build;
|
|
6
|
+
import android.os.ext.SdkExtensions;
|
|
7
|
+
import android.provider.MediaStore;
|
|
8
|
+
import java.util.ArrayList;
|
|
9
|
+
import java.util.List;
|
|
10
|
+
|
|
11
|
+
/** Builds gallery picker intents and applies selection limits on Android. */
|
|
12
|
+
final class MediaPickerIntents {
|
|
13
|
+
|
|
14
|
+
private MediaPickerIntents() {}
|
|
15
|
+
|
|
16
|
+
static boolean isPhotoPickerAvailable() {
|
|
17
|
+
return isPhotoPickerAvailable(Build.VERSION.SDK_INT, getRExtensionVersion());
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
static boolean isPhotoPickerAvailable(int sdkInt, int rExtensionVersion) {
|
|
21
|
+
if (sdkInt >= Build.VERSION_CODES.TIRAMISU) {
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
if (sdkInt >= Build.VERSION_CODES.R) {
|
|
25
|
+
return rExtensionVersion >= 2;
|
|
26
|
+
}
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
private static int getRExtensionVersion() {
|
|
31
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
|
32
|
+
return SdkExtensions.getExtensionVersion(Build.VERSION_CODES.R);
|
|
33
|
+
}
|
|
34
|
+
return 0;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
static Intent buildPickImagesIntent(int limit, boolean photoPickerAvailable) {
|
|
38
|
+
return buildGalleryIntent("image/*", MediaStore.Images.Media.EXTERNAL_CONTENT_URI, limit, photoPickerAvailable);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
static Intent buildPickVideosIntent(int limit, boolean photoPickerAvailable) {
|
|
42
|
+
return buildGalleryIntent("video/*", MediaStore.Video.Media.EXTERNAL_CONTENT_URI, limit, photoPickerAvailable);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
static int resolvePhotoPickerMaxLimit(int limit, int pickImagesMaxLimit) {
|
|
46
|
+
if (limit <= 0) {
|
|
47
|
+
return pickImagesMaxLimit > 0 ? pickImagesMaxLimit : limit;
|
|
48
|
+
}
|
|
49
|
+
return pickImagesMaxLimit > 0 ? Math.min(limit, pickImagesMaxLimit) : limit;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
static void applyPhotoPickerMaxLimit(Intent intent, int limit, int pickImagesMaxLimit) {
|
|
53
|
+
if (limit == 1) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
intent.putExtra(MediaStore.EXTRA_PICK_IMAGES_MAX, resolvePhotoPickerMaxLimit(limit, pickImagesMaxLimit));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
static Intent buildGalleryIntent(String mimeType, Uri legacyPickUri, int limit, boolean photoPickerAvailable) {
|
|
60
|
+
return buildGalleryIntent(mimeType, legacyPickUri, limit, photoPickerAvailable, MediaStore.getPickImagesMaxLimit());
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
static Intent buildGalleryIntent(String mimeType, Uri legacyPickUri, int limit, boolean photoPickerAvailable, int pickImagesMaxLimit) {
|
|
64
|
+
if (photoPickerAvailable) {
|
|
65
|
+
Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES);
|
|
66
|
+
intent.setType(mimeType);
|
|
67
|
+
applyPhotoPickerMaxLimit(intent, limit, pickImagesMaxLimit);
|
|
68
|
+
return intent;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
Intent intent = new Intent(Intent.ACTION_PICK, legacyPickUri);
|
|
72
|
+
intent.setType(mimeType);
|
|
73
|
+
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, limit != 1);
|
|
74
|
+
return intent;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
static boolean shouldTruncateResultsToLimit(boolean photoPickerAvailable) {
|
|
78
|
+
return !photoPickerAvailable;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
static List<Uri> truncateUris(List<Uri> uris, int limit) {
|
|
82
|
+
if (limit <= 0 || uris.size() <= limit) {
|
|
83
|
+
return uris;
|
|
84
|
+
}
|
|
85
|
+
return new ArrayList<>(uris.subList(0, limit));
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
package app.capgo.capacitor.filepicker;
|
|
2
|
+
|
|
3
|
+
import com.getcapacitor.PluginCall;
|
|
4
|
+
import java.util.ArrayDeque;
|
|
5
|
+
import java.util.Queue;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Serializes picker activity launches so only one Capacitor saved activity call is active.
|
|
9
|
+
* All queue mutations and launches run on the main thread.
|
|
10
|
+
*/
|
|
11
|
+
final class PickerLaunchQueue {
|
|
12
|
+
|
|
13
|
+
static final class PendingPickerLaunch {
|
|
14
|
+
|
|
15
|
+
final PluginCall call;
|
|
16
|
+
final IntentHolder intent;
|
|
17
|
+
final String callbackName;
|
|
18
|
+
final boolean truncateResultsToLimit;
|
|
19
|
+
|
|
20
|
+
PendingPickerLaunch(PluginCall call, android.content.Intent intent, String callbackName, boolean truncateResultsToLimit) {
|
|
21
|
+
this.call = call;
|
|
22
|
+
this.intent = new IntentHolder(intent);
|
|
23
|
+
this.callbackName = callbackName;
|
|
24
|
+
this.truncateResultsToLimit = truncateResultsToLimit;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
android.content.Intent getIntent() {
|
|
28
|
+
return intent.get();
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Holds a copy of the intent so queued launches are not affected if callers mutate the original.
|
|
34
|
+
*/
|
|
35
|
+
private static final class IntentHolder {
|
|
36
|
+
|
|
37
|
+
private final android.content.Intent intent;
|
|
38
|
+
|
|
39
|
+
IntentHolder(android.content.Intent intent) {
|
|
40
|
+
this.intent = new android.content.Intent(intent);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
android.content.Intent get() {
|
|
44
|
+
return intent;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
interface MainThreadScheduler {
|
|
49
|
+
void run(Runnable runnable);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* @return true when the activity was launched and the call is retained for the result callback
|
|
54
|
+
*/
|
|
55
|
+
interface ActivityLauncher {
|
|
56
|
+
boolean launch(PendingPickerLaunch launch);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
private final MainThreadScheduler mainThreadScheduler;
|
|
60
|
+
private final ActivityLauncher activityLauncher;
|
|
61
|
+
private final Queue<PendingPickerLaunch> pendingPickerLaunches = new ArrayDeque<>();
|
|
62
|
+
private PendingPickerLaunch activePickerLaunch;
|
|
63
|
+
|
|
64
|
+
PickerLaunchQueue(MainThreadScheduler mainThreadScheduler, ActivityLauncher activityLauncher) {
|
|
65
|
+
this.mainThreadScheduler = mainThreadScheduler;
|
|
66
|
+
this.activityLauncher = activityLauncher;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
void enqueue(PendingPickerLaunch launch) {
|
|
70
|
+
mainThreadScheduler.run(() -> enqueueOnMainThread(launch));
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
void completeActive() {
|
|
74
|
+
mainThreadScheduler.run(this::completeActiveOnMainThread);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
PendingPickerLaunch getActiveLaunch() {
|
|
78
|
+
return activePickerLaunch;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
boolean isTruncateResultsForCall(PluginCall call) {
|
|
82
|
+
PendingPickerLaunch active = activePickerLaunch;
|
|
83
|
+
if (active != null && active.call == call) {
|
|
84
|
+
return active.truncateResultsToLimit;
|
|
85
|
+
}
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
private void enqueueOnMainThread(PendingPickerLaunch launch) {
|
|
90
|
+
if (activePickerLaunch != null) {
|
|
91
|
+
pendingPickerLaunches.add(launch);
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
startPickerLaunch(launch);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
private void completeActiveOnMainThread() {
|
|
98
|
+
activePickerLaunch = null;
|
|
99
|
+
startNextQueuedLaunch();
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
private void startNextQueuedLaunch() {
|
|
103
|
+
PendingPickerLaunch next = pendingPickerLaunches.poll();
|
|
104
|
+
if (next != null) {
|
|
105
|
+
startPickerLaunch(next);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
private void startPickerLaunch(PendingPickerLaunch launch) {
|
|
110
|
+
boolean launched;
|
|
111
|
+
try {
|
|
112
|
+
launched = activityLauncher.launch(launch);
|
|
113
|
+
} catch (RuntimeException e) {
|
|
114
|
+
launch.call.reject("Unable to open file picker", e);
|
|
115
|
+
startNextQueuedLaunch();
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (!launched) {
|
|
120
|
+
startNextQueuedLaunch();
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
activePickerLaunch = launch;
|
|
125
|
+
}
|
|
126
|
+
}
|
package/dist/docs.json
CHANGED
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
"text": "```typescript\nconst result = await CapgoFilePicker.pickImages({\n limit: 10,\n readData: false\n});\nconsole.log('Picked images:', result.files);\n```"
|
|
76
76
|
}
|
|
77
77
|
],
|
|
78
|
-
"docs": "Pick one or more images from the gallery.\nAndroid/iOS only.",
|
|
78
|
+
"docs": "Pick one or more images from the gallery.\nAndroid/iOS only.\nOn Android, `limit` is enforced by the system photo picker when available\n(Android 13+, or Android 11-12 with the Google Play system photo picker extension).\nOlder Android versions may allow selecting more items than `limit`; results are\ntruncated to `limit` before they are returned.",
|
|
79
79
|
"complexTypes": [
|
|
80
80
|
"PickFilesResult",
|
|
81
81
|
"PickMediaOptions"
|
|
@@ -111,7 +111,7 @@
|
|
|
111
111
|
"text": "```typescript\nconst result = await CapgoFilePicker.pickVideos({\n limit: 3,\n skipTranscoding: true\n});\nconsole.log('Picked videos:', result.files);\n```"
|
|
112
112
|
}
|
|
113
113
|
],
|
|
114
|
-
"docs": "Pick one or more videos from the gallery.\nAndroid/iOS only.",
|
|
114
|
+
"docs": "Pick one or more videos from the gallery.\nAndroid/iOS only.\nOn Android, `limit` is enforced by the system photo picker when available\n(Android 13+, or Android 11-12 with the Google Play system photo picker extension).\nOlder Android versions may allow selecting more items than `limit`; results are\ntruncated to `limit` before they are returned.",
|
|
115
115
|
"complexTypes": [
|
|
116
116
|
"PickFilesResult",
|
|
117
117
|
"PickMediaOptions"
|
|
@@ -557,7 +557,7 @@
|
|
|
557
557
|
"name": "default"
|
|
558
558
|
}
|
|
559
559
|
],
|
|
560
|
-
"docs": "Maximum number of files to pick.\nSet to 0 for unlimited (platform default).",
|
|
560
|
+
"docs": "Maximum number of files to pick.\nSet to 0 for unlimited (platform default).\nOn Android, when the system photo picker is available, the platform enforces this\nlimit (capped by the device maximum). On older Android versions the picker may not\nenforce the limit; the plugin truncates the returned list to this value when needed.",
|
|
561
561
|
"complexTypes": [],
|
|
562
562
|
"type": "number | undefined"
|
|
563
563
|
},
|
|
@@ -33,6 +33,9 @@ export interface PickMediaOptions {
|
|
|
33
33
|
/**
|
|
34
34
|
* Maximum number of files to pick.
|
|
35
35
|
* Set to 0 for unlimited (platform default).
|
|
36
|
+
* On Android, when the system photo picker is available, the platform enforces this
|
|
37
|
+
* limit (capped by the device maximum). On older Android versions the picker may not
|
|
38
|
+
* enforce the limit; the plugin truncates the returned list to this value when needed.
|
|
36
39
|
* @default 0
|
|
37
40
|
*/
|
|
38
41
|
limit?: number;
|
|
@@ -195,6 +198,10 @@ export interface CapgoFilePickerPlugin {
|
|
|
195
198
|
/**
|
|
196
199
|
* Pick one or more images from the gallery.
|
|
197
200
|
* Android/iOS only.
|
|
201
|
+
* On Android, `limit` is enforced by the system photo picker when available
|
|
202
|
+
* (Android 13+, or Android 11-12 with the Google Play system photo picker extension).
|
|
203
|
+
* Older Android versions may allow selecting more items than `limit`; results are
|
|
204
|
+
* truncated to `limit` before they are returned.
|
|
198
205
|
*
|
|
199
206
|
* @param options - Options for picking images
|
|
200
207
|
* @returns Promise that resolves with the picked images
|
|
@@ -212,6 +219,10 @@ export interface CapgoFilePickerPlugin {
|
|
|
212
219
|
/**
|
|
213
220
|
* Pick one or more videos from the gallery.
|
|
214
221
|
* Android/iOS only.
|
|
222
|
+
* On Android, `limit` is enforced by the system photo picker when available
|
|
223
|
+
* (Android 13+, or Android 11-12 with the Google Play system photo picker extension).
|
|
224
|
+
* Older Android versions may allow selecting more items than `limit`; results are
|
|
225
|
+
* truncated to `limit` before they are returned.
|
|
215
226
|
*
|
|
216
227
|
* @param options - Options for picking videos
|
|
217
228
|
* @returns Promise that resolves with the picked videos
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"","sourcesContent":["import type { PluginListenerHandle } from '@capacitor/core';\n\n/**\n * Options for picking files.\n *\n * @since 8.0.0\n */\nexport interface PickFilesOptions {\n /**\n * List of accepted MIME types or file extensions.\n * On iOS, only MIME types are supported.\n * Examples: ['image/*'], ['application/pdf'], ['.pdf', '.doc']\n */\n types?: string[];\n /**\n * Maximum number of files to pick.\n * Set to 0 for unlimited (platform default).\n * @default 0\n */\n limit?: number;\n /**\n * Whether to read the file data as base64.\n * Note: Reading large files may cause memory issues.\n * @default false\n */\n readData?: boolean;\n}\n\n/**\n * Options for picking media (images/videos).\n *\n * @since 8.0.0\n */\nexport interface PickMediaOptions {\n /**\n * Maximum number of files to pick.\n * Set to 0 for unlimited (platform default).\n * @default 0\n */\n limit?: number;\n /**\n * Whether to read the file data as base64.\n * Note: Reading large files may cause memory issues.\n * @default false\n */\n readData?: boolean;\n /**\n * iOS only: Skip transcoding of videos.\n * @default false\n */\n skipTranscoding?: boolean;\n /**\n * iOS 15+ only: Show ordered selection badges.\n * @default false\n */\n ordered?: boolean;\n}\n\n/**\n * Options for converting HEIC to JPEG.\n *\n * @since 8.0.0\n */\nexport interface ConvertHeicToJpegOptions {\n /** The path to the HEIC file to convert */\n path: string;\n /**\n * The compression quality for JPEG (0.0 - 1.0).\n * @default 0.9\n */\n quality?: number;\n}\n\n/**\n * Result of HEIC to JPEG conversion.\n *\n * @since 8.0.0\n */\nexport interface ConvertHeicToJpegResult {\n /** The path to the converted JPEG file */\n path: string;\n}\n\n/**\n * Options for copying a file.\n *\n * @since 8.0.0\n */\nexport interface CopyFileOptions {\n /** Source file path */\n from: string;\n /** Destination file path */\n to: string;\n /**\n * Whether to overwrite if destination exists.\n * @default false\n */\n overwrite?: boolean;\n}\n\n/**\n * Represents a picked file.\n *\n * @since 8.0.0\n */\nexport interface PickedFile {\n /** The name of the file */\n name: string;\n /** The path to the file */\n path?: string;\n /** The MIME type of the file */\n mimeType: string;\n /** The size of the file in bytes */\n size: number;\n /**\n * The base64 encoded data of the file.\n * Only present if readData was true.\n */\n data?: string;\n /**\n * The Blob instance of the file.\n * Web only.\n */\n blob?: Blob;\n /** Width in pixels (images/videos only) */\n width?: number;\n /** Height in pixels (images/videos only) */\n height?: number;\n /** Duration in seconds (videos only) */\n duration?: number;\n /** Last modified timestamp in milliseconds */\n modifiedAt?: number;\n}\n\n/**\n * Result of picking files.\n *\n * @since 8.0.0\n */\nexport interface PickFilesResult {\n /** Array of picked files */\n files: PickedFile[];\n}\n\n/**\n * Result of picking a directory.\n *\n * @since 8.0.0\n */\nexport interface PickDirectoryResult {\n /** The path to the selected directory */\n path: string;\n}\n\n/**\n * Permission status for file access.\n *\n * @since 8.0.0\n */\nexport interface PermissionStatus {\n /**\n * Whether broad external storage or media permission is granted.\n * Picker APIs that return user-selected files usually do not require this on Android 13+.\n */\n readExternalStorage: PermissionState;\n /** Whether permission to access media location is granted */\n accessMediaLocation?: PermissionState;\n}\n\n/**\n * Permission state values.\n *\n * @since 8.0.0\n */\nexport type PermissionState = 'prompt' | 'prompt-with-rationale' | 'granted' | 'denied';\n\n/**\n * Listener callback for picker dismissed event.\n *\n * @since 8.0.0\n */\nexport type PickerDismissedListener = (event: null) => void;\n\n/**\n * Capacitor File Picker Plugin interface for selecting files, images, videos, and directories.\n *\n * @since 8.0.0\n */\nexport interface CapgoFilePickerPlugin {\n /**\n * Pick one or more files from the device.\n *\n * @param options - Options for picking files\n * @returns Promise that resolves with the picked files\n * @since 8.0.0\n * @example\n * ```typescript\n * const result = await CapgoFilePicker.pickFiles({\n * types: ['application/pdf', 'image/*'],\n * limit: 5,\n * readData: false\n * });\n * console.log('Picked files:', result.files);\n * ```\n */\n pickFiles(options?: PickFilesOptions): Promise<PickFilesResult>;\n\n /**\n * Pick one or more images from the gallery.\n * Android/iOS only.\n *\n * @param options - Options for picking images\n * @returns Promise that resolves with the picked images\n * @since 8.0.0\n * @example\n * ```typescript\n * const result = await CapgoFilePicker.pickImages({\n * limit: 10,\n * readData: false\n * });\n * console.log('Picked images:', result.files);\n * ```\n */\n pickImages(options?: PickMediaOptions): Promise<PickFilesResult>;\n\n /**\n * Pick one or more videos from the gallery.\n * Android/iOS only.\n *\n * @param options - Options for picking videos\n * @returns Promise that resolves with the picked videos\n * @since 8.0.0\n * @example\n * ```typescript\n * const result = await CapgoFilePicker.pickVideos({\n * limit: 3,\n * skipTranscoding: true\n * });\n * console.log('Picked videos:', result.files);\n * ```\n */\n pickVideos(options?: PickMediaOptions): Promise<PickFilesResult>;\n\n /**\n * Pick one or more images or videos from the gallery.\n * Android/iOS only.\n *\n * @param options - Options for picking media\n * @returns Promise that resolves with the picked media files\n * @since 8.0.0\n * @example\n * ```typescript\n * const result = await CapgoFilePicker.pickMedia({\n * limit: 5,\n * readData: true\n * });\n * console.log('Picked media:', result.files);\n * ```\n */\n pickMedia(options?: PickMediaOptions): Promise<PickFilesResult>;\n\n /**\n * Pick a directory from the device.\n * Android/iOS only.\n *\n * @returns Promise that resolves with the picked directory path\n * @since 8.0.0\n * @example\n * ```typescript\n * const result = await CapgoFilePicker.pickDirectory();\n * console.log('Selected directory:', result.path);\n * ```\n */\n pickDirectory(): Promise<PickDirectoryResult>;\n\n /**\n * Convert a HEIC image to JPEG format.\n * iOS only.\n *\n * @param options - Options for the conversion\n * @returns Promise that resolves with the converted file path\n * @since 8.0.0\n * @example\n * ```typescript\n * const result = await CapgoFilePicker.convertHeicToJpeg({\n * path: '/path/to/image.heic',\n * quality: 0.9\n * });\n * console.log('Converted file:', result.path);\n * ```\n */\n convertHeicToJpeg(options: ConvertHeicToJpegOptions): Promise<ConvertHeicToJpegResult>;\n\n /**\n * Copy a file to a new location.\n *\n * @param options - Options for copying the file\n * @returns Promise that resolves when the copy is complete\n * @since 8.0.0\n * @example\n * ```typescript\n * await CapgoFilePicker.copyFile({\n * from: '/source/file.pdf',\n * to: '/destination/file.pdf',\n * overwrite: true\n * });\n * ```\n */\n copyFile(options: CopyFileOptions): Promise<void>;\n\n /**\n * Check broad storage or media permission state.\n * Picker-only flows should not gate `pickFiles()`, `pickImages()`,\n * `pickVideos()`, or `pickMedia()` on this permission on Android 13+.\n * Android only.\n *\n * @returns Promise that resolves with the permission status\n * @since 8.0.0\n * @example\n * ```typescript\n * const status = await CapgoFilePicker.checkPermissions();\n * console.log('Read permission:', status.readExternalStorage);\n * ```\n */\n checkPermissions(): Promise<PermissionStatus>;\n\n /**\n * Request broad storage or media permissions.\n * Do not request or declare `READ_MEDIA_IMAGES` or `READ_MEDIA_VIDEO`\n * only to use picker APIs. Google Play allows these permissions only\n * when picker alternatives are not sufficient for core app functionality.\n * Use `@capgo/capacitor-file-picker` picker methods instead for\n * user-selected file, image, or video access.\n * Android only.\n *\n * @returns Promise that resolves with the permission status\n * @since 8.0.0\n * @example\n * ```typescript\n * const status = await CapgoFilePicker.requestPermissions();\n * if (status.readExternalStorage === 'granted') {\n * console.log('Permission granted');\n * }\n * ```\n */\n requestPermissions(): Promise<PermissionStatus>;\n\n /**\n * Add a listener for the picker dismissed event.\n * iOS only.\n *\n * @param eventName - The event name\n * @param listenerFunc - The listener function\n * @returns Promise that resolves with the listener handle\n * @since 8.0.0\n */\n addListener(eventName: 'pickerDismissed', listenerFunc: PickerDismissedListener): Promise<PluginListenerHandle>;\n\n /**\n * Remove all listeners for this plugin.\n *\n * @since 8.0.0\n */\n removeAllListeners(): Promise<void>;\n\n /**\n * Get the native Capacitor plugin version.\n *\n * @returns Promise that resolves with the plugin version\n * @since 8.0.0\n * @example\n * ```typescript\n * const { version } = await CapgoFilePicker.getPluginVersion();\n * console.log('Plugin version:', version);\n * ```\n */\n getPluginVersion(): Promise<{ version: string }>;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"","sourcesContent":["import type { PluginListenerHandle } from '@capacitor/core';\n\n/**\n * Options for picking files.\n *\n * @since 8.0.0\n */\nexport interface PickFilesOptions {\n /**\n * List of accepted MIME types or file extensions.\n * On iOS, only MIME types are supported.\n * Examples: ['image/*'], ['application/pdf'], ['.pdf', '.doc']\n */\n types?: string[];\n /**\n * Maximum number of files to pick.\n * Set to 0 for unlimited (platform default).\n * @default 0\n */\n limit?: number;\n /**\n * Whether to read the file data as base64.\n * Note: Reading large files may cause memory issues.\n * @default false\n */\n readData?: boolean;\n}\n\n/**\n * Options for picking media (images/videos).\n *\n * @since 8.0.0\n */\nexport interface PickMediaOptions {\n /**\n * Maximum number of files to pick.\n * Set to 0 for unlimited (platform default).\n * On Android, when the system photo picker is available, the platform enforces this\n * limit (capped by the device maximum). On older Android versions the picker may not\n * enforce the limit; the plugin truncates the returned list to this value when needed.\n * @default 0\n */\n limit?: number;\n /**\n * Whether to read the file data as base64.\n * Note: Reading large files may cause memory issues.\n * @default false\n */\n readData?: boolean;\n /**\n * iOS only: Skip transcoding of videos.\n * @default false\n */\n skipTranscoding?: boolean;\n /**\n * iOS 15+ only: Show ordered selection badges.\n * @default false\n */\n ordered?: boolean;\n}\n\n/**\n * Options for converting HEIC to JPEG.\n *\n * @since 8.0.0\n */\nexport interface ConvertHeicToJpegOptions {\n /** The path to the HEIC file to convert */\n path: string;\n /**\n * The compression quality for JPEG (0.0 - 1.0).\n * @default 0.9\n */\n quality?: number;\n}\n\n/**\n * Result of HEIC to JPEG conversion.\n *\n * @since 8.0.0\n */\nexport interface ConvertHeicToJpegResult {\n /** The path to the converted JPEG file */\n path: string;\n}\n\n/**\n * Options for copying a file.\n *\n * @since 8.0.0\n */\nexport interface CopyFileOptions {\n /** Source file path */\n from: string;\n /** Destination file path */\n to: string;\n /**\n * Whether to overwrite if destination exists.\n * @default false\n */\n overwrite?: boolean;\n}\n\n/**\n * Represents a picked file.\n *\n * @since 8.0.0\n */\nexport interface PickedFile {\n /** The name of the file */\n name: string;\n /** The path to the file */\n path?: string;\n /** The MIME type of the file */\n mimeType: string;\n /** The size of the file in bytes */\n size: number;\n /**\n * The base64 encoded data of the file.\n * Only present if readData was true.\n */\n data?: string;\n /**\n * The Blob instance of the file.\n * Web only.\n */\n blob?: Blob;\n /** Width in pixels (images/videos only) */\n width?: number;\n /** Height in pixels (images/videos only) */\n height?: number;\n /** Duration in seconds (videos only) */\n duration?: number;\n /** Last modified timestamp in milliseconds */\n modifiedAt?: number;\n}\n\n/**\n * Result of picking files.\n *\n * @since 8.0.0\n */\nexport interface PickFilesResult {\n /** Array of picked files */\n files: PickedFile[];\n}\n\n/**\n * Result of picking a directory.\n *\n * @since 8.0.0\n */\nexport interface PickDirectoryResult {\n /** The path to the selected directory */\n path: string;\n}\n\n/**\n * Permission status for file access.\n *\n * @since 8.0.0\n */\nexport interface PermissionStatus {\n /**\n * Whether broad external storage or media permission is granted.\n * Picker APIs that return user-selected files usually do not require this on Android 13+.\n */\n readExternalStorage: PermissionState;\n /** Whether permission to access media location is granted */\n accessMediaLocation?: PermissionState;\n}\n\n/**\n * Permission state values.\n *\n * @since 8.0.0\n */\nexport type PermissionState = 'prompt' | 'prompt-with-rationale' | 'granted' | 'denied';\n\n/**\n * Listener callback for picker dismissed event.\n *\n * @since 8.0.0\n */\nexport type PickerDismissedListener = (event: null) => void;\n\n/**\n * Capacitor File Picker Plugin interface for selecting files, images, videos, and directories.\n *\n * @since 8.0.0\n */\nexport interface CapgoFilePickerPlugin {\n /**\n * Pick one or more files from the device.\n *\n * @param options - Options for picking files\n * @returns Promise that resolves with the picked files\n * @since 8.0.0\n * @example\n * ```typescript\n * const result = await CapgoFilePicker.pickFiles({\n * types: ['application/pdf', 'image/*'],\n * limit: 5,\n * readData: false\n * });\n * console.log('Picked files:', result.files);\n * ```\n */\n pickFiles(options?: PickFilesOptions): Promise<PickFilesResult>;\n\n /**\n * Pick one or more images from the gallery.\n * Android/iOS only.\n * On Android, `limit` is enforced by the system photo picker when available\n * (Android 13+, or Android 11-12 with the Google Play system photo picker extension).\n * Older Android versions may allow selecting more items than `limit`; results are\n * truncated to `limit` before they are returned.\n *\n * @param options - Options for picking images\n * @returns Promise that resolves with the picked images\n * @since 8.0.0\n * @example\n * ```typescript\n * const result = await CapgoFilePicker.pickImages({\n * limit: 10,\n * readData: false\n * });\n * console.log('Picked images:', result.files);\n * ```\n */\n pickImages(options?: PickMediaOptions): Promise<PickFilesResult>;\n\n /**\n * Pick one or more videos from the gallery.\n * Android/iOS only.\n * On Android, `limit` is enforced by the system photo picker when available\n * (Android 13+, or Android 11-12 with the Google Play system photo picker extension).\n * Older Android versions may allow selecting more items than `limit`; results are\n * truncated to `limit` before they are returned.\n *\n * @param options - Options for picking videos\n * @returns Promise that resolves with the picked videos\n * @since 8.0.0\n * @example\n * ```typescript\n * const result = await CapgoFilePicker.pickVideos({\n * limit: 3,\n * skipTranscoding: true\n * });\n * console.log('Picked videos:', result.files);\n * ```\n */\n pickVideos(options?: PickMediaOptions): Promise<PickFilesResult>;\n\n /**\n * Pick one or more images or videos from the gallery.\n * Android/iOS only.\n *\n * @param options - Options for picking media\n * @returns Promise that resolves with the picked media files\n * @since 8.0.0\n * @example\n * ```typescript\n * const result = await CapgoFilePicker.pickMedia({\n * limit: 5,\n * readData: true\n * });\n * console.log('Picked media:', result.files);\n * ```\n */\n pickMedia(options?: PickMediaOptions): Promise<PickFilesResult>;\n\n /**\n * Pick a directory from the device.\n * Android/iOS only.\n *\n * @returns Promise that resolves with the picked directory path\n * @since 8.0.0\n * @example\n * ```typescript\n * const result = await CapgoFilePicker.pickDirectory();\n * console.log('Selected directory:', result.path);\n * ```\n */\n pickDirectory(): Promise<PickDirectoryResult>;\n\n /**\n * Convert a HEIC image to JPEG format.\n * iOS only.\n *\n * @param options - Options for the conversion\n * @returns Promise that resolves with the converted file path\n * @since 8.0.0\n * @example\n * ```typescript\n * const result = await CapgoFilePicker.convertHeicToJpeg({\n * path: '/path/to/image.heic',\n * quality: 0.9\n * });\n * console.log('Converted file:', result.path);\n * ```\n */\n convertHeicToJpeg(options: ConvertHeicToJpegOptions): Promise<ConvertHeicToJpegResult>;\n\n /**\n * Copy a file to a new location.\n *\n * @param options - Options for copying the file\n * @returns Promise that resolves when the copy is complete\n * @since 8.0.0\n * @example\n * ```typescript\n * await CapgoFilePicker.copyFile({\n * from: '/source/file.pdf',\n * to: '/destination/file.pdf',\n * overwrite: true\n * });\n * ```\n */\n copyFile(options: CopyFileOptions): Promise<void>;\n\n /**\n * Check broad storage or media permission state.\n * Picker-only flows should not gate `pickFiles()`, `pickImages()`,\n * `pickVideos()`, or `pickMedia()` on this permission on Android 13+.\n * Android only.\n *\n * @returns Promise that resolves with the permission status\n * @since 8.0.0\n * @example\n * ```typescript\n * const status = await CapgoFilePicker.checkPermissions();\n * console.log('Read permission:', status.readExternalStorage);\n * ```\n */\n checkPermissions(): Promise<PermissionStatus>;\n\n /**\n * Request broad storage or media permissions.\n * Do not request or declare `READ_MEDIA_IMAGES` or `READ_MEDIA_VIDEO`\n * only to use picker APIs. Google Play allows these permissions only\n * when picker alternatives are not sufficient for core app functionality.\n * Use `@capgo/capacitor-file-picker` picker methods instead for\n * user-selected file, image, or video access.\n * Android only.\n *\n * @returns Promise that resolves with the permission status\n * @since 8.0.0\n * @example\n * ```typescript\n * const status = await CapgoFilePicker.requestPermissions();\n * if (status.readExternalStorage === 'granted') {\n * console.log('Permission granted');\n * }\n * ```\n */\n requestPermissions(): Promise<PermissionStatus>;\n\n /**\n * Add a listener for the picker dismissed event.\n * iOS only.\n *\n * @param eventName - The event name\n * @param listenerFunc - The listener function\n * @returns Promise that resolves with the listener handle\n * @since 8.0.0\n */\n addListener(eventName: 'pickerDismissed', listenerFunc: PickerDismissedListener): Promise<PluginListenerHandle>;\n\n /**\n * Remove all listeners for this plugin.\n *\n * @since 8.0.0\n */\n removeAllListeners(): Promise<void>;\n\n /**\n * Get the native Capacitor plugin version.\n *\n * @returns Promise that resolves with the plugin version\n * @since 8.0.0\n * @example\n * ```typescript\n * const { version } = await CapgoFilePicker.getPluginVersion();\n * console.log('Plugin version:', version);\n * ```\n */\n getPluginVersion(): Promise<{ version: string }>;\n}\n"]}
|
|
@@ -11,7 +11,7 @@ import UniformTypeIdentifiers
|
|
|
11
11
|
*/
|
|
12
12
|
@objc(CapgoFilePickerPlugin)
|
|
13
13
|
public class CapgoFilePickerPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
14
|
-
private let pluginVersion: String = "8.2.
|
|
14
|
+
private let pluginVersion: String = "8.2.8"
|
|
15
15
|
public let identifier = "CapgoFilePickerPlugin"
|
|
16
16
|
public let jsName = "CapgoFilePicker"
|
|
17
17
|
public let pluginMethods: [CAPPluginMethod] = [
|
package/package.json
CHANGED