Android: Öffnen Sie die Datei mit der Absichtswahl von URI, die von Storage Access Framework erhalten wird

Am Anfang kann der Benutzer Dateien mit dem neuen Storage Access Framework auswählen (Angenommen, die App ist API> 19):

Https://developer.android.com/guide/topics/providers/document-provider.html

  • Änderungsdatum String-Format in Android
  • Unterschied zwischen Running Task und Running Process in Android
  • Wie man alles abruft und Anwendungsgrößen läuft, CPU-Nutzung programmgesteuert?
  • ListView-Tools: listitem funktioniert nicht im Android-Studio 2.2 stabiler Kanal
  • Android FB API 3.0 - Berechtigungen nur einmal festlegen
  • Android: Wie kann ich verhindern, dass die Softtastatur meine Ansicht nach oben drückt?
  • Dann spare ich Referenzen auf die gewählten Dateien, indem du die URIs abbringe, die wie folgt aussehen:

    content://com.android.providers.downloads.documments/document/745 

    (In diesem Fall ist die Datei aus dem Standard-Download-Verzeichnis).

    Später möchte ich, dass der Benutzer diese Dateien öffnen kann (zB werden die Namen in der UI-Liste angezeigt und der Benutzer wählt einen aus).

    Ich möchte dies mit der Android berühmten Intent chooser-Funktion zu tun, und alles, was ich habe, ist das obige URI-Objekt …

    Vielen Dank,

  • Wie man Layout xml von einem anderen Modul aufruft
  • Wie zu erwähnen Weg der Bibliotheken in Android.mk Datei oder Application.mk Datei?
  • Android-Komponenten präsentieren / visuelle Führer
  • Wie gruppiert man Elemente aus der Datenbank und zeigt sie an - Android
  • Android SeekBar setzt Fortschritt Wert
  • Android initialise openGL2.0 Kontext mit EGL
  • 2 Solutions collect form web for “Android: Öffnen Sie die Datei mit der Absichtswahl von URI, die von Storage Access Framework erhalten wird”

    Edit: Ich habe diese Antwort überarbeitet, um den Beispielcode des Ansatzes einzuschließen, den ich ursprünglich als "Schreiben eines spezialisierten ContentProvider" bezeichnet habe. Dies sollte die Anforderungen der Frage vollständig erfüllen. Wahrscheinlich macht die Antwort zu groß, aber es hat interne Code-Abhängigkeiten jetzt, also lasst uns es als Ganzes verlassen. Der wichtigste Punkt gilt immer noch: Verwenden Sie den ContentPrvder unten, wenn Sie wollen, aber versuchen Sie, file:// zu geben file:// Uris zu Apps, die sie unterstützen, es sei denn, Sie wollen für jemandes App-Crashing verantwortlich gemacht werden.

    Ursprüngliche Antwort


    Ich würde weg von Storage Access Framework bleiben, wie es jetzt ist. Es ist unzureichend von Google unterstützt, und die Unterstützung in Apps ist abgründig, so dass es schwer ist, zwischen Bugs in diesen Apps und SAF selbst zu erzählen. Wenn Sie zuversichtlich genug sind (was wirklich bedeutet, "kann Try-catch-Block besser als durchschnittlich Android-Entwickler verwenden"), verwenden Sie Storage Access Framework selbst, aber übergeben Sie an andere nur gut-alte file:// Pfade.

    Sie können den folgenden Trick verwenden, um den Dateisystempfad von ParcelFileDescriptor zu erhalten (Sie können ihn von ContentResolver erhalten, indem Sie openFileDescriptor aufrufen):

     class FdCompat { public static String getFdPath(ParcelFileDescriptor fd) { final String resolved; try { final File procfsFdFile = new File("/proc/self/fd/" + fd.getFd()); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { // Returned name may be empty or "pipe:", "socket:", "(deleted)" etc. resolved = Os.readlink(procfsFdFile.getAbsolutePath()); } else { // Returned name is usually valid or empty, but may start from // funny prefix if the file does not have a name resolved = procfsFdFile.getCanonicalPath(); } if (TextUtils.isEmpty(resolved) || resolved.charAt(0) != '/' || resolved.startsWith("/proc/") || resolved.startsWith("/fd/")) return null; } catch (IOException ioe) { // This exception means, that given file DID have some name, but it is // too long, some of symlinks in the path were broken or, most // likely, one of it's directories is inaccessible for reading. // Either way, it is almost certainly not a pipe. return ""; } catch (Exception errnoe) { // Actually ErrnoException, but base type avoids VerifyError on old versions // This exception should be VERY rare and means, that the descriptor // was made unavailable by some Unix magic. return null; } return resolved; } } 

    Sie müssen vorbereitet sein, dass die obige Methode null zurückgibt (die Datei ist eine Pipe oder Sockel, die vollkommen legitim ist) oder einen leeren Pfad (kein Lesezugriff auf das übergeordnete Verzeichnis der Datei). Wenn dies geschieht, kopieren Sie den gesamten Stream in ein Verzeichnis, auf das Sie zugreifen können .

    Komplettlösung


    Wenn du wirklich mit dem Content Provider Uris bleiben willst, gehst du hier. Nehmen Sie den Code von ContentProvider unten. Füge in dein App ein (und registriere es in AndroidManifest). Verwenden getShareableUri Methode getShareableUri unten, um das empfangene Storage Access Framework Uri in Ihre eigenen zu konvertieren. Übergeben Sie diese Uri an andere Apps statt der ursprünglichen Uri.

    Der Code unten ist unsicher (man kann es ganz sicher machen, aber erklären, dass die Länge dieser Antwort über die Vorstellungskraft hinaus erweitert werden würde). Wenn Sie sich interessieren, verwenden Sie die file:// Uris-Linux Dateisysteme sind weithin als sicher genug.

    Wenn Sie die untenstehende Lösung erweitern, um beliebige Dateideskriptoren ohne entsprechende Uri zu liefern, wird als Übung für den Leser ausgelassen.

     public class FdProvider extends ContentProvider { private static final String ORIGINAL_URI = "o"; private static final String FD = "fd"; private static final String PATH = "p"; private static final Uri BASE_URI = Uri.parse("content://com.example.fdhelper/"); // Create an Uri from some other Uri and (optionally) corresponding // file descriptor (if you don't plan to close it until your process is dead). public static Uri getShareableUri(@Nullable ParcelFileDescriptor fd, Uri trueUri) { String path = fd == null ? null : FdCompat.getFdPath(fd); String uri = trueUri.toString(); Uri.Builder builder = BASE_URI.buildUpon(); if (!TextUtils.isEmpty(uri)) builder.appendQueryParameter(ORIGINAL_URI, uri); if (fd != null && !TextUtils.isEmpty(path)) builder.appendQueryParameter(FD, String.valueOf(fd.getFd())) .appendQueryParameter(PATH, path); return builder.build(); } public boolean onCreate() { return true; } public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException { String o = uri.getQueryParameter(ORIGINAL_URI); String fd = uri.getQueryParameter(FD); String path = uri.getQueryParameter(PATH); if (TextUtils.isEmpty(o)) return null; // offer the descriptor directly, if our process still has it try { if (!TextUtils.isEmpty(fd) && !TextUtils.isEmpty(path)) { int intFd = Integer.parseInt(fd); ParcelFileDescriptor desc = ParcelFileDescriptor.fromFd(intFd); if (intFd >= 0 && path.equals(FdCompat.getFdPath(desc))) { return desc; } } } catch (RuntimeException | IOException ignore) {} // otherwise just forward the call try { Uri trueUri = Uri.parse(o); return getContext().getContentResolver() .openFileDescriptor(trueUri, mode); } catch (RuntimeException ignore) {} throw new FileNotFoundException(); } // all other calls are forwarded the same way as above public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { String o = uri.getQueryParameter(ORIGINAL_URI); if (TextUtils.isEmpty(o)) return null; try { Uri trueUri = Uri.parse(o); return getContext().getContentResolver().query(trueUri, projection, selection, selectionArgs, sortOrder); } catch (RuntimeException ignore) {} return null; } public String getType(Uri uri) { String o = uri.getQueryParameter(ORIGINAL_URI); if (TextUtils.isEmpty(o)) return "*/*"; try { Uri trueUri = Uri.parse(o); return getContext().getContentResolver().getType(trueUri); } catch (RuntimeException e) { return null; } } public Uri insert(Uri uri, ContentValues values) { return null; } public int delete(Uri uri, String selection, String[] selectionArgs) { return 0; } public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { return 0; } } 

    Nun, die Lösung wurde bereits auf der SO bereitgestellt und du musst nur danach suchen.

    Das ist die Antwort von Paul Burke . Er hat eine Utility-Klasse geschrieben, die den vollständigen Dateipfad für einen solchen Content-Pfad zurückgibt.

    Er sagt:

    Dies wird der Dateipfad von MediaProvider, DownloadsProvider und ExternalStorageProvider erhalten und dabei auf die inoffizielle ContentProvider-Methode zurückgreifen, die Sie erwähnen.

    und

    Diese werden aus meiner Open Source Library, aFileChooser , entnommen .

    FileUtils.java ist der Ort, an dem Paul Burke die Methode geschrieben hat, nach der du suchst.

    Das Android ist ein Google Android Fan-Website, Alles ├╝ber Android Phones, Android Wear, Android Dev und Android Spiele Apps und so weiter.