I have a project that I once created in Delphi Seattle and I would now like to move to Delphi Rio. I read the directory structure on an external SD-Card using findfirst/findnext
i := findfirst(datadir + '*', faanyfile, ts);
datadir variable contains a valid directory. On Seattle, the code works fine (return value i=0) and the first directory entry is returned in variable ts. Now, compiling the same code on Rio, I get a return error value i=13 (access denied).
The permission READ_EXTERNAL_STORAGE is set in my project.
If I hardcode the targetSdkVersion in the manifest file (which is automatically set to 26 in Rio) down to 19 (which is the minSdkVersion) the code works again, even in Rio. So obviously some way to handle sd-card access has changed from sdk-level 19 to 26?
Any hints someone?
Android OS introduced Runtime Permissions model since API 23.
That means in addition to specifying permission in Manifest, you also need to ask user for granting you permission for so called dangerous permissions at runtime. User has a choice to give you permission when asked, but it can also revoke that permission at any time.
Whenever your application deals with code that needs runtime permission it must verify that user granted you that permission and be prepared to deal with situation where user didn't gave you the permission.
READ_EXTERNAL_STORAGE is one of them.
Overview of different permissions (including their classification) can be found at Permissions overview
To upload your application on Google Play Store, your application needs to support minimum API 26 (for the moment) and Delphi Rio by default targets new API levels. It also introduces support for asking permissions at runtime.
Following is basic example that asks for
READ_EXTERNAL_STORAGE permission and reads files from shared downloads folder.
uses System.Permissions, Androidapi.Helpers, Androidapi.JNI.App, Androidapi.JNI.OS, ... procedure TMainForm.AddFiles; var LFiles: TArray<string>; LFile: string; begin LFiles := TDirectory.GetFiles(TPath.GetSharedDownloadsPath); for LFile in LFiles do begin Memo1.Lines.Add(LFile); end; end; procedure TMainForm.Button1Click(Sender: TObject); begin PermissionsService.RequestPermissions([JStringToString(TJManifest_permission.JavaClass.READ_EXTERNAL_STORAGE)], procedure(const APermissions: TArray<string>; const AGrantResults: TArray<TPermissionStatus>) begin if (Length(AGrantResults) = 1) and (AGrantResults = TPermissionStatus.Granted) then begin Memo1.Lines.Add('GRANTED'); AddFiles; end else begin Memo1.Lines.Add('NOT GRANTED'); end; end) end;