2

I have written a photo application that streams images from an external source to my app on a tablet (Samsung Galaxy TAB S). I have decided to use the external SD card in the tablet as storage, as the internal storage often runs out of space. Plus, I need to remove it to backup all the images.

Everything works fine for "reading" the JPGs, but when it comes to "writing" JPGs from the stream to the SD Card, permission is denied.

I have already set the WRITE_EXTERNAL_STORAGE permission, but this does not work using the Android SDK 25.2.5, as Android changed the way the permissions work for external access.

fWriteStorage:=JStringToString(TJManifest_permission.JavaClass.WRITE_EXTERNAL_STORAGE);

PermissionsService.RequestPermissions([fReadStorage, fWriteStorage], DisplayRationale);

What I want to do is invoke the Android System Folder Chooser Dialog to allow access for the App to write to the SD card. As described here: SD Card on Android 5.0 and Later.

Does anyone know how I can invoke the permissions selector for the SD card in in Delphi 10.3 Rio? Similar to all apps including TotalCommander for Android, that need write access to an external SD card.

I have now tried again, using the following code and still no luck. I need to create new folders for image galleries, and the folder can not be created.

  fWriteStorage := JStringToString(TJManifest_permission.JavaClass.WRITE_EXTERNAL_STORAGE);
  if PermissionsService.IsPermissionGranted(fWriteStorage) then
  begin
    applog('IsPermissionGranted: TRUE');
    if ForceDirectories(System.IOUtils.TPath.Combine(GLOBAL_SDCARD, 'FoxyTab/Storage')) then
      AppLog('GLOBAL_SDCARD Created')
    else
      AppLog('Can not create SD CARD folder ' + System.IOUtils.TPath.Combine(GLOBAL_SDCARD, 'FoxyTab/Storage'));
      PermissionsService.RequestPermissions([fWriteStorage],
    procedure(const APermissions: TArray<string>; const AGrantResults: TArray<TPermissionStatus>)
    begin
      if (Length(AGrantResults) = 1) and (AGrantResults[0] = TPermissionStatus.Granted) then
      begin
        AppLog('Access Granted');
        if ForceDirectories(System.IOUtils.TPath.Combine(GLOBAL_SDCARD, 'FoxyTab/Storage')) then
          AppLog('GLOBAL_SDCARD Created')
        else
          AppLog('Can not create SD CARD folder ' + System.IOUtils.TPath.Combine(GLOBAL_SDCARD, 'FoxyTab/Storage'));

      end
      else
      begin
        AppLog('Access Denied');
      end;
    end);

I always get "Access Granted", but the folder can not be created.

The SD card is not internal to the tablet, but a "removable" microSD card because I need to remove it when the galleries are full to backup to another device (PC/MAC). The path to the card is /storage/2266-7298/. Different from what is returned using the standard directory.

  • "permission is denied". An exact error message, and preferably a stacktrace from logcat would go a long way to determining the issue. – Dave Nottage Aug 22 '19 at 05:39
  • I will take a look at logcat and see what I can find. I have never used it before on my tablet. – Alex Plasma Aug 22 '19 at 21:07

1 Answers1

1

You don't need to invoke a system dialog to get access.

When using PermissionsService.RequestPermissions(), you need to actually wait for Android to reply with a granted or denied status before you can then attempt to perform the action you are requesting permissions for.

In the code you have shown, you are not passing a callback function to RequestPermissions() to get that reply. So you don't know whether the user actually granted access to the SD card before you write files to it.

You need something more like this:

fWriteStorage := JStringToString(TJManifest_permission.JavaClass.WRITE_EXTERNAL_STORAGE);

...

if PermissionsService.IsPermissionGranted(fWriteStorage) then
begin
  // access previously granted, write files...
end
else
begin
  PermissionsService.RequestPermissions([fWriteStorage],
    procedure(const APermissions: TArray<string>; const AGrantResults: TArray<TPermissionStatus>)
    begin
      if (Length(AGrantResults) = 1) and (AGrantResults[0] = TPermissionStatus.Granted) then
      begin
        // access granted, write files...
      end
      else
      begin
        // access denied, can't write files...
      end;
    end,
    DisplayRationale);
  end;
end;

Note, if you use Android's Context.getExternalFilesDir() function (which is wrapped by Delphi's System.IOUtils.TPath.GetSharedDocumentsPath() method), you don't actually need WRITE_EXTERNAL_STORAGE permission to write to the SD card if you are writing to a folder that belongs to your app package:

Starting in Build.VERSION_CODES.KITKAT, no permissions are required to read or write to the returned path; it's always accessible to the calling app. This only applies to paths generated for package name of the calling application. To access paths belonging to other packages, Manifest.permission.WRITE_EXTERNAL_STORAGE and/or Manifest.permission.READ_EXTERNAL_STORAGE are required.

Apps like TotalCommander access other app's files, that is why it requires (READ|WRITE)_EXTERNAL_STORAGE. Your app may not, if it is just reading/writing its own files for its own use.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • Thanks Remy. I tried this code, but although it reports "Access Granted" I still can not create a folder, and not save images. I need to write to the external/removable microSD and not the internal one. Perhaps the permissions need to be the same, as the external SD is seen like "foreign" files. – Alex Plasma Aug 22 '19 at 21:15
  • Use getExternalStorageDirectory from Androidapi.JNI.Os.pas – Jim McKeeth Feb 09 '22 at 04:50