Search for:

Android Arsenal – Dateisystem

Sie können diese leichtgewichtige Bibliothek verwenden, um die Anhangsfunktion zu implementieren (Bilder mit der Kamera aufnehmen, Dateien / Bilder aus einer Galerie oder einem Dateisystem oder einer Google-Festplatte herunterladen). Die Bibliothek hilft Ihnen, alle Dateiauswahlprozesse zu vereinfachen, ohne sich Gedanken über Systemberechtigungen machen zu müssen

Sprachunterstützung

Warnung!

  1. Diese Bibliothek wurde mit erstellt AndroidX.Also empfehle ich Ihnen, Ihr Projekt zu migrieren AndroidX Andernfalls kann es zu Problemen bei der gemeinsamen Verwendung von Androidx- und Libo-Unterstützung kommen.

  2. Sie können einem Fehler begegnen Invoke-customs are only supported starting with android 0 --min-api 26 .Um dies zu lösen, fügen Sie unterhalb der Zeile auf Anwendungsebene hinzu build.gradle Datei.

compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
  1. Fügen Sie Berechtigungen und Anbieter hinzu AndroidManifest.xml
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.CAMERA" />
 <provider
            android:name="androidx.core.content.FileProvider"
            android:authorities="${applicationId}.attachmentmanager"
            android:exported="false"
            android:grantUriPermissions="true"
            tools:replace="android:authorities">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_provider"
                tools:replace="android:resource" />
  </provider>
  1. Erstellen file_provider.xml in res/xml
<?xml version="1.0" encoding="utf-8"?>
<paths>
    <external-path
        name="myApp"
        path="Download/" />
    <external-files-path
        name="images"
        path="Pictures" />
</paths>
  1. Wenn Sie auf Android 11+ abzielen, müssen Sie die folgenden Abfragen hinzufügen AndroidManifest.xml
<queries>
        <intent>
            <action android:name="android.intent.action.OPEN_DOCUMENT" />
            <!-- If you don't know the MIME type in advance, set "mimeType" to "*/*". -->
            <data android:mimeType="*/*" />
        </intent>
        <intent>
            <action android:name="android.intent.action.PICK" />
            <!-- If you don't know the MIME type in advance, set "mimeType" to "*/*". -->
            <data android:mimeType="*/*" />
        </intent>
    </queries>
  
  1. Aktualisieren Sie die Projektebene build.gradle Datei.
allprojects {
   repositories {
       jcenter()
        maven { url "https://jitpack.io" }  //Make sure to add this in your project
   }
}
   implementation 'com.github.Zaid-Mirza:AttachmentManager:2.0.1'
  1. Führen Sie das AttachmentManager-Objekt mithilfe des Erstellungsformulars aus

Kotlin

private var attachmentManager: AttachmentManager? = null
var gallery = arrayOf("image/png",
            "image/jpg",
            "image/jpeg")
    var files = arrayOf("application/msword",
            "application/vnd.openxmlformats-officedocument.wordprocessingml.document",  // .ppt & .pptx
            "application/pdf")

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        
      attachmentManager = AttachmentManager.AttachmentBuilder(this) // must pass Context
            .fragment(null) // pass fragment reference if you are in fragment
            .setUiTitle("Choose File") // title of dialog or bottom sheet
            .allowMultiple(false) // set true if you want make multiple selection, default is false
            .asBottomSheet(true) // set true if you need to show selection as bottom sheet, default is as Dialog
            .setOptionsTextColor(android.R.color.holo_green_light) // change text color
            .setImagesColor(R.color.colorAccent) // change icon color
            .hide(HideOption.DOCUMENT) // You can hide any option do you want
            .setMaxPhotoSize(200000) // Set max  photo size in bytes
            .galleryMimeTypes(gallery) // mime types for gallery
            .filesMimeTypes(files) // mime types for files
            .build(); // Hide any of the three options
       
    }
    

Java

    private AttachmentManager attachmentManager = null;
    String[] gallery = {"image/png",
            "image/jpg",
            "image/jpeg"};
    String[] files  = { "application/msword",
            "application/vnd.openxmlformats-officedocument.wordprocessingml.document", // .ppt & .pptx
            "application/pdf"};

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
       attachmentManager = new AttachmentManager.AttachmentBuilder(this) // must pass Context
                .fragment(null) // pass fragment reference if you are in fragment
                .setUiTitle("Choose File") // title of dialog or bottom sheet
                .allowMultiple(false) // set true if you want make multiple selection, default is false
                .asBottomSheet(true) // set true if you need to show selection as bottom sheet, default is as Dialog
                .setOptionsTextColor(android.R.color.holo_green_light) // change text color
                .setImagesColor(R.color.colorAccent) // change icon color
                .hide(HideOption.DOCUMENT) // You can hide any option do you want
                .setMaxPhotoSize(200000) // Set max  photo size in bytes
                .galleryMimeTypes(gallery) // mime types for gallery
                .filesMimeTypes(files) // mime types for files
                .build(); // Hide any of the three options
    }
  1. Deklarieren Sie registerForActivityResult

Kotlin

 private var mLauncher = registerForActivityResult(StartActivityForResult()) { result ->

    val list =  attachmentManager?.manipulateAttachments(this,result.resultCode,result.data)
    Toast.makeText(this, list?.size.toString(), Toast.LENGTH_LONG).show()
}

Java

 ActivityResultLauncher<Intent> mLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> {

        ArrayList<AttachmentDetail> list = attachmentManager.manipulateAttachments(this,result.getResultCode(),result.getData());

        });
  1. Anruf openSelection () Methode zum Anzeigen der Benutzeroberfläche zum Auswählen und Weiterleiten von ActivityResultLauncher

Kotlin

 attachmentManager?.openSelection(mLauncher)

Java

attachmentManager.openSelection(mLauncher);
  1. OnRequestPermissionsResult rückgängig machen (optional)

Kotlin

override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
        attachmentManager?.handlePermissionResponse(requestCode, permissions, grantResults)
    }

Java

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        attachmentManager.handlePermissionResponse(requestCode,permissions,grantResults);
    }

Andere Verwendungen

  1. Sie können eine Galerie, Kamera oder ein Dateisystem direkt öffnen, ohne dem Benutzer die Benutzerauswahlschnittstelle anzuzeigen

Kotlin

 attachmentManager?.startCamera(mLauncher)
 // OR
 attachmentManager?.openGallery(mLauncher)
 // OR
 attachmentManager?.openFilSystem(mLauncher)

Java

 attachmentManager.startCamera(mLauncher);
 // OR
 attachmentManager.openGallery(mLauncher);
 // OR
 attachmentManager.openFilSystem(mLauncher);

Notiz

Jede Art von Verbesserungen und Vorschlägen sind willkommen. Wenn Sie diese Bibliothek in Ihrem Projekt verwenden, teilen Sie mir bitte auch die URL Ihrer Anwendung mit. Ich werde Ihre Anwendung hier auflisten.

Android Arsenal – Dateisystem

Inhalt

Überblick

Je höher das API-Level, desto stärker beschränkt Google den Zugriff auf Dateien im Android-Speicher. Obwohl das Storage Access Framework (SAF) darauf ausgelegt ist, den Benutzerspeicher vor bösartigen Anwendungen zu schützen, macht es uns den Zugriff auf Dateien noch schwerer. Nehmen Sie das Beispiel wo java.io.File ist in Android 10 veraltet.

Die einfache Speicherung erleichtert Ihnen den Zugriff auf und die Verwaltung von Dateien auf allen Ebenen der API. Wenn Sie mehr über den Hintergrund dieser Bibliothek erfahren möchten, lesen Sie diesen Artikel: Framework für einfachen Speicherzugriff in Android mit SimpleStorage

Das Hinzufügen von Simple Storage zu Ihrem Projekt ist ziemlich einfach:

implementation "com.anggrayudi:storage:X.Y.Z"

Wo X.Y.Z ist eine Version der Bibliothek:

Alle Versionen sind zu finden Hier. Benutzen SNAPSHOT Version müssen Sie diese URL zu Root-Gradle hinzufügen:

allprojects {
    repositories {
        google()
        mavenCentral()
        // add this line
        maven { url "https://oss.sonatype.org/content/repositories/snapshots" }
    }
}

Java-Kompatibilität

Simple Storage wurde in Kotlin erstellt. Folge dies Dokumentation um es in Ihrem Java-Projekt zu verwenden.

Terminologie

Andere Terminologie

Dateien lesen

In einfacher Lagerung, DocumentFile Wird verwendet, um auf Dateien zuzugreifen, wenn Ihrer Anwendung vollständiger Speicherzugriff gewährt wurde, einschließlich Lese- und Schreibberechtigungs-URIs. Während MediaFile wird verwendet, um auf Mediendateien zuzugreifen MediaStore keine Speicherberechtigungs-URIs.

Sie können die Hilfsfunktionsdatei einlesen DocumentFileCompat ich MediaStoreCompat:

DocumentFileCompat

  • DocumentFileCompat.fromFullPath()
  • DocumentFileCompat.fromSimplePath()
  • DocumentFileCompat.fromFile()
  • DocumentFileCompat.fromPublicFolder()

Beispiel

val fileFromExternalStorage = DocumentFileCompat.fromSimplePath(context, basePath = "Downloads/MyMovie.mp4")

val fileFromSdCard = DocumentFileCompat.fromSimplePath(context, storageId = "9016-4EF8", basePath = "Downloads/MyMovie.mp4")

MediaStoreCompat

  • MediaStoreCompat.fromMediaId()
  • MediaStoreCompat.fromFileName()
  • MediaStoreCompat.fromRelativePath()
  • MediaStoreCompat.fromFileNameContains()
  • MediaStoreCompat.fromMimeType()
  • MediaStoreCompat.fromMediaType()

Beispiel

val myVideo = MediaStoreCompat.fromFileName(context, MediaType.DOWNLOADS, "MyMovie.mp4")

val imageList = MediaStoreCompat.fromMediaType(context, MediaType.IMAGE)

Dokumentenverwaltung

DocumentFile

Denn java.io.File ist in Android 10 veraltet, also müssen Sie es verwenden DocumentFile um Dateien zu verwalten.

Simple Storage fügt Kotlina-Erweiterungsfunktionen hinzu DocumentFileSo können Sie Dateien wie folgt verwalten:

  • DocumentFile.getStorageId()
  • DocumentFile.getStorageType()
  • DocumentFile.getBasePath()
  • DocumentFile.copyFileTo()
  • List<DocumentFile>.moveTo()
  • DocumentFile.search()
  • DocumentFile.deleteRecursively()
  • DocumentFile.getProperties()
  • DocumentFile.openOutputStream()und vieles mehr…

MediaFile

Sie können ähnliche Optionen für Mediendateien haben DocumentFileoder:

  • MediaFile.absolutePath
  • MediaFile.isPending
  • MediaFile.delete()
  • MediaFile.renameTo()
  • MediaFile.copyFileTo()
  • MediaFile.moveFileTo()
  • MediaFile.openInputStream()
  • MediaFile.openOutputStream()etc.

Fordern Sie Zugang zum Lager an

Obwohl der Benutzer während der Laufzeit Lese- und Schreibberechtigungen erteilt hat, hat Ihre Anwendung möglicherweise noch keinen vollständigen Zugriff auf das Repository, sodass Sie keine Dateien suchen, verschieben und kopieren können. Ob Sie Zugriff auf das Lager haben, können Sie über prüfen SimpleStorage.hasStorageAccess().

Um vollen Zugriff auf das Repository zu gewähren, müssen Sie das SAF öffnen und dem Benutzer erlauben, URIs Lese- und Schreibzugriffsberechtigungen zu erteilen. Diese Bibliothek bietet Ihnen eine Hilfsklasse namens SimpleStorage um den Bewerbungsprozess zu erleichtern:

class MainActivity : AppCompatActivity() {

    private val storage = SimpleStorage(this)

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        setupSimpleStorage()
        btnRequestStorageAccess.setOnClickListener {
            storage.requestStorageAccess(REQUEST_CODE_STORAGE_ACCESS)
        }
    }

    private fun setupSimpleStorage() {
        storage.storageAccessCallback = object : StorageAccessCallback {
            override fun onRootPathNotSelected(
                requestCode: Int,
                rootPath: String,
                uri: Uri,
                selectedStorageType: StorageType,
                expectedStorageType: StorageType
            ) {
                MaterialDialog(this@MainActivity)
                    .message(text = "Please select $rootPath")
                    .negativeButton(android.R.string.cancel)
                    .positiveButton {
                        val initialRoot = if (expectedStorageType.isExpected(selectedStorageType)) selectedStorageType else expectedStorageType
                        storage.requestStorageAccess(REQUEST_CODE_STORAGE_ACCESS, initialRoot, expectedStorageType)
                    }.show()
            }

            override fun onCanceledByUser(requestCode: Int) {
                Toast.makeText(baseContext, "Canceled by user", Toast.LENGTH_SHORT).show()
            }

            override fun onStoragePermissionDenied(requestCode: Int) {
                /*
                Request runtime permissions for Manifest.permission.WRITE_EXTERNAL_STORAGE
                and Manifest.permission.READ_EXTERNAL_STORAGE
                */
            }

            override fun onRootPathPermissionGranted(requestCode: Int, root: DocumentFile) {
                Toast.makeText(baseContext, "Storage access has been granted for ${root.getStorageId(baseContext)}", Toast.LENGTH_SHORT).show()
            }
        }
    }

    override fun onSaveInstanceState(outState: Bundle) {
        storage.onSaveInstanceState(outState)
        super.onSaveInstanceState(outState)
    }

    override fun onRestoreInstanceState(savedInstanceState: Bundle) {
        super.onRestoreInstanceState(savedInstanceState)
        storage.onRestoreInstanceState(savedInstanceState)
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        // Mandatory for Activity, but not for Fragment & ComponentActivity
        storage.onActivityResult(requestCode, resultCode, data)
    }

    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        // Mandatory for Activity, but not for Fragment & ComponentActivity
        storage.onRequestPermissionsResult(requestCode, permissions, grantResults)
    }
}

Ordnerauswahl

private fun requestStoragePermission() {
    /*
    Request runtime permissions for Manifest.permission.WRITE_EXTERNAL_STORAGE
    and Manifest.permission.READ_EXTERNAL_STORAGE
    */
}

private fun setupFolderPickerCallback() {
    storage.folderPickerCallback = object : FolderPickerCallback {
        override fun onStoragePermissionDenied(requestCode: Int) {
            requestStoragePermission()
        }

        override fun onStorageAccessDenied(requestCode: Int, folder: DocumentFile?, storageType: StorageType) {
            if (storageType == StorageType.UNKNOWN) {
                requestStoragePermission()
                return
            }
            MaterialDialog(this@MainActivity)
                .message(
                    text = "You have no write access to this storage, thus selecting this folder is useless." +
                            "nWould you like to grant access to this folder?"
                )
                .negativeButton(android.R.string.cancel)
                .positiveButton {
                    storage.requestStorageAccess(REQUEST_CODE_STORAGE_ACCESS, storageType)
                }.show()
        }

        override fun onFolderSelected(requestCode: Int, folder: DocumentFile) {
            Toast.makeText(baseContext, folder.getAbsolutePath(baseContext), Toast.LENGTH_SHORT).show()
        }

        override fun onCanceledByUser(requestCode: Int) {
            Toast.makeText(baseContext, "Folder picker canceled by user", Toast.LENGTH_SHORT).show()
        }
    }
}

Dateiauswahl

private fun setupFilePickerCallback() {
    storage.filePickerCallback = object : FilePickerCallback {
        override fun onCanceledByUser(requestCode: Int) {
            Toast.makeText(baseContext, "File picker canceled by user", Toast.LENGTH_SHORT).show()
        }

        override fun onStoragePermissionDenied(requestCode: Int, file: DocumentFile?) {
            requestStoragePermission()
        }

        override fun onFileSelected(requestCode: Int, file: DocumentFile) {
            Toast.makeText(baseContext, "File selected: ${file.name}", Toast.LENGTH_SHORT).show()
        }
    }
}

SimpleStorageHelper

Wenn Sie die Implementierung der Ordner- und Dateiauswahl und der vollständigen Festplattenanforderungen kompliziert genug finden, können Sie verwenden SimpleStorageHelper um den Prozess zu vereinfachen. Diese Hilfsklasse enthält Standardstile für die Verwaltung des Warehouse-Zugriffs.

class MainActivity : AppCompatActivity() {

    private val storageHelper = SimpleStorageHelper(this)

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        storageHelper.onFolderSelected = { requestCode, folder ->
            // do stuff
        }
        storageHelper.onFileSelected = { requestCode, files ->
            // do stuff
        }

        btnRequestStorageAccess.setOnClickListener { storageHelper.requestStorageAccess() }
        btnOpenFolderPicker.setOnClickListener { storageHelper.openFolderPicker() }
        btnOpenFilePicker.setOnClickListener { storageHelper.openFilePicker() }
    }

    override fun onSaveInstanceState(outState: Bundle) {
        storageHelper.onSaveInstanceState(outState)
        super.onSaveInstanceState(outState)
    }

    override fun onRestoreInstanceState(savedInstanceState: Bundle) {
        super.onRestoreInstanceState(savedInstanceState)
        storageHelper.onRestoreInstanceState(savedInstanceState)
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        // Mandatory for Activity, but not for Fragment & ComponentActivity
        storageHelper.storage.onActivityResult(requestCode, resultCode, data)
    }

    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        // Mandatory for Activity, but not for Fragment & ComponentActivity
        storageHelper.onRequestPermissionsResult(requestCode, permissions, grantResults)
    }
}

Einfacher, oder?

Verschieben und kopieren: Dateien und Ordner

Simple Storage hilft Ihnen beim Kopieren/Verschieben von Dateien und Ordnern über:

  • DocumentFile.copyFileTo()
  • DocumentFile.moveFileTo()
  • DocumentFile.copyFolderTo()
  • DocumentFile.moveFolderTo()

Sie können beispielsweise einen Ordner mit mehreren Codezeilen verschieben:

val folder: DocumentFile = ...
val targetFolder: DocumentFile = ...

// Since moveFolderTo() is annotated with @WorkerThread, you must execute it in background thread
folder.moveFolderTo(applicationContext, targetFolder, skipEmptyFiles = false, callback = object : FolderCallback() {
    override fun onPrepare() {
        // Show notification or progress bar dialog with indeterminate state
    }

    override fun onCountingFiles() {
        // Inform user that the app is counting & calculating files
    }

    override fun onStart(folder: DocumentFile, totalFilesToCopy: Int, workerThread: Thread): Long {
        return 1000 // update progress every 1 second
    }

    override fun onParentConflict(destinationFolder: DocumentFile, action: FolderCallback.ParentFolderConflictAction, canMerge: Boolean) {
        handleParentFolderConflict(destinationFolder, action, canMerge)
    }

    override fun onContentConflict(
        destinationFolder: DocumentFile,
        conflictedFiles: MutableList<FolderCallback.FileConflict>,
        action: FolderCallback.FolderContentConflictAction
    ) {
        handleFolderContentConflict(action, conflictedFiles)
    }

    override fun onReport(report: Report) {
        Timber.d("onReport() -> ${report.progress.toInt()}% | Copied ${report.fileCount} files")
    }

    override fun onCompleted(result: Result) {
        Toast.makeText(baseContext, "Copied ${result.totalCopiedFiles} of ${result.totalFilesToCopy} files", Toast.LENGTH_SHORT).show()
    }

    override fun onFailed(errorCode: ErrorCode) {
        Toast.makeText(baseContext, "An error has occurred: $errorCode", Toast.LENGTH_SHORT).show()
    }
})

Das Beste an dieser Bibliothek ist, dass Sie Benutzer bitten können, „Zusammenführen“, „Ersetzen“, „Neu erstellen“ oder „Doppelte Ordner und Dateien überspringen“ zu wählen, wenn ein Konflikt über gefunden wird onConflict(). Hier sind Screenshots des Codebeispiels zum Umgang mit Konflikten:

Lesen MainActivity aus dem Beispielcode, wenn Sie die obigen Dialoge imitieren möchten.

FAQ

Probleme? Weiter lesen Häufig gestellte Fragen.

Weitere Beispiele für die Verwendung von SimpleStorage

In diesen Open-Source-Projekten kommt SimpleStorage zum Einsatz. Sehen Sie sich an, wie diese Repositories es verwenden:

Lizenz

Copyright © 2020-2022 Anggrayudi Hardiannico A.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.