@capgo/capacitor-file-picker 8.0.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/CapgoCapacitorFilePicker.podspec +17 -0
- package/Package.swift +28 -0
- package/README.md +403 -0
- package/android/build.gradle +58 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/java/app/capgo/capacitor/filepicker/CapgoFilePickerPlugin.java +473 -0
- package/dist/docs.json +804 -0
- package/dist/esm/definitions.d.ts +347 -0
- package/dist/esm/definitions.js +2 -0
- package/dist/esm/definitions.js.map +1 -0
- package/dist/esm/index.d.ts +4 -0
- package/dist/esm/index.js +7 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/web.d.ts +22 -0
- package/dist/esm/web.js +147 -0
- package/dist/esm/web.js.map +1 -0
- package/dist/plugin.cjs.js +161 -0
- package/dist/plugin.cjs.js.map +1 -0
- package/dist/plugin.js +164 -0
- package/dist/plugin.js.map +1 -0
- package/ios/Sources/CapgoFilePickerPlugin/CapgoFilePickerPlugin.swift +393 -0
- package/ios/Tests/CapgoFilePickerPluginTests/CapgoFilePickerPluginTests.swift +11 -0
- package/package.json +88 -0
|
@@ -0,0 +1,473 @@
|
|
|
1
|
+
package app.capgo.capacitor.filepicker;
|
|
2
|
+
|
|
3
|
+
import android.Manifest;
|
|
4
|
+
import android.app.Activity;
|
|
5
|
+
import android.content.ClipData;
|
|
6
|
+
import android.content.Intent;
|
|
7
|
+
import android.database.Cursor;
|
|
8
|
+
import android.graphics.BitmapFactory;
|
|
9
|
+
import android.media.MediaMetadataRetriever;
|
|
10
|
+
import android.net.Uri;
|
|
11
|
+
import android.os.Build;
|
|
12
|
+
import android.os.Environment;
|
|
13
|
+
import android.provider.DocumentsContract;
|
|
14
|
+
import android.provider.MediaStore;
|
|
15
|
+
import android.provider.OpenableColumns;
|
|
16
|
+
import android.util.Base64;
|
|
17
|
+
import android.webkit.MimeTypeMap;
|
|
18
|
+
import androidx.activity.result.ActivityResult;
|
|
19
|
+
import androidx.annotation.Nullable;
|
|
20
|
+
import com.getcapacitor.JSArray;
|
|
21
|
+
import com.getcapacitor.JSObject;
|
|
22
|
+
import com.getcapacitor.Plugin;
|
|
23
|
+
import com.getcapacitor.PluginCall;
|
|
24
|
+
import com.getcapacitor.PluginMethod;
|
|
25
|
+
import com.getcapacitor.annotation.ActivityCallback;
|
|
26
|
+
import com.getcapacitor.annotation.CapacitorPlugin;
|
|
27
|
+
import com.getcapacitor.annotation.Permission;
|
|
28
|
+
import com.getcapacitor.annotation.PermissionCallback;
|
|
29
|
+
import java.io.ByteArrayOutputStream;
|
|
30
|
+
import java.io.File;
|
|
31
|
+
import java.io.FileInputStream;
|
|
32
|
+
import java.io.FileOutputStream;
|
|
33
|
+
import java.io.InputStream;
|
|
34
|
+
import java.io.OutputStream;
|
|
35
|
+
import java.util.ArrayList;
|
|
36
|
+
import java.util.List;
|
|
37
|
+
import org.json.JSONException;
|
|
38
|
+
|
|
39
|
+
@CapacitorPlugin(
|
|
40
|
+
name = "CapgoFilePicker",
|
|
41
|
+
permissions = {
|
|
42
|
+
@Permission(
|
|
43
|
+
alias = "readExternalStorage",
|
|
44
|
+
strings = {
|
|
45
|
+
Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.READ_MEDIA_IMAGES, Manifest.permission.READ_MEDIA_VIDEO
|
|
46
|
+
}
|
|
47
|
+
),
|
|
48
|
+
@Permission(alias = "accessMediaLocation", strings = { Manifest.permission.ACCESS_MEDIA_LOCATION })
|
|
49
|
+
}
|
|
50
|
+
)
|
|
51
|
+
public class CapgoFilePickerPlugin extends Plugin {
|
|
52
|
+
|
|
53
|
+
private final String pluginVersion = "8.0.0";
|
|
54
|
+
private boolean readData = false;
|
|
55
|
+
|
|
56
|
+
// MARK: - Pick Files
|
|
57
|
+
|
|
58
|
+
@PluginMethod
|
|
59
|
+
public void pickFiles(PluginCall call) {
|
|
60
|
+
readData = call.getBoolean("readData", false);
|
|
61
|
+
int limit = call.getInt("limit", 0);
|
|
62
|
+
|
|
63
|
+
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
|
|
64
|
+
intent.setType("*/*");
|
|
65
|
+
|
|
66
|
+
JSArray types = call.getArray("types");
|
|
67
|
+
if (types != null && types.length() > 0) {
|
|
68
|
+
try {
|
|
69
|
+
String[] mimeTypes = new String[types.length()];
|
|
70
|
+
for (int i = 0; i < types.length(); i++) {
|
|
71
|
+
String type = types.getString(i);
|
|
72
|
+
if (type.startsWith(".")) {
|
|
73
|
+
String ext = type.substring(1);
|
|
74
|
+
String mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(ext);
|
|
75
|
+
mimeTypes[i] = mimeType != null ? mimeType : "*/*";
|
|
76
|
+
} else {
|
|
77
|
+
mimeTypes[i] = type;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
intent.putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes);
|
|
81
|
+
} catch (JSONException e) {
|
|
82
|
+
// Use default type
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
intent.addCategory(Intent.CATEGORY_OPENABLE);
|
|
87
|
+
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, limit != 1);
|
|
88
|
+
|
|
89
|
+
startActivityForResult(call, intent, "pickFilesResult");
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// MARK: - Pick Images
|
|
93
|
+
|
|
94
|
+
@PluginMethod
|
|
95
|
+
public void pickImages(PluginCall call) {
|
|
96
|
+
readData = call.getBoolean("readData", false);
|
|
97
|
+
int limit = call.getInt("limit", 0);
|
|
98
|
+
|
|
99
|
+
Intent intent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
|
|
100
|
+
intent.setType("image/*");
|
|
101
|
+
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, limit != 1);
|
|
102
|
+
|
|
103
|
+
startActivityForResult(call, intent, "pickMediaResult");
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// MARK: - Pick Videos
|
|
107
|
+
|
|
108
|
+
@PluginMethod
|
|
109
|
+
public void pickVideos(PluginCall call) {
|
|
110
|
+
readData = call.getBoolean("readData", false);
|
|
111
|
+
int limit = call.getInt("limit", 0);
|
|
112
|
+
|
|
113
|
+
Intent intent = new Intent(Intent.ACTION_PICK, MediaStore.Video.Media.EXTERNAL_CONTENT_URI);
|
|
114
|
+
intent.setType("video/*");
|
|
115
|
+
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, limit != 1);
|
|
116
|
+
|
|
117
|
+
startActivityForResult(call, intent, "pickMediaResult");
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// MARK: - Pick Media
|
|
121
|
+
|
|
122
|
+
@PluginMethod
|
|
123
|
+
public void pickMedia(PluginCall call) {
|
|
124
|
+
readData = call.getBoolean("readData", false);
|
|
125
|
+
int limit = call.getInt("limit", 0);
|
|
126
|
+
|
|
127
|
+
Intent intent;
|
|
128
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
|
129
|
+
intent = new Intent(MediaStore.ACTION_PICK_IMAGES);
|
|
130
|
+
intent.setType("*/*");
|
|
131
|
+
intent.putExtra(Intent.EXTRA_MIME_TYPES, new String[] { "image/*", "video/*" });
|
|
132
|
+
if (limit > 1) {
|
|
133
|
+
intent.putExtra(MediaStore.EXTRA_PICK_IMAGES_MAX, limit);
|
|
134
|
+
}
|
|
135
|
+
} else {
|
|
136
|
+
intent = new Intent(Intent.ACTION_GET_CONTENT);
|
|
137
|
+
intent.setType("*/*");
|
|
138
|
+
intent.putExtra(Intent.EXTRA_MIME_TYPES, new String[] { "image/*", "video/*" });
|
|
139
|
+
intent.addCategory(Intent.CATEGORY_OPENABLE);
|
|
140
|
+
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, limit != 1);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
startActivityForResult(call, intent, "pickMediaResult");
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// MARK: - Pick Directory
|
|
147
|
+
|
|
148
|
+
@PluginMethod
|
|
149
|
+
public void pickDirectory(PluginCall call) {
|
|
150
|
+
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
|
|
151
|
+
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
|
|
152
|
+
|
|
153
|
+
startActivityForResult(call, intent, "pickDirectoryResult");
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// MARK: - Convert HEIC to JPEG
|
|
157
|
+
|
|
158
|
+
@PluginMethod
|
|
159
|
+
public void convertHeicToJpeg(PluginCall call) {
|
|
160
|
+
call.reject("convertHeicToJpeg is not supported on Android");
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// MARK: - Copy File
|
|
164
|
+
|
|
165
|
+
@PluginMethod
|
|
166
|
+
public void copyFile(PluginCall call) {
|
|
167
|
+
String from = call.getString("from");
|
|
168
|
+
String to = call.getString("to");
|
|
169
|
+
boolean overwrite = call.getBoolean("overwrite", false);
|
|
170
|
+
|
|
171
|
+
if (from == null || to == null) {
|
|
172
|
+
call.reject("from and to are required");
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
try {
|
|
177
|
+
File fromFile = new File(from);
|
|
178
|
+
File toFile = new File(to);
|
|
179
|
+
|
|
180
|
+
if (!fromFile.exists()) {
|
|
181
|
+
call.reject("Source file does not exist");
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (toFile.exists() && !overwrite) {
|
|
186
|
+
call.reject("Destination file already exists");
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Create parent directories if needed
|
|
191
|
+
File parentDir = toFile.getParentFile();
|
|
192
|
+
if (parentDir != null && !parentDir.exists()) {
|
|
193
|
+
parentDir.mkdirs();
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
try (InputStream in = new FileInputStream(fromFile); OutputStream out = new FileOutputStream(toFile)) {
|
|
197
|
+
byte[] buffer = new byte[8192];
|
|
198
|
+
int length;
|
|
199
|
+
while ((length = in.read(buffer)) > 0) {
|
|
200
|
+
out.write(buffer, 0, length);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
call.resolve();
|
|
205
|
+
} catch (Exception e) {
|
|
206
|
+
call.reject("Failed to copy file: " + e.getMessage());
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// MARK: - Permissions
|
|
211
|
+
|
|
212
|
+
@PluginMethod
|
|
213
|
+
public void checkPermissions(PluginCall call) {
|
|
214
|
+
JSObject result = new JSObject();
|
|
215
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
|
216
|
+
result.put("readExternalStorage", "granted");
|
|
217
|
+
} else {
|
|
218
|
+
String state = getPermissionState("readExternalStorage").toString().toLowerCase();
|
|
219
|
+
result.put("readExternalStorage", state);
|
|
220
|
+
}
|
|
221
|
+
call.resolve(result);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
@PluginMethod
|
|
225
|
+
public void requestPermissions(PluginCall call) {
|
|
226
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
|
227
|
+
JSObject result = new JSObject();
|
|
228
|
+
result.put("readExternalStorage", "granted");
|
|
229
|
+
call.resolve(result);
|
|
230
|
+
} else {
|
|
231
|
+
requestPermissionForAlias("readExternalStorage", call, "permissionCallback");
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
@PermissionCallback
|
|
236
|
+
private void permissionCallback(PluginCall call) {
|
|
237
|
+
JSObject result = new JSObject();
|
|
238
|
+
String state = getPermissionState("readExternalStorage").toString().toLowerCase();
|
|
239
|
+
result.put("readExternalStorage", state);
|
|
240
|
+
call.resolve(result);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// MARK: - Plugin Version
|
|
244
|
+
|
|
245
|
+
@PluginMethod
|
|
246
|
+
public void getPluginVersion(PluginCall call) {
|
|
247
|
+
JSObject ret = new JSObject();
|
|
248
|
+
ret.put("version", this.pluginVersion);
|
|
249
|
+
call.resolve(ret);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// MARK: - Activity Results
|
|
253
|
+
|
|
254
|
+
@ActivityCallback
|
|
255
|
+
private void pickFilesResult(PluginCall call, ActivityResult result) {
|
|
256
|
+
handleFilePickerResult(call, result);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
@ActivityCallback
|
|
260
|
+
private void pickMediaResult(PluginCall call, ActivityResult result) {
|
|
261
|
+
handleFilePickerResult(call, result);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
private void handleFilePickerResult(PluginCall call, ActivityResult result) {
|
|
265
|
+
if (call == null) {
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
if (result.getResultCode() != Activity.RESULT_OK) {
|
|
270
|
+
notifyListeners("pickerDismissed", new JSObject());
|
|
271
|
+
JSObject ret = new JSObject();
|
|
272
|
+
ret.put("files", new JSArray());
|
|
273
|
+
call.resolve(ret);
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
Intent data = result.getData();
|
|
278
|
+
if (data == null) {
|
|
279
|
+
JSObject ret = new JSObject();
|
|
280
|
+
ret.put("files", new JSArray());
|
|
281
|
+
call.resolve(ret);
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
List<Uri> uris = new ArrayList<>();
|
|
286
|
+
|
|
287
|
+
ClipData clipData = data.getClipData();
|
|
288
|
+
if (clipData != null) {
|
|
289
|
+
for (int i = 0; i < clipData.getItemCount(); i++) {
|
|
290
|
+
uris.add(clipData.getItemAt(i).getUri());
|
|
291
|
+
}
|
|
292
|
+
} else if (data.getData() != null) {
|
|
293
|
+
uris.add(data.getData());
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
JSArray files = new JSArray();
|
|
297
|
+
for (Uri uri : uris) {
|
|
298
|
+
JSObject fileInfo = getFileInfo(uri);
|
|
299
|
+
if (fileInfo != null) {
|
|
300
|
+
files.put(fileInfo);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
JSObject ret = new JSObject();
|
|
305
|
+
ret.put("files", files);
|
|
306
|
+
call.resolve(ret);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
@ActivityCallback
|
|
310
|
+
private void pickDirectoryResult(PluginCall call, ActivityResult result) {
|
|
311
|
+
if (call == null) {
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
if (result.getResultCode() != Activity.RESULT_OK) {
|
|
316
|
+
notifyListeners("pickerDismissed", new JSObject());
|
|
317
|
+
call.reject("User cancelled");
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
Intent data = result.getData();
|
|
322
|
+
if (data == null || data.getData() == null) {
|
|
323
|
+
call.reject("No directory selected");
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
Uri uri = data.getData();
|
|
328
|
+
String path = getPathFromUri(uri);
|
|
329
|
+
|
|
330
|
+
JSObject ret = new JSObject();
|
|
331
|
+
ret.put("path", path != null ? path : uri.toString());
|
|
332
|
+
call.resolve(ret);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// MARK: - Helpers
|
|
336
|
+
|
|
337
|
+
@Nullable
|
|
338
|
+
private JSObject getFileInfo(Uri uri) {
|
|
339
|
+
try {
|
|
340
|
+
JSObject result = new JSObject();
|
|
341
|
+
|
|
342
|
+
// Get name and size from content resolver
|
|
343
|
+
try (Cursor cursor = getContext().getContentResolver().query(uri, null, null, null, null)) {
|
|
344
|
+
if (cursor != null && cursor.moveToFirst()) {
|
|
345
|
+
int nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
|
|
346
|
+
int sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE);
|
|
347
|
+
|
|
348
|
+
if (nameIndex >= 0) {
|
|
349
|
+
result.put("name", cursor.getString(nameIndex));
|
|
350
|
+
}
|
|
351
|
+
if (sizeIndex >= 0) {
|
|
352
|
+
result.put("size", cursor.getLong(sizeIndex));
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// Get MIME type
|
|
358
|
+
String mimeType = getContext().getContentResolver().getType(uri);
|
|
359
|
+
result.put("mimeType", mimeType != null ? mimeType : "application/octet-stream");
|
|
360
|
+
|
|
361
|
+
// Get path
|
|
362
|
+
String path = getPathFromUri(uri);
|
|
363
|
+
result.put("path", path != null ? path : uri.toString());
|
|
364
|
+
|
|
365
|
+
// Get modified date
|
|
366
|
+
Long modifiedAt = getModifiedAt(uri);
|
|
367
|
+
if (modifiedAt != null) {
|
|
368
|
+
result.put("modifiedAt", modifiedAt);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// Get dimensions for images
|
|
372
|
+
if (mimeType != null && mimeType.startsWith("image/")) {
|
|
373
|
+
try (InputStream is = getContext().getContentResolver().openInputStream(uri)) {
|
|
374
|
+
BitmapFactory.Options options = new BitmapFactory.Options();
|
|
375
|
+
options.inJustDecodeBounds = true;
|
|
376
|
+
BitmapFactory.decodeStream(is, null, options);
|
|
377
|
+
result.put("width", options.outWidth);
|
|
378
|
+
result.put("height", options.outHeight);
|
|
379
|
+
} catch (Exception e) {
|
|
380
|
+
// Ignore dimension errors
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// Get dimensions and duration for videos
|
|
385
|
+
if (mimeType != null && mimeType.startsWith("video/")) {
|
|
386
|
+
try {
|
|
387
|
+
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
|
|
388
|
+
retriever.setDataSource(getContext(), uri);
|
|
389
|
+
|
|
390
|
+
String width = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH);
|
|
391
|
+
String height = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT);
|
|
392
|
+
String duration = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
|
|
393
|
+
String rotation = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION);
|
|
394
|
+
|
|
395
|
+
int w = width != null ? Integer.parseInt(width) : 0;
|
|
396
|
+
int h = height != null ? Integer.parseInt(height) : 0;
|
|
397
|
+
int rot = rotation != null ? Integer.parseInt(rotation) : 0;
|
|
398
|
+
|
|
399
|
+
// Swap dimensions if rotated 90 or 270 degrees
|
|
400
|
+
if (rot == 90 || rot == 270) {
|
|
401
|
+
result.put("width", h);
|
|
402
|
+
result.put("height", w);
|
|
403
|
+
} else {
|
|
404
|
+
result.put("width", w);
|
|
405
|
+
result.put("height", h);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
if (duration != null) {
|
|
409
|
+
result.put("duration", Long.parseLong(duration) / 1000.0);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
retriever.release();
|
|
413
|
+
} catch (Exception e) {
|
|
414
|
+
// Ignore metadata errors
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
// Read data if requested
|
|
419
|
+
if (readData) {
|
|
420
|
+
try (InputStream is = getContext().getContentResolver().openInputStream(uri)) {
|
|
421
|
+
if (is != null) {
|
|
422
|
+
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
|
423
|
+
byte[] data = new byte[8192];
|
|
424
|
+
int length;
|
|
425
|
+
while ((length = is.read(data)) != -1) {
|
|
426
|
+
buffer.write(data, 0, length);
|
|
427
|
+
}
|
|
428
|
+
result.put("data", Base64.encodeToString(buffer.toByteArray(), Base64.NO_WRAP));
|
|
429
|
+
}
|
|
430
|
+
} catch (Exception e) {
|
|
431
|
+
// Ignore data read errors
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
return result;
|
|
436
|
+
} catch (Exception e) {
|
|
437
|
+
return null;
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
@Nullable
|
|
442
|
+
private String getPathFromUri(Uri uri) {
|
|
443
|
+
if (uri == null) {
|
|
444
|
+
return null;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
// Try to get actual file path
|
|
448
|
+
if ("file".equals(uri.getScheme())) {
|
|
449
|
+
return uri.getPath();
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
// For content URIs, return the URI string
|
|
453
|
+
return uri.toString();
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
@Nullable
|
|
457
|
+
private Long getModifiedAt(Uri uri) {
|
|
458
|
+
try {
|
|
459
|
+
String[] projection = { MediaStore.MediaColumns.DATE_MODIFIED };
|
|
460
|
+
try (Cursor cursor = getContext().getContentResolver().query(uri, projection, null, null, null)) {
|
|
461
|
+
if (cursor != null && cursor.moveToFirst()) {
|
|
462
|
+
int columnIndex = cursor.getColumnIndex(MediaStore.MediaColumns.DATE_MODIFIED);
|
|
463
|
+
if (columnIndex >= 0) {
|
|
464
|
+
return cursor.getLong(columnIndex) * 1000; // Convert to milliseconds
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
} catch (Exception e) {
|
|
469
|
+
// Ignore
|
|
470
|
+
}
|
|
471
|
+
return null;
|
|
472
|
+
}
|
|
473
|
+
}
|