Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Cross-Platform-Entwicklung (https://www.delphipraxis.net/91-cross-platform-entwicklung/)
-   -   Einfachen String als Datei speichern und via Intent an eine APP übergeben (https://www.delphipraxis.net/214459-einfachen-string-als-datei-speichern-und-via-intent-eine-app-uebergeben.html)

fisipjm 15. Jan 2024 15:57

Einfachen String als Datei speichern und via Intent an eine APP übergeben
 
Hi,

Ich glaub ich stehe heute extrem auf dem Schlauch.
Ich versuche folgendes, aus meiner Sicht einfaches, Vorhaben zu realisieren.

Ich entwickle mit FireMonkey (FMX) eine Android App (eigentlich auch iOS, aber so weit will ich ja noch gar nicht gehen).
In dieser APP habe ich einen einfachen String. In dem String eine kleine HTML Datei.
Das einzige was ich jetzt machen möchte, ist den String zu speichern (von mir aus auch ohne speichern, wüsste aber nicht wie das gehen soll) und die Datei per intent an den Android Öffnen dialog zu übergeben. Aber jedes mal wenn ich eine APP zum öffnen auswähle bekomme ich entweder die Meldung, dass die Datei nicht existiert oder der Zugriff nicht möglich ist.

Ich verwende Delphi 12. Ich entwickle auf Android 14. Ich habe in der Berechtigungsliste die sichere Dateifreigabe aktiviert.
Die Datei provider_paths.xml sieht so aus:

Code:
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<files-path name="internal_private_files" path="."/>
<cache-path name="internal_cache_files" path="."/>
<external-path name="external_public_files" path="."/>
<external-files-path name="external_private_files" path="."/>
<external-cache-path name="external_cache_files" path="."/>
<external-media-path name="external_media_files" path="."/>
</paths>
Mein Code mit dem ich das Versuche sieht so aus

Delphi-Quellcode:
procedure Share;
{$IF DEFINED(ANDROID)}
  procedure OpenHTMLFile(const FilePath: string);
  var
    Intent: JIntent;
    arch: JFile;
    Uri: Jnet_Uri;
  begin
    arch := TJFile.JavaClass.init(StringToJString(FilePath));
    arch.setReadable(true, false);

    if TJBuild_VERSION.JavaClass.SDK_INT >= 24 then
    begin
      Uri := TJcontent_FileProvider.JavaClass.getUriForFile(TAndroidHelper.Context,
        StringToJString(System.Concat(JStringToString(TAndroidHelper.Context.getPackageName), '.fileprovider')), arch);
    end
    else
    begin
      Uri := TJnet_Uri.JavaClass.fromFile(arch);
    end;

    Intent := TJIntent.Create;
    Intent.putExtra(TJIntent.JavaClass.EXTRA_NOT_UNKNOWN_SOURCE, true);
    Intent.setAction(TJIntent.JavaClass.ACTION_VIEW);
    Intent.setDataAndType(Uri, StringToJString('text/html'));

    TAndroidHelper.Context.startActivity(Intent);
  end;
{$ENDIF}

var
  Filename: String;
  lHTML: String;
begin
  lHTML:='<HTML><HEAD></HEAD><BODY><h1>Hallo World!</h1></BODY><HTML>'
  Filename := TPath.Combine(TPath.GetPublicPath, 'MyHTML.html');
  TFile.WriteAllText(Filename, lHTML);

{$IF DEFINED(ANDROID)} OpenHTMLFile(Filename); {$ENDIF}
end;
Hat jemand einen hinweis was ich falsch mache?

himitsu 15. Jan 2024 16:26

AW: Einfachen String als Datei speichern und via Intent an eine APP übergeben
 
Wie sieht denn TPath.GetPublicPath aus?

War da nicht der Programmname im Pfad mit enthalten, so ala
storage/emulated/0/android/data/com.embarcadero.testapp/

Kann sein, dass die andere App auf dieses Verzeichnis einfach keinen Zugriff hatt.

Versuche doch mal irgendwas von TPath.Shared.....
Oder alternativ den Download-Ordner benutzen.

PS: Es ist bei manchen Pfaden so, dass sie sich rechtemäßig unterschiedlich verhalten, wenn man sie intern hat, oder wenn es eine SD-Karte gibt und sie dann da drauf liegen.

TurboMagic 16. Jan 2024 07:19

AW: Einfachen String als Datei speichern und via Intent an eine APP übergeben
 
Wenn man den Download Ordner benutzen will muss man das inzwischen über
"Scoped storage" umsetzen, sonst kann man da seit Android 11 oder so auch
nicht mehr rein schreiben.

Grüße
TurboMagic

fisipjm 16. Jan 2024 15:44

AW: Einfachen String als Datei speichern und via Intent an eine APP übergeben
 
Zitat:

Zitat von himitsu (Beitrag 1531939)
Wie sieht denn TPath.GetPublicPath aus?

War da nicht der Programmname im Pfad mit enthalten, so ala
storage/emulated/0/android/data/com.embarcadero.testapp/

Kann sein, dass die andere App auf dieses Verzeichnis einfach keinen Zugriff hatt.

Versuche doch mal irgendwas von TPath.Shared.....
Oder alternativ den Download-Ordner benutzen.

PS: Es ist bei manchen Pfaden so, dass sie sich rechtemäßig unterschiedlich verhalten, wenn man sie intern hat, oder wenn es eine SD-Karte gibt und sie dann da drauf liegen.

Also, ich komme endlich zum antworten.
Ich hab es schon mit allem möglichen versucht, SharedDocument, Downloads... geht alles nicht.

Wenn ich mir den Filename ausgeben lasse, dann bekomme ich: /storage/emulated/0/Download/MyHtml.html
Wenn ich mir die URI ausgeben lasse, dann bekomme ich: content://my.app.emba.com.fileprovider/external_public_files/Download/MyHtml.html
Wenn ich die Datei später mit z.B. TotalComander aufrufe, dann öffnet der mir: content://com.ghisler.files/storage/emulated/0/Download/MyHtml.html

Ich verstehs nicht :? irgendwas mach ich doch falsch :(

himitsu 16. Jan 2024 16:21

AW: Einfachen String als Datei speichern und via Intent an eine APP übergeben
 
Vermutlich hat der TotalComander die Berechtigung, um auf "fremde" Dateien zugreifen zu dürfen
und deine App hat das halt nicht.

Wie TurboMagic schon anklingen ließ, wird es immer schwerer, bis fast unmöglich, um Dateien zwischen Anwendungen auszutauschen, bzw. Diese gemeinsam nutzen zu können.

* einmal könnte man versuchen z.B. via TCP/IP sich zwischen den Programmen zu unterhalten
* oder ein Intend-Objekt und es absenden -> die andere Anwendung braucht dann einen Filter darauf, um es empfangen zu können
* oder man schreibt die Datei in Google-Drive und lädt sie in der anderen Anwendung wieder runter :stupid:
* ...

TurboMagic 16. Jan 2024 17:20

AW: Einfachen String als Datei speichern und via Intent an eine APP übergeben
 
Wie geschrieben: der nötige Suchbegriff ist "scoped storage".
Ich hatte das sogar mal in einer App für meinen Arbeitgeber umgesetzt, allerdings ist die inzwischen
aus anderen Gründen gestorben und wirklich lustig war das aus einem Grund nicht:
Die Android Entwickler sind kurzsichtig oder engstirnig!

Warum? Weil man für scoped storage so einen Datei Laden Intent vom ANdroid aufrufen muss,
was eigentlich ganz ok wäre, wenn man da die Dateifilter auch flexibel angeben könnte und
nicht nur die paar vordefinierten Mimetypes nutzen könnte, für die es keine Erweiterungsmöglichgkeit
zu geben scheint.

Hallo Android Entwickler: nicht alles ist eine Textdatei, ein Bild, Video oder Musik!

=> da blieb mir dann glaube ich nur das Equivalent zu *.*

Grüße
TurboMagic

fisipjm 17. Jan 2024 08:14

AW: Einfachen String als Datei speichern und via Intent an eine APP übergeben
 
Moin ihr beiden,

schon mal Danke für eure Antworten. Ich glaube aber das Problem liegt in diesem speziellen Fall an einer anderen Stelle.

Wieso denke ich das?
Ich hab ja bereits beschrieben, wie sich die einzelnen URIs verhalten:
Zitat:

Wenn ich mir den Filename ausgeben lasse, dann bekomme ich: /storage/emulated/0/Download/MyHtml.html
Wenn ich mir die URI ausgeben lasse, dann bekomme ich: content://my.app.emba.com.fileprovider/external_public_files/Download/MyHtml.html
Wenn ich die Datei später mit z.B. TotalComander aufrufe, dann öffnet der mir: content://com.ghisler.files/storage/emulated/0/Download/MyHtml.html
Ich denke nicht das es an Zugriffsrechten liegt. Warum?:
Die Datei, wenn ich z.B. SharedDownload angebe, wir auch dort gespeichert. Ich kann dann mit allen Apps, die irgendwie auf das Dateisystem zugreifen dürfen die Datei sehen und auch öffnen. Das hat an der Stelle auch nichts mit dem TotalComander und Zugriffsrechten zu tun. Ich lasse mit mir reden, wenn es darum geht, die Datei anzuzeigen, OK. Also der TotalComander sieht die Datei nur wenn er auf das Verzeichnis Zugriffsrechte hat, gut, geschenkt.
Warum ist das an der Stelle irrelevant?:
Mir geht es nicht um den Zugriff auf die Datei die im SharedDownload liegt, sondern um das Intent, dass an die App übergeben wird um das File zu öffnen. Wenn ich das mit dem TotalComander mache, erhalte ich als URI in der app mit der ich öffnen möchte (was ja auch klappt):
Code:
content://com.ghisler.files/storage/emulated/0/Download/MyHtml.html
Öffne ich das selbe Dokument mit Google Files und Teile es mit einer anderen App erhalte ich:
Code:
content://com.google.android.apps.nbu.files.provider/2/1000000088
Irgendwie fühlt sich das an, als hätte ich irgend ein Problem mit der URI und nicht mit den Rechten.
Hab ich damit recht, oder lieg ich komplett daneben?:|

Gruß
PJM

QuickAndDirty 17. Jan 2024 10:42

AW: Einfachen String als Datei speichern und via Intent an eine APP übergeben
 
Ich benutze das hier um Dateien (Logs der App) mit Gmail "mailto" zu teilen...
Es funktioniert irgendwie nur mit GMail...ich glaube es geht nicht nicht mit whatsapp...
Auf jedenfall
-Baue ich die Fileprovider URL anders zusammen
-Ich setze einen Mime type damit er nicht versucht es zu öffnen...bei dir wäre aber vielleicht 'text/html' angemessen.
-Ich setzte Read permissions
Vielleicht hilft es wenn du mit den 3 sachen bei dir im Projekt etwas spielst....
Ich habe das auch nur durch wildes ausprobieren und raten ans laufen bekommen.

Delphi-Quellcode:
Class procedure TFileProviderService.SendFileToChooser(file_name: string);
{$IFDEF ANDROID}
var
  content,id:String;
  theFile: JFile;
  Intent: JIntent;
  FileURI:JNet_URI;
  ParcelURI:JParcelable;
{$ENDIF}
begin
{$IFDEF ANDROID}

  if System.SysUtils.TOSVersion.Check(7) then
  Begin
    theFile := TJFile.JavaClass.init(StringToJString(file_name));
    FileURI := TAndroidHelper.JFileToJURI(theFile);
    ParcelURI := JParcelable(FileURI);

    Intent := TJIntent.Create;
    Intent.setAction(TJIntent.JavaClass.ACTION_SEND);
    Intent.setDataAndType(fileuri,StringtoJstring('application/*'));
    Intent.PutExtra(TJIntent.JavaClass.EXTRA_STREAM, ParcelURI);
    Intent.SetFlags(TJIntent.JavaClass.FLAG_GRANT_READ_URI_PERMISSION);
    //depricated// SharedActivity.startActivity(TJIntent.JavaClass.createChooser(intent, StrToJCharSequence ('Share With')));
    TAndroidHelper.Activity.startActivity(TJIntent.JavaClass.createChooser(intent, StrToJCharSequence ('Share With')));
  End
  else
  Begin
    theFile := TJFile.JavaClass.init(StringToJString(file_name));

    id := JStringToString( TAndroidHelper.Context.getApplicationContext.getPackageName ) + '.fileprovider';
    content := 'content://' + id + '/root' + JStringToString( theFile.getCanonicalPath );

    FileURI := TJnet_Uri.JavaClass.parse(StringtoJString(content));
    ParcelURI := JParcelable(FileURI);

    Intent := TJIntent.Create;
    Intent.setAction(TJIntent.JavaClass.ACTION_SEND);
    Intent.addFlags(TJIntent.JavaClass.FLAG_GRANT_READ_URI_PERMISSION);
    Intent.PutExtra(TJIntent.JavaClass.EXTRA_STREAM, ParcelURI);
    Intent.setDataAndType(fileuri,StringtoJstring('application/*'));
    //depricated// SharedActivity.startActivity(TJIntent.JavaClass.createChooser(intent, StrToJCharSequence ('Share With')));
    TAndroidHelper.Activity.startActivity(TJIntent.JavaClass.createChooser(intent, StrToJCharSequence ('Share With')));
  End;
{$ENDIF}
end;
Ist das nicht schön wie einfach und leicht nachvollziehbar es ist eine Datei zu teilen oder ein Dokument zu öffnen?


Alle Zeitangaben in WEZ +1. Es ist jetzt 12:36 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz