Filepath und Storage Access Framework

  • Hallo,
    ich schreibe an einer kleinen Messager App (API 24+), bei der u.a. Fotos und Dokumente auf einen Server geladen werden.
    Die Fotos werden zuvor verkleinert.
    In der bisherigen Testphase habe ich einen file chooser verwendet, mit dem ich durch das Dateisystem browse und als Ergebnis ein File mit einem Pfad wie z.B. "/mnt/sdcard/picturexy.jpg" erhalten habe.
    Da die typische Auswahl eines (von vielen) Fotos nur anhand des Names ohne Vorschau unpraktisch ist, möchte ich den file chooser ersetzen.

    Daher lag der Rückgriff auf das Bordmittel Storage Access Framework (SAF) nahe.
    Ich lasse also das File nach Vorgabe verschiedener Mime-Typen per Intent.ACTION_OPEN_DOCUMENT auswählen und erhalte einen Uri zurück.
    Und da beginnen meine Probleme.


    Meine App erwartet die Übergabe eines Files
    file = new File(uri.getPath())
    aus dem die Extension
    String extension = MimeTypeMap.getFileExtensionFromUrl(file.getName())
    und der MimeType
    String mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension)
    bestimmt werden.
    Je nach Extension wird das File dann skaliert (wenn es ein Image ist) oder gleich auf den Server hochgeladen.
    Spätestens beim Upload wird zudem der MediaType (erhalten aus dem dem Mime-Type) benötigt.


    Aber: Das SAF liefert nicht diese Art von uri, sondern einen Pfad wie "/document/image:60".
    Daraus erhalte ich mit meinen bisherigen Methoden aber nicht den Filetyp.


    Wie kann ich aus dem vom SAF gelieferten uri den "echten" Pfad erhalten?
    Oder: wie lese ich den mime-Typ aus?


    (Ist etwa ernsthaft vorgesehen, aus dem uri-"image" auf den mime-Typ "image/*" zu schließen? Da fehlt noch der Subtyp "*", also jpg, png, ...)




    Und dann gibt es am Horizont noch das Problem, ob das SAF alle vorhandenen Files auf dem Gerät tatsächlich findet.
    Um in den dortigen Tabellen indiziert zu werden, müssen die Files ja bekannt gemacht worden sein, was normalerweise die App übernimmt.
    Meine App löst das so:

    Code
    DownloadManager manager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
    manager.addCompletedDownload(downloaded_file.getName(),"myApp",true,"image/png",downloaded_file.getPath(),downloaded_file.length(),true);

    Aber wie ist das, wenn der User beispielsweise per USB-Kabel sein "Schaut_her.pdf" in den Download-Ordner seines Geräts geladen hat.
    Findet SAF dieses File dann überhaupt? (Ja, "application/pdf" ist als mime-Type in der Auswahl zugelassen)



    Ist das SAF für einen solchen Einsatz (beliebige Files verschiedener Apps, nicht zwangsläufig mit einem Content/Media/?Provider vertreten, dazu "external storage") überhaupt sinnvoll, oder sollte ich bei meinem alten File Chooser bleiben und diesen um eine Analyse der mime-Typen erweitern (also z.B. Typ-Icons und Thumbnails bei Bildern anzeigen)?
    Wenn ich daran denke, wieviel Datenmüll auf manchen Smartphones herumfliegt ("wieso löschen? Ich habe eine 64-GB-Speicherkarte drin..."), sind das nicht gerade rosige Aussichten, was die zu erwartende Performance angeht.
    Kann aber auch sein, dass ich grundsätzlich falsch liege - das SAF beansprucht schließlich ausdrücklich, über die Gerätegrenzen, d.h. auf die Cloud ausgedehnt, gültig zu sein.

  • HI, den Mime Typ müsstest du so bekommen

    Code
    ContentResolver cR = context.getContentResolver();
    MimeTypeMap mime = MimeTypeMap.getSingleton();
    String type = mime.getExtensionFromMimeType(cR.getType(uri));

    für "*" gibt es glaube ich EXTRA_MIME_TYPES


    https://stackoverflow.com/ques…ple-mime-types-in-android
    https://www.sitepoint.com/mime-types-complete-list/

  • @jogimuc Danke für die Antwort.
    Die Eingabe mehrerer Mime-Types auf einmal kannte ich schon.



    Den Mime-Type über den ContentResolver zu erhalten, ist auf jeden Fall ein nützliches Mosaiksteinchen.
    Schade nur, dass man mit dem SAF nicht den ursprünglichen Filenamen erhält (sondern nur z.B. "image:60).
    In meinem Fall spielt das für Bilder zwar keine Rolle, da diese beim Upload ohnehin neu vergeben werden, aber beim Upload von Dokumenten mit ihren aussagekräftigen Filenamen sieht das anders aus.



    Ansonsten hier der Code zur Nutzung des SAF, der streng genommen ja kein Datei-Auswahldialog ist, sondern zu einem Dokument die App öffnen soll.


    Aufruf des Auswahldialogs (context ist der Context der Activity, FILE_OPEN_REQUEST_CODE eine beliebig gewählte int):

    Code
    Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
    intent.addCategory(Intent.CATEGORY_OPENABLE);
    intent.setType("*/*");
    String[] mimetypes = {"image/*", "video/*", "audio/*", "text/*", "application/pdf"};
    intent.putExtra(Intent.EXTRA_MIME_TYPES, mimetypes);
    startActivityForResult(intent,FILE_OPEN_REQUEST_CODE);


    und die Behandlung des Ergebnisses:


    Ich denke, ich werde dennoch einen eigenen File-Dialog schreiben,
    - da ich somit die Möglichkeit habe, potenziell ALLE Files zur Auswahl anbieten zu können (also nicht nur die mit mime-Types, die auf dem Android-Gerät bereits bekannt sind),
    - weil ich vor allem auch die Original-Filenamen erhalte,
    - und weil ich so natürlich auch noch das Design anpassen kann.



    Vielleicht kennt jemand jemand einen Weg, aus einem über das SAF erhaltenem Uri doch noch den Original-Namen zu erhalten und postet diesen hier?
    Ich könnte mir auch vorstellen, dass dem API eine entsprechende Methode in Zukunft hinzugefügt wird.

  • Hier mit müssten alle Datein zur auswahl stehen


    • ACTION_GET_CONTENT with MIME type */* and category CATEGORY_OPENABLE -- Display all pickers for data that can be opened with ContentResolver.openInputStream(), allowing the user to pick one of them and then some data inside of it and returning the resulting URI to the caller. This can be used, for example, in an e-mail application to allow the user to pick some data to include as an attachment.

    There are a variety of standard Intent action and category constants defined in the Intent class, but applications can also define their own. These strings use Java-style scoping, to ensure they are unique -- for example, the standard ACTION_VIEW is called "android.intent.action.VIEW".
    Put together, the set of actions, data types, categories, and extra data defines a language for the system allowing for the expression of phrases such as "call john smith's cell". As applications are added to the system, they can extend this language by adding new actions, types, and categories, or they can modify the behavior of existing phrases by supplying their own activities that handle them.

  • Teste mal


    File myFile = new File(uri.getPath());
    String myPath = myFile.getAbsolutePath();
    String myDateiName = myFile.getAbsoluteFile();

    Erzeugt man aus dem SAF-uri ein File und liest deren diverse path-Informationen aus (getAbsolutePath oder getAbsoluteFile, ebenso die canonical Variante), erhält man ebenfalls nur Verweise vom Typ "image:60".


    Das SAF weigert sich beharrlich, seine Rolle als Vermittler zwischen Provider und Client aufzugeben.
    Da haben die API-Konstrukteure zwar m.M.n.etwas zu kurz gedacht, aber so ist Android eben.



    Hier mit müssten alle Datein zur auswahl stehen


    Das funktioniert ebenfalls nicht, da der ContentResolver nur Dokumente liefern kann, für die die vorhandenen Apps "ihre" Mime-Typen angemeldet haben.
    Unbekannte Dokumente tauchen daher gar nicht erst auf.


    Damit wird das Ganze für mich aber unbrauchbar, da die Dokumente nicht zwangsläufig auch auf dem Gerät geöffnet werden müssen.



    Fazit:
    Für begrenzte Zwecke - Auswahl nur einzelner Dokument-Typen - ist das SAF ganz praktisch, aber für meinen Zweck zu beschränkt.
    Mein eigener File-Dialog nimmt langsam Gestalt an, auch wenn das neue constrait Layout neue Fallstricke bereit hält. Aber das ist ein anderes Thema.



    Vielen Dank nochmal.

Jetzt mitmachen!

Sie haben noch kein Benutzerkonto auf unserer Seite? Registrieren Sie sich kostenlos und nehmen Sie an unserer Community teil!