7777import java .io .OutputStream ;
7878import java .io .IOException ;
7979
80+ import android .os .Build ;
81+ import android .os .Environment ;
82+ import android .Manifest ;
83+ import android .content .Context ;
84+ import android .content .Intent ;
85+ import android .content .pm .PackageInfo ;
86+ import android .content .pm .PackageManager ;
87+ import android .net .Uri ;
88+ import android .provider .Settings ;
89+
90+ import androidx .core .content .ContextCompat ;
91+
92+
8093
8194public class System extends CordovaPlugin {
8295
@@ -240,6 +253,65 @@ public void run() {
240253
241254 callbackContext .success (arch );
242255 return true ;
256+
257+ case "requestStorageManager" :
258+ if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .R ) {
259+ try {
260+ Intent intent = new Intent (Settings .ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION );
261+ intent .setData (Uri .parse ("package:" + context .getPackageName ()));
262+ intent .addFlags (Intent .FLAG_ACTIVITY_NEW_TASK );
263+ context .startActivity (intent );
264+ callbackContext .success ("true" );
265+ } catch (Exception e ) {
266+ // Fallback to general settings if specific one fails
267+ Intent intent = new Intent (Settings .ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION );
268+ intent .addFlags (Intent .FLAG_ACTIVITY_NEW_TASK );
269+ context .startActivity (intent );
270+ callbackContext .success ("true" );
271+ }
272+ } else {
273+ callbackContext .success ("false" ); // Not needed on Android < 11
274+ }
275+ return true ;
276+
277+
278+ case "hasGrantedStorageManager" :
279+ boolean granted ;
280+ if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .R ) {
281+ granted = Environment .isExternalStorageManager ();
282+ } else {
283+ // Fallback for Android 10 and below
284+ granted = ContextCompat .checkSelfPermission (
285+ context ,
286+ Manifest .permission .READ_EXTERNAL_STORAGE
287+ ) == PackageManager .PERMISSION_GRANTED ;
288+ }
289+ callbackContext .success (String .valueOf (granted ));
290+ return true ;
291+
292+ case "isManageExternalStorageDeclared" :
293+ PackageManager pm = context .getPackageManager ();
294+ try {
295+ PackageInfo info = pm .getPackageInfo (context .getPackageName (), PackageManager .GET_PERMISSIONS );
296+ String [] permissions = info .requestedPermissions ;
297+ String isDeclared = "false" ;
298+
299+ if (permissions != null ) {
300+ for (String perm : permissions ) {
301+ if (perm .equals ("android.permission.MANAGE_EXTERNAL_STORAGE" )) {
302+ isDeclared = "true" ;
303+ break ;
304+ }
305+ }
306+ }
307+ callbackContext .success (isDeclared );
308+
309+ } catch (PackageManager .NameNotFoundException e ) {
310+ e .printStackTrace ();
311+ callbackContext .error (e .toString ());
312+ }
313+
314+ return true ;
243315 case "mkdirs" :
244316 File file = new File (args .getString (0 ));
245317 if (file .mkdirs ()) {
@@ -260,106 +332,104 @@ public void run() {
260332 public void run () {
261333 switch (action ) {
262334 case "copyToUri" :
263- try {
264- //srcUri is a file
265- Uri srcUri = Uri .parse (args .getString (0 ));
266-
267- //destUri is a directory
268- Uri destUri = Uri .parse (args .getString (1 ));
269-
270- //create a file named this into the dest Directory and copy the srcUri into it
271- String fileName = args .getString (2 );
272-
273- InputStream in = null ;
274- OutputStream out = null ;
275- try {
276- // Open input stream from the source URI
277- if ("file" .equalsIgnoreCase (srcUri .getScheme ())) {
278- File file = new File (srcUri .getPath ());
279- in = new FileInputStream (file );
280- } else {
281- in = context .getContentResolver ().openInputStream (srcUri );
282- }
283-
284- // Create the destination file using DocumentFile for better URI handling
285- DocumentFile destFile = null ;
286-
287- if ("file" .equalsIgnoreCase (destUri .getScheme ())) {
288- // Handle file:// scheme using DocumentFile
289- File destDir = new File (destUri .getPath ());
290- if (!destDir .exists ()) {
291- destDir .mkdirs (); // Create directory if it doesn't exist
292- }
293- DocumentFile destDocDir = DocumentFile .fromFile (destDir );
294-
295- // Check if file already exists and delete it
296- DocumentFile existingFile = destDocDir .findFile (fileName );
297- if (existingFile != null && existingFile .exists ()) {
298- existingFile .delete ();
299- }
300-
301- // Create new file
302- String mimeType = getMimeTypeFromExtension (fileName );
303- destFile = destDocDir .createFile (mimeType , fileName );
304- } else {
305- // Handle content:// scheme using DocumentFile
306- DocumentFile destDocDir = DocumentFile .fromTreeUri (context , destUri );
307-
308- if (destDocDir == null || !destDocDir .exists () || !destDocDir .isDirectory ()) {
309- callbackContext .error ("Destination directory does not exist or is not accessible" );
310- return ;
311- }
312-
313- // Check if file already exists and delete it
314- DocumentFile existingFile = destDocDir .findFile (fileName );
315- if (existingFile != null && existingFile .exists ()) {
316- existingFile .delete ();
317- }
318-
319- // Create new file
320- String mimeType = getMimeTypeFromExtension (fileName );
321- destFile = destDocDir .createFile (mimeType , fileName );
322- }
323-
324- if (destFile == null || !destFile .exists ()) {
325- callbackContext .error ("Failed to create destination file" );
326- return ;
327- }
328-
329- // Open output stream to the created file
330- out = context .getContentResolver ().openOutputStream (destFile .getUri ());
331-
332- if (in == null || out == null ) {
333- callbackContext .error ("uri streams are null" );
334- return ;
335- }
336-
337- // Copy stream
338- byte [] buffer = new byte [8192 ];
339- int len ;
340- while ((len = in .read (buffer )) > 0 ) {
341- out .write (buffer , 0 , len );
342- }
343-
344- out .flush ();
345- callbackContext .success ();
346- } catch (IOException e ) {
347- e .printStackTrace ();
348- callbackContext .error (e .toString ());
349- } finally {
350- try {
351- if (in != null ) in .close ();
352- if (out != null ) out .close ();
353- } catch (IOException e ) {
354- e .printStackTrace ();
355- callbackContext .error (e .toString ());
356- }
357- }
358- } catch (Exception e ) {
359- e .printStackTrace ();
360- callbackContext .error (e .toString ());
361- }
362- break ;
335+ try {
336+ //srcUri is a file
337+ Uri srcUri = Uri .parse (args .getString (0 ));
338+
339+ //destUri is a directory
340+ Uri destUri = Uri .parse (args .getString (1 ));
341+
342+ //create a file named this into the dest Directory and copy the srcUri into it
343+ String fileName = args .getString (2 );
344+
345+ InputStream in = null ;
346+ OutputStream out = null ;
347+ try {
348+ // Open input stream from the source URI
349+ if ("file" .equalsIgnoreCase (srcUri .getScheme ())) {
350+ File file = new File (srcUri .getPath ()); in = new FileInputStream (file );
351+ } else { in = context .getContentResolver ().openInputStream (srcUri );
352+ }
353+
354+ // Create the destination file using DocumentFile for better URI handling
355+ DocumentFile destFile = null ;
356+
357+ if ("file" .equalsIgnoreCase (destUri .getScheme ())) {
358+ // Handle file:// scheme using DocumentFile
359+ File destDir = new File (destUri .getPath ());
360+ if (!destDir .exists ()) {
361+ destDir .mkdirs (); // Create directory if it doesn't exist
362+ }
363+ DocumentFile destDocDir = DocumentFile .fromFile (destDir );
364+
365+ // Check if file already exists and delete it
366+ DocumentFile existingFile = destDocDir .findFile (fileName );
367+ if (existingFile != null && existingFile .exists ()) {
368+ existingFile .delete ();
369+ }
370+
371+ // Create new file
372+ String mimeType = getMimeTypeFromExtension (fileName );
373+ destFile = destDocDir .createFile (mimeType , fileName );
374+ } else {
375+ // Handle content:// scheme using DocumentFile
376+ DocumentFile destDocDir = DocumentFile .fromTreeUri (context , destUri );
377+
378+ if (destDocDir == null || !destDocDir .exists () || !destDocDir .isDirectory ()) {
379+ callbackContext .error ("Destination directory does not exist or is not accessible" );
380+ return ;
381+ }
382+
383+ // Check if file already exists and delete it
384+ DocumentFile existingFile = destDocDir .findFile (fileName );
385+ if (existingFile != null && existingFile .exists ()) {
386+ existingFile .delete ();
387+ }
388+
389+ // Create new file
390+ String mimeType = getMimeTypeFromExtension (fileName );
391+ destFile = destDocDir .createFile (mimeType , fileName );
392+ }
393+
394+ if (destFile == null || !destFile .exists ()) {
395+ callbackContext .error ("Failed to create destination file" );
396+ return ;
397+ }
398+
399+ // Open output stream to the created file
400+ out = context .getContentResolver ().openOutputStream (destFile .getUri ());
401+
402+ if ( in == null || out == null ) {
403+ callbackContext .error ("uri streams are null" );
404+ return ;
405+ }
406+
407+ // Copy stream
408+ byte [] buffer = new byte [8192 ];
409+ int len ;
410+ while ((len = in .read (buffer )) > 0 ) {
411+ out .write (buffer , 0 , len );
412+ }
413+
414+ out .flush ();
415+ callbackContext .success ();
416+ } catch (IOException e ) {
417+ e .printStackTrace ();
418+ callbackContext .error (e .toString ());
419+ } finally {
420+ try {
421+ if ( in != null ) in .close ();
422+ if (out != null ) out .close ();
423+ } catch (IOException e ) {
424+ e .printStackTrace ();
425+ callbackContext .error (e .toString ());
426+ }
427+ }
428+ } catch (Exception e ) {
429+ e .printStackTrace ();
430+ callbackContext .error (e .toString ());
431+ }
432+ break ;
363433 case "get-webkit-info" :
364434 getWebkitInfo (callbackContext );
365435 break ;
@@ -445,16 +515,16 @@ public void run() {
445515 }
446516
447517 // Helper method to determine MIME type using Android's built-in MimeTypeMap
448- private String getMimeTypeFromExtension (String fileName ) {
449- String extension = "" ;
450- int lastDotIndex = fileName .lastIndexOf ('.' );
451- if (lastDotIndex > 0 && lastDotIndex < fileName .length () - 1 ) {
452- extension = fileName .substring (lastDotIndex + 1 ).toLowerCase ();
518+ private String getMimeTypeFromExtension (String fileName ) {
519+ String extension = "" ;
520+ int lastDotIndex = fileName .lastIndexOf ('.' );
521+ if (lastDotIndex > 0 && lastDotIndex < fileName .length () - 1 ) {
522+ extension = fileName .substring (lastDotIndex + 1 ).toLowerCase ();
523+ }
524+
525+ String mimeType = MimeTypeMap .getSingleton ().getMimeTypeFromExtension (extension );
526+ return mimeType != null ? mimeType : "application/octet-stream" ;
453527 }
454-
455- String mimeType = MimeTypeMap .getSingleton ().getMimeTypeFromExtension (extension );
456- return mimeType != null ? mimeType : "application/octet-stream" ;
457- }
458528
459529 private void getConfiguration (CallbackContext callback ) {
460530 try {
0 commit comments