[SOLVED] WRITE_EXTERNEL_STORAGE Failed in ANDROID 14 lazarus / 43-Android ---------------------------------------------------------------------- Guser979 · 2024-04-08 23:46 I can't get "WRITE EXTERNEL STORAGE" permission my MANIFEST   Inside the APP I am using:         manifestPermissions[1]:= 'android.permission.WRITE_EXTERNAL_STORAGE" tools:remove="android:maxSdkVersion" tools:ignore="ScopedStorage'; I keep getting permission denied error ANDROID 14 ---------------------------------------------------------------------- maxerist · 2024-04-09 08:26 Quote from: Guser979 on April 09, 2024, 01:46:05 amI can't get "WRITE EXTERNEL STORAGE" permission ..... I keep getting permission denied error ANDROID 14 I'm not sure this is LAMW-specific issue. You didn't provide details about the location you're trying to write, but you may trying reading answers to  googling result [ WRITE_EXTERNAL_STORAGE "android 12..14" site:stackoverflow.com "permission denied" ] shows many useful answers where mostly Android 11+ restricts usage of this setting. The quote from the docs  usually mentioned  in the answers is QuoteIf your app targets Android 11, both the WRITE_EXTERNAL_STORAGE permission and the WRITE_MEDIA_STORAGE privileged permission no longer provide any additional access. ---------------------------------------------------------------------- jmpessoa · 2024-04-10 17:28 QuoteAppPublicFoldersAccessDemo1   <---------------------------------it worked Yes, This is a requirement Android > 10 ... It's the best we got for Android > 10 .... (and with each new version more restrictions are placed on access to "public" folders........) ---------------------------------------------------------------------- Guser979 · 2024-04-10 20:12 Quote from: jmpessoa on April 10, 2024, 07:28:09 pmQuoteAppPublicFoldersAccessDemo1   <---------------------------------it worked Yes, This is a requirement Android > 10 ... It's the best we got for Android > 10 .... (and with each new version more restrictions are placed on access to "public" folders........) This is really bad. I have this app (made entirely in LAWM) that fails whenever I have to perform operations where I need to use folders outside of it. In short, the app maintains a database with locations and objects found in them. In order not to lose this work, would it be possible to resurrect it with Android STUDIO (I would have to install it)? Or would it be wasted time? ---------------------------------------------------------------------- Leledumbo · 2024-04-11 11:43 You can not achieve what you want anymore, not with recent Android version. The restriction comes from Android itself, only apps like a file manager and antivirus can have such a permission, the rest can only have user granted directory selection, it can not be dialogless. Not sure about the possibility to circumvent it for sideloaded apps, but here's a reading. ---------------------------------------------------------------------- Guser979 · 2024-04-12 19:55 I got a temporary solution to export all data. In the following example I compress all the photos taken. Then use RequestCreateFile from LAWM. In "ActivityResult" I finish saving the zip file in Downloads using "SaveBytesToUri" Problem: I found it slow to finish writing. caused by Loop  FOR....  to pass the data from Tfilestream to the array. But for now it's better than not being able to export the data. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i  //////  call zip ( from Button , Menu , etc.)    procedure TAndroidModule3.zip;   var  zip: TZipper;  L: TStrings;  i: Integer;  relativefn: String;   beginzip := TZipper.Create;  try    zip.Filename := 'exportado.zip';    L := TStringList.Create;    try      FindAllFiles(L,GetEnvironmentDirectoryPath(dirDCIM), '*.jpg', true);        WriteLn(L.Count);      for i:=0 to L.Count-1 do begin        relativefn := CreateRelativePath(L[i],GetEnvironmentDirectoryPath(dirDownloads));      zip.Entries.AddFileEntry(SysToUTF8(L[i]), relativefn);      end;  finally      L.Free;    end;       zip.SaveToFile(GetEnvironmentDirec toryPath(dirDownloads)+'/exportado.zip');   Self.RequestCreateFile(Sel f.GetEnvironmentDirectoryPath(dirDownloads),'application/zip','exporta do.zip', 111);   //handled by "OnActivityResult"     finally    zip.Free;  end; end;       procedure TAndroidModule3.AndroidModule3ActivityResult(Sender: TObject;  requestCode: integer; resultCode: TAndroidResult; intentData: jObject);var  treeUri,uri: jObject;  arrayData: TDynArrayOfString;  listData: TstringList;  cnt,count, i: integer;  contentText: string;  arrayb: Tdynarrayofjbyte;  b:byte;  FS: TFileStream;  outFileName, outUriValue: string;  begin   if resultCode = RESULT_OK then   begin       if intentData = nil then      begin         ShowMessage('Sorry... data nil received...');         Exit;      end;       treeUri:= IntentManager1.GetDataUri(intentData);       Uri:= IntentManager1.GetDataUri(intentData);             if treeUri = nil then      begin         ShowMessage('Sorry... Uri nil received...');       Exit;      end;  if requestCode = 111 then  //create file       begin             FS := TFileStream.Create(GetEnvironmentDirectoryPat h(dirDownloads)+'/exportado.zip', fmOpenRead or fmShareDenyWrite);        SetLength( arrayb, fs.Size);   fs.Position:=0;                  Fs.Read(arrayb[0], fs.Size);   self.SaveBytesToUri(arrayb,treeUri); // release the filestream  Fs.free; end;                       UPDATE : Slowness problem resolved. Just eliminate the FOR.... Unnecessary because Fs.Read(arrayb[0], fs.Size); do the reading. UPDATE : In activityresult: release the filestream The code has been edited. ---------------------------------------------------------------------- magleft · 2024-05-27 13:35 Hello All. Guser979  I need your help. I am facing the same problem as you. My app needs to copy the database it uses to a shared folder. Using your example, I created the compressed file in the downloads folder, but the entire path to the file is saved. This is something I would not want to happen. I am attaching your code as I modified it: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure TFmaster.CreateZipFile;var   zip: TZipper;   i: Integer;   relativefn: String;begin     zip := TZipper.Create;     try        zip.Filename :=zipFile ;        try                 relativefn:=self.GetEnvironmentDirectoryPath(dirDatabase)+'/'+dbname  ;               zip.Entries.AddFileEntry(PChar(relativefn));        finally               L.Free;        end;        zip.SaveToFile(GetEnv ironmentDirectoryPath(dirDownloads)+'/'+zipFile);        Self.RequestC reateFile(Self.GetEnvironmentDirectoryPath(dirDownloads),'application/ zip',zipFile, 111);   //handled by "OnActivityResult"     finally      zip.Free;     end;end;   What should I fix? Also, how could I decompress the file inside the "dirDatabase" folder of the application? Thanks in advance for your time and help. ---------------------------------------------------------------------- Guser979 · 2024-06-13 20:24 The error is probably here: Your code: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i    relativefn:=self.GetEnvironmentDirectoryPath(dirDatabase)+'/'+dbname ;      Try readapting your code to relative path: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i  relativefn := CreateRelativePath(L[i],GetEnvironmentDirectoryPath(dirDownloads)); L is the file path I haven't tested unzip but you can study the zipper unit and try. There are examples in the source folder LAMW4Windows\fpc\3.2.0\source\fixes_3_2\packages\paszlib\examples\fpun zipper.lpi ---------------------------------------------------------------------- magleft · 2024-06-15 13:05 Thanks. I will try it. ---------------------------------------------------------------------- magleft · 2024-07-14 16:14 Hi to all. Following the example, it finally worked. My problem now is that when creating the compressed file, if it exists, it creates the 'filename(1).zip' file. Is there a way to make it save over the old file or delete the file if it exists? ---------------------------------------------------------------------- magleft · 2024-07-16 18:20 Thanks. As you very correctly wrote, only fileExists works. I don't know if and how the file can be requested to be deleted through the activityresult event. ---------------------------------------------------------------------- Guser979 · 2024-07-24 11:25 About my app, it is now practically functional again. Just a few more tweaks. Detail: In the last version I used jIntentManager to VIEW an image from the list of registered objects (jListView with bitmaps) . In this version (Android 14 ) only worked when I used Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i self.StartDefaultActivityForFile.     - In the previous version I had a button to add photo from the gallery as an additional photo captured by the camera. In this version this only worked when I used this line in the Result of jIntentManager.SetAction('android.intent.action.PICK'); Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i GetEnvironmentDirectoryPath(dirDCIM)+'/'+self.GetFileNameByUri(FjUri )  I hope this can help anyone who is updating their own app and needs this type of action. Update: My app is completely finished. Features used and functional: Camera Codebar Scan Data and photo compression (zip) Import and export data (sqlite) and photos. Data sharing (email, whatsapp, etc.) Deeply grateful to jmpessoa and all the LAMW developers. https://github.com/jmpessoa/lazandroidmodulewizard ---------------------------------------------------------------------- magleft · 2024-07-27 19:40 Congratulations. What did I do wrong and it didn't work for me? I made the modification in forms.java. In procedure TFmaster.FmasterActivityResult(Sender: TObject; requestCode: integer;  resultCode: TAndroidResult; intentData: jObject); I added the call as shown in the code below: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure TFmaster.FmasterActivityResult(Sender: TObject; requestCode: integer;  resultCode: TAndroidResult; intentData: jObject);var  treeUri,uri: jObject;  arrayData: TDynArrayOfString;  listData: TstringList;  cnt,count, i: integer;  contentText: string;  arrayb: Tdynarrayofjbyte;  b:byte;  FS: TFileStream;  outFileName, outUriValue: string;  dbFile:String;  szip:TUnZipper ;begin   IntentMa nager1.SetAction('android.settings.MANAGE_ALL_FILES_ACCESS_PERMISSION' )   ;   if resultCode = RESULT_OK then   begin      if intentData = nil then      begin         ShowMessage('Sorry... data nil received...');         Exit;      end;      treeUri:= IntentManager1.GetDataUri(intentData);      Uri:= IntentManager1.GetDataUri(intentData);      if treeUri = nil then    begin         ShowMessage('Sorry... Uri nil received...');         Exit;      end;      if requestCode = 111 then  //create file      begin              DeleteFile(UriToString(treeUri))  ;              FS := TFileStream.Create(GetEnvironmentDirectoryPath(dirDownloads)+'/'+zi pFileName , fmOpenRead or fmShareDenyWrite );              SetLength( arrayb, fs.Size);              fs.Position:=0;              Fs.Read(arrayb[0], fs.Size);              self.SaveBytesToUri(arrayb,treeUri);      end;       if requestCode = 333 then  //open file      begin         CopyFileFromUri(treeUri,Self. GetEnvironmentDirectoryPath(dirDownloads));        StartDecompressFile ;      end;    end;end; ---------------------------------------------------------------------- Guser979 · 2024-07-28 01:31 I haven't tested your code, but I've already discovered my mistake. When I tested the modification in jform.java , I simply deleted the file I had just created with the create intent. This is actually useless. But chatgpt suggested an approach that I'm going to try to implement. It seems pretty simple to me: 1. Create the file "filename.zip" for the first time and store the URI. 2. Use the stored URI for future write operations. That is, when filename.zip is created, the URI will be stored. When using the Create intent again , it will be possible to delete the stored URI so that filename.zip is created and not filename(1).zip. Let's see? Sorry for the mistake. Code: Java  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i  import android.content.SharedPreferences;import android.net.Uri;   // Store the URI in SharedPreferences private void saveFileUri(Uri uri) {    SharedPreferences prefs = getSharedPreferences("my_prefs", MODE_PRIVATE);    SharedPreferences.Editor editor = prefs.edit();    editor.putString("file_uri", uri.toString());    editor.apply();}    // Retrieve the URI from SharedPreferencesprivate Uri getFileUri() {    SharedPreferences prefs = getSharedPreferences("my_prefs", MODE_PRIVATE);    String uriString = prefs.getString("file_uri", null);    if (uriString != null) {        return Uri.parse(uriString);    }    return null;} ---------------------------------------------------------------------- magleft · 2024-07-28 09:19 Could you please help me by modifying my application? I can't understand where the java code should be added. I tried adding it to the jform.java file but it deletes it in the compile. Thanks in advance for your time ---------------------------------------------------------------------- Guser979 · 2024-07-28 14:49 Solved using chatgpt's suggestion, but using only LAMW (no need to modify jform.java) to save the URI. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i  //Delete files named filezip.zip// Add jpreferences component (Android Bridges Extra) // Add URI verificationprocedure AndroidModule1.zip;///// variables, etc begin///// ......here the code for compressing files generating "filezip.zip" // Here check if the URI already exists. // If URI does not exist then create "filezip.zip"// If URI exists then open file "filezip.zip" and it will be overwritten if Preferences1.GetStringData('uri','') <> '' then self.RequestOpenFile(S elf.GetEnvironmentDirectoryPath(dirDownloads),'application/zip' ,112) else  Self.RequestCreateFile(Self.GetEnvironmentDirectoryPath(dirDownl oads),'application/zip','filezip.zip', 111); end; // Here is where we first register the URI in preferences after "Create intent"   procedure TAndroidModule1.AndroidModule1ActivityResult(Sender: TObject;  requestCode: integer; resultCode: TAndroidResult; intentData: jObject);   var arrayb: Tdynarrayofjbyte;   treeUri : jObject;   fs:tfilestream;                     begin                                 if intentData = nil then      begin         ShowMessage('Sorry... data nil received...');         Exit;      end;       treeUri:= IntentManager1.GetDataUri(intentData);  if requestCode = 111 then  //create file    begin    //   Save URI       Preferences1.SetStringData('uri',UriToString(treeUri));            FS := TFileStream.Create(GetEnvironmentDirectoryPath(dirDownloads)+'/file zip.zip', fmOpenRead or fmShareDenyWrite);    SetLength( arrayb, fs.Size);   fs.Position:=0;    Fs.Read(arrayb[0], fs.Size);    SaveBytesToUri(arrayb,treeUri);    FS.Free;  end;  if requestCode = 112 then  //open  file  begin    FS := TFileStream.Create(GetEnvironme ntDirectoryPath(dirDownloads)+'/filezip.zip', fmOpenRead or fmShareDenyWrite);    SetLength( arrayb, fs.Size);   fs.Position:=0;  Fs.Read(arrayb[0], fs.Size);    SaveBytesToUri(arrayb,treeUri);    FS.Free;  end;  end; ---------------------------------------------------------------------- Guser979 · 2024-07-28 17:02 Quote from: magleft on July 28, 2024, 11:19:52 amCould you please help me by modifying my application? I can't understand where the java code should be added. I tried adding it to the jform.java file but it deletes it in the compile. Thanks in advance for your time The LAMW installed on my PC can't open your project, causing an "Access violation" error. But with the latest modification you will be able to get what you need without modifying jform.java. Unfortunately Android 14 has restrictions on handling files. For now this is what I managed to get working here, regarding this. ---------------------------------------------------------------------- magleft · 2024-07-28 17:41 Thank you very much . I'll test it and update. ---------------------------------------------------------------------- magleft · 2024-07-29 15:18 Works!!!! Thanks. Very good work. Thanks again.