Paylaşılan depolama alanındaki dokümanlara ve diğer dosyalara erişme

Uygulamanız, Android 4.4 (API düzeyi 19) ve sonraki sürümleri çalıştıran cihazlarda Depolama Erişim Çerçevesi'ni kullanarak harici depolama birimleri ve bulut tabanlı depolama da dahil olmak üzere bir belge sağlayıcı ile etkileşim kurabilir. Bu çerçeve, kullanıcıların bir sistem seçiciyle etkileşim kurarak belge sağlayıcısı seçmesine ve uygulamanızın oluşturması, açması veya değiştirmesi için belirli dokümanları ve diğer dosyaları seçmesine olanak tanır.

Uygulamanızın erişebileceği dosyaları veya dizinleri seçme süreci kullanıcıda rol oynadığından, bu mekanizma herhangi bir sistem izni gerektirmez ve kullanıcı denetimi ve gizliliği geliştirilir. Ayrıca, uygulamaya özel bir dizinin ve medya mağazasının dışında depolanan bu dosyalar, uygulamanız kaldırıldıktan sonra cihazda kalır.

Çerçeveyi kullanmak aşağıdaki adımları içerir:

  1. Bir uygulama, depolama alanıyla ilgili işlem içeren bir niyet çağırır. Bu işlem, çerçevenin kullanıma sunduğu belirli bir kullanım alanına karşılık gelir.
  2. Kullanıcıya, doküman sağlayıcıya göz atmasına ve depolamayla ilgili işlemin gerçekleştirileceği konumu veya dokümanı seçmesine olanak tanıyan bir sistem seçici gösterilir.
  3. Uygulama, kullanıcının seçilen konumunu veya belgesini temsil eden bir URI'ye okuma ve yazma erişimi elde eder. Uygulama, bu URI'yi kullanarak seçilen konumda işlem gerçekleştirebilir.

Android 9 (API düzeyi 28) veya önceki sürümleri çalıştıran cihazlarda medya dosyası erişimini desteklemek için READ_EXTERNAL_STORAGE iznini beyan edin ve maxSdkVersion özelliğini 28 olarak ayarlayın.

Bu kılavuzda, çerçevenin dosyalarla ve diğer belgelerle çalışmak için desteklediği farklı kullanım alanları açıklanmaktadır. Ayrıca, kullanıcı tarafından seçilen konumda işlemlerin nasıl gerçekleştirileceği de açıklanmaktadır.

Dokümanlara ve diğer dosyalara erişim için kullanım alanları

Depolama Erişim Çerçevesi, dosyalara ve diğer belgelere erişmek için aşağıdaki kullanım alanlarını destekler.

Yeni dosya oluşturma
ACTION_CREATE_DOCUMENT amaç işlemi, kullanıcıların dosyayı belirli bir konuma kaydetmesine olanak tanır.
Doküman veya dosya açma
ACTION_OPEN_DOCUMENT amaç işlemi, kullanıcıların açacakları belirli bir dokümanı veya dosyayı seçmesine olanak tanır.
Bir dizin içeriğine erişim izni verme
Android 5.0 (API düzeyi 21) ve sonraki sürümlerde kullanılabilen ACTION_OPEN_DOCUMENT_TREE amaç işlemi, kullanıcıların belirli bir dizini seçmesine olanak tanıyarak uygulamanızın ilgili dizindeki tüm dosyalara ve alt dizinlere erişmesine izin verir.

Aşağıdaki bölümlerde, her bir kullanım alanının nasıl yapılandırılacağı konusunda yol gösterilmektedir.

Yeni dosya oluşturma

Sistem dosyası seçiciyi yüklemek ve kullanıcının dosya içeriğinin yazılacağı konumu seçmesine izin vermek için ACTION_CREATE_DOCUMENT amaç işlemini kullanın. Bu işlem, diğer işletim sistemlerinin kullandığı "farklı kaydet" iletişim kutularında kullanılan işleme benzer.

Not: ACTION_CREATE_DOCUMENT mevcut bir dosyanın üzerine yazamaz. Uygulamanız aynı adla bir dosya kaydetmeye çalışırsa sistem, dosya adının sonuna parantez içinde bir sayı ekler.

Örneğin, uygulamanız confirmation.pdf adlı bir dosyayı zaten bu ada sahip bir dosyanın bulunduğu bir dizine kaydetmeye çalışırsa sistem yeni dosyayı confirmation(1).pdf adıyla kaydeder.

Niyeti yapılandırırken dosyanın adını ve MIME türünü belirtin ve dilerseniz EXTRA_INITIAL_URI amaç ekstrasını kullanarak dosya seçicinin ilk yüklendiğinde görüntülemesi gereken dosya veya dizinin URI'sını belirtin.

Aşağıdaki kod snippet'i, dosya oluşturma amacını nasıl oluşturacağınızı ve çağıracağınızı gösterir:

Kotlin

// Request code for creating a PDF document.
const val CREATE_FILE = 1

private fun createFile(pickerInitialUri: Uri) {
    val intent = Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
        addCategory(Intent.CATEGORY_OPENABLE)
        type = "application/pdf"
        putExtra(Intent.EXTRA_TITLE, "invoice.pdf")

        // Optionally, specify a URI for the directory that should be opened in
        // the system file picker before your app creates the document.
        putExtra(DocumentsContract.EXTRA_INITIAL_URI, pickerInitialUri)
    }
    startActivityForResult(intent, CREATE_FILE)
}

Java

// Request code for creating a PDF document.
private static final int CREATE_FILE = 1;

private void createFile(Uri pickerInitialUri) {
    Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
    intent.addCategory(Intent.CATEGORY_OPENABLE);
    intent.setType("application/pdf");
    intent.putExtra(Intent.EXTRA_TITLE, "invoice.pdf");

    // Optionally, specify a URI for the directory that should be opened in
    // the system file picker when your app creates the document.
    intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, pickerInitialUri);

    startActivityForResult(intent, CREATE_FILE);
}

Dosya aç

Kullanıcılar, dokümanları, benzerleriyle paylaşmak veya başka dokümanlara aktarmak isteyebilecekleri verileri girdikleri depolama birimi olarak kullanabilir. Bir kullanıcının üretkenlik dokümanını veya EPUB dosyası olarak kaydedilmiş bir kitabı açmasını örnek olarak verebiliriz.

Bu gibi durumlarda, kullanıcının ACTION_OPEN_DOCUMENT niyetini çağırarak açılacak dosyayı seçmesine izin verin. Bu işlem sistemin dosya seçici uygulamasını açar. Yalnızca uygulamanızın desteklediği dosya türlerini görmek için bir MIME türü belirtin. Ayrıca, isteğe bağlı olarak EXTRA_INITIAL_URI amaç ekstrasını kullanarak dosya seçicinin ilk yüklendiğinde görüntülemesi gereken dosya URI'sını belirtebilirsiniz.

Aşağıdaki kod snippet'i, bir PDF dokümanını açma niyetinin nasıl oluşturulacağını ve çağrılacağını gösterir:

Kotlin

// Request code for selecting a PDF document.
const val PICK_PDF_FILE = 2

fun openFile(pickerInitialUri: Uri) {
    val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
        addCategory(Intent.CATEGORY_OPENABLE)
        type = "application/pdf"

        // Optionally, specify a URI for the file that should appear in the
        // system file picker when it loads.
        putExtra(DocumentsContract.EXTRA_INITIAL_URI, pickerInitialUri)
    }

    startActivityForResult(intent, PICK_PDF_FILE)
}

Java

// Request code for selecting a PDF document.
private static final int PICK_PDF_FILE = 2;

private void openFile(Uri pickerInitialUri) {
    Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
    intent.addCategory(Intent.CATEGORY_OPENABLE);
    intent.setType("application/pdf");

    // Optionally, specify a URI for the file that should appear in the
    // system file picker when it loads.
    intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, pickerInitialUri);

    startActivityForResult(intent, PICK_PDF_FILE);
}

Erişim kısıtlamaları

Android 11 (API düzeyi 30) ve sonraki sürümlerde, kullanıcının aşağıdaki dizinlerden ayrı ayrı dosyalar seçmesini istemek için ACTION_OPEN_DOCUMENT intent işlemini kullanamazsınız:

  • Android/data/ dizini ve tüm alt dizinler.
  • Android/obb/ dizini ve tüm alt dizinler.

Dizin içeriğine erişim izni verme

Dosya yönetimi ve medya oluşturma uygulamaları genellikle bir dizin hiyerarşisindeki dosya gruplarını yönetir. Uygulamanızda bu özelliği sağlamak için ACTION_OPEN_DOCUMENT_TREE amaç işlemini kullanın. Bu işlem, Android 11'den (API düzeyi 30) başlayan bazı istisnalarla birlikte kullanıcının tüm dizin ağacına erişim izni vermesine olanak tanır. Böylece uygulamanız, seçili dizindeki ve bu dizinin alt dizinlerindeki tüm dosyalara erişebilir.

ACTION_OPEN_DOCUMENT_TREE kullanıldığında uygulamanız yalnızca kullanıcının seçtiği dizindeki dosyalara erişim kazanır. Kullanıcı tarafından seçilen dizinin dışında bulunan diğer uygulamaların dosyalarına erişiminiz yoktur. Kullanıcı tarafından kontrol edilen bu erişim, kullanıcıların uygulamanızla tam olarak hangi içeriği paylaşmak istediklerini seçmelerine olanak tanır.

İsteğe bağlı olarak, EXTRA_INITIAL_URI amaç ekstrasını kullanarak dosya seçicinin ilk yüklendiğinde görüntülemesi gereken dizinin URI'sini belirtebilirsiniz.

Aşağıdaki kod snippet'i, bir dizin açma niyetinin nasıl oluşturulacağını ve çağrılacağını gösterir:

Kotlin

fun openDirectory(pickerInitialUri: Uri) {
    // Choose a directory using the system's file picker.
    val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).apply {
        // Optionally, specify a URI for the directory that should be opened in
        // the system file picker when it loads.
        putExtra(DocumentsContract.EXTRA_INITIAL_URI, pickerInitialUri)
    }

    startActivityForResult(intent, your-request-code)
}

Java

public void openDirectory(Uri uriToLoad) {
    // Choose a directory using the system's file picker.
    Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);

    // Optionally, specify a URI for the directory that should be opened in
    // the system file picker when it loads.
    intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, uriToLoad);

    startActivityForResult(intent, your-request-code);
}

Erişim kısıtlamaları

Android 11 (API düzeyi 30) ve sonraki sürümlerde, aşağıdaki dizinlere erişim isteğinde bulunmak için ACTION_OPEN_DOCUMENT_TREE intent işlemini kullanamazsınız:

  • Dahili depolama biriminin kök dizini.
  • Kartın emüle edilmiş veya çıkarılabilir olmasına bakılmaksızın, cihaz üreticisinin güvenilir olarak kabul ettiği her SD kart biriminin kök dizini. Güvenilir bir birim, bir uygulamanın çoğu zaman başarıyla erişebildiği hacimdir.
  • Download dizini.

Ayrıca, Android 11 (API düzeyi 30) ve sonraki sürümlerde, ACTION_OPEN_DOCUMENT_TREE intent işlemini kullanarak kullanıcıdan aşağıdaki dizinlerdeki dosyaları tek tek seçmesini isteyemezsiniz:

  • Android/data/ dizini ve tüm alt dizinler.
  • Android/obb/ dizini ve tüm alt dizinler.

Seçilen konumda işlemleri gerçekleştirin

Kullanıcı, sistemin dosya seçiciyi kullanarak dosya veya dizin seçtikten sonra, onActivityResult() içinde aşağıdaki kodu kullanarak seçili öğenin URI'sını alabilirsiniz:

Kotlin

override fun onActivityResult(
        requestCode: Int, resultCode: Int, resultData: Intent?) {
    if (requestCode == your-request-code
            && resultCode == Activity.RESULT_OK) {
        // The result data contains a URI for the document or directory that
        // the user selected.
        resultData?.data?.also { uri ->
            // Perform operations on the document using its URI.
        }
    }
}

Java

@Override
public void onActivityResult(int requestCode, int resultCode,
        Intent resultData) {
    if (requestCode == your-request-code
            && resultCode == Activity.RESULT_OK) {
        // The result data contains a URI for the document or directory that
        // the user selected.
        Uri uri = null;
        if (resultData != null) {
            uri = resultData.getData();
            // Perform operations on the document using its URI.
        }
    }
}

Uygulamanız, seçili öğenin URI'sına bir referans alarak öğe üzerinde çeşitli işlemler gerçekleştirebilir. Örneğin, öğenin meta verilerine erişebilir, öğeyi yerinde düzenleyebilir ve öğeyi silebilirsiniz.

Aşağıdaki bölümlerde, kullanıcının seçtiği dosyalarda işlemlerin nasıl tamamlanacağı gösterilmektedir.

Bir sağlayıcının desteklediği işlemleri belirleme

Farklı içerik sağlayıcıları, belgeler üzerinde farklı işlemlerin (dokümanı kopyalama veya belgenin küçük resmini görüntüleme gibi) gerçekleştirilmesine izin verir. Belirli bir sağlayıcının hangi işlemleri desteklediğini belirlemek için Document.COLUMN_FLAGS değerini kontrol edin. Böylece uygulamanızın kullanıcı arayüzünde yalnızca sağlayıcının desteklediği seçenekler gösterilebilir.

İzinleri koru

Uygulamanız, okuma veya yazma için bir dosya açtığında sistem uygulamanıza o dosya için bir URI izin izni verir. Bu izin, kullanıcının cihazı yeniden başlatılana kadar geçerli olur. Ancak, uygulamanızın bir resim düzenleme uygulaması olduğunu ve kullanıcıların en son düzenledikleri 5 resme doğrudan uygulamanızdan erişebilmelerini istediğinizi varsayalım. Kullanıcının cihazı yeniden başlatılmışsa, dosyaları bulmak için kullanıcıyı sistem seçiciye geri göndermeniz gerekir.

Uygulamanız, cihaz yeniden başlatıldığında dosyalara erişimi korumak ve daha iyi bir kullanıcı deneyimi sunmak için sistemin sunduğu kalıcı URI izni iznini aşağıdaki kod snippet'inde gösterildiği gibi "alabilir":

Kotlin

val contentResolver = applicationContext.contentResolver

val takeFlags: Int = Intent.FLAG_GRANT_READ_URI_PERMISSION or
        Intent.FLAG_GRANT_WRITE_URI_PERMISSION
// Check for the freshest data.
contentResolver.takePersistableUriPermission(uri, takeFlags)

Java

final int takeFlags = intent.getFlags()
            & (Intent.FLAG_GRANT_READ_URI_PERMISSION
            | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
// Check for the freshest data.
getContentResolver().takePersistableUriPermission(uri, takeFlags);

Doküman meta verilerini inceleme

Bir dokümanın URI'sına sahip olduğunuzda dokümanın meta verilerine erişirsiniz. Bu snippet, URI tarafından belirtilen bir dokümanın meta verilerini alır ve günlüğe kaydeder:

Kotlin

val contentResolver = applicationContext.contentResolver

fun dumpImageMetaData(uri: Uri) {

    // The query, because it only applies to a single document, returns only
    // one row. There's no need to filter, sort, or select fields,
    // because we want all fields for one document.
    val cursor: Cursor? = contentResolver.query(
            uri, null, null, null, null, null)

    cursor?.use {
        // moveToFirst() returns false if the cursor has 0 rows. Very handy for
        // "if there's anything to look at, look at it" conditionals.
        if (it.moveToFirst()) {

            // Note it's called "Display Name". This is
            // provider-specific, and might not necessarily be the file name.
            val displayName: String =
                    it.getString(it.getColumnIndex(OpenableColumns.DISPLAY_NAME))
            Log.i(TAG, "Display Name: $displayName")

            val sizeIndex: Int = it.getColumnIndex(OpenableColumns.SIZE)
            // If the size is unknown, the value stored is null. But because an
            // int can't be null, the behavior is implementation-specific,
            // and unpredictable. So as
            // a rule, check if it's null before assigning to an int. This will
            // happen often: The storage API allows for remote files, whose
            // size might not be locally known.
            val size: String = if (!it.isNull(sizeIndex)) {
                // Technically the column stores an int, but cursor.getString()
                // will do the conversion automatically.
                it.getString(sizeIndex)
            } else {
                "Unknown"
            }
            Log.i(TAG, "Size: $size")
        }
    }
}

Java

public void dumpImageMetaData(Uri uri) {

    // The query, because it only applies to a single document, returns only
    // one row. There's no need to filter, sort, or select fields,
    // because we want all fields for one document.
    Cursor cursor = getActivity().getContentResolver()
            .query(uri, null, null, null, null, null);

    try {
        // moveToFirst() returns false if the cursor has 0 rows. Very handy for
        // "if there's anything to look at, look at it" conditionals.
        if (cursor != null && cursor.moveToFirst()) {

            // Note it's called "Display Name". This is
            // provider-specific, and might not necessarily be the file name.
            String displayName = cursor.getString(
                    cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
            Log.i(TAG, "Display Name: " + displayName);

            int sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE);
            // If the size is unknown, the value stored is null. But because an
            // int can't be null, the behavior is implementation-specific,
            // and unpredictable. So as
            // a rule, check if it's null before assigning to an int. This will
            // happen often: The storage API allows for remote files, whose
            // size might not be locally known.
            String size = null;
            if (!cursor.isNull(sizeIndex)) {
                // Technically the column stores an int, but cursor.getString()
                // will do the conversion automatically.
                size = cursor.getString(sizeIndex);
            } else {
                size = "Unknown";
            }
            Log.i(TAG, "Size: " + size);
        }
    } finally {
        cursor.close();
    }
}

Doküman açma

Bir dokümanın URI'sına referans vererek dokümanı daha fazla işlenmesi için açabilirsiniz. Bu bölümde bit eşlem ve giriş akışı açma örnekleri gösterilmektedir.

Bit eşlem

Aşağıdaki kod snippet'i, URI'si ile bir Bitmap dosyasının nasıl açılacağını gösterir:

Kotlin

val contentResolver = applicationContext.contentResolver

@Throws(IOException::class)
private fun getBitmapFromUri(uri: Uri): Bitmap {
    val parcelFileDescriptor: ParcelFileDescriptor =
            contentResolver.openFileDescriptor(uri, "r")
    val fileDescriptor: FileDescriptor = parcelFileDescriptor.fileDescriptor
    val image: Bitmap = BitmapFactory.decodeFileDescriptor(fileDescriptor)
    parcelFileDescriptor.close()
    return image
}

Java

private Bitmap getBitmapFromUri(Uri uri) throws IOException {
    ParcelFileDescriptor parcelFileDescriptor =
            getContentResolver().openFileDescriptor(uri, "r");
    FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();
    Bitmap image = BitmapFactory.decodeFileDescriptor(fileDescriptor);
    parcelFileDescriptor.close();
    return image;
}

Bit eşlemi açtıktan sonra ImageView içinde görüntüleyebilirsiniz.

Giriş akışı

Aşağıdaki kod snippet'i, URI'si ile bir InputStream nesnesinin nasıl açılacağını göstermektedir. Bu snippet'te dosyanın satırları bir dizeye dönüştürülmektedir:

Kotlin

val contentResolver = applicationContext.contentResolver

@Throws(IOException::class)
private fun readTextFromUri(uri: Uri): String {
    val stringBuilder = StringBuilder()
    contentResolver.openInputStream(uri)?.use { inputStream ->
        BufferedReader(InputStreamReader(inputStream)).use { reader ->
            var line: String? = reader.readLine()
            while (line != null) {
                stringBuilder.append(line)
                line = reader.readLine()
            }
        }
    }
    return stringBuilder.toString()
}

Java

private String readTextFromUri(Uri uri) throws IOException {
    StringBuilder stringBuilder = new StringBuilder();
    try (InputStream inputStream =
            getContentResolver().openInputStream(uri);
            BufferedReader reader = new BufferedReader(
            new InputStreamReader(Objects.requireNonNull(inputStream)))) {
        String line;
        while ((line = reader.readLine()) != null) {
            stringBuilder.append(line);
        }
    }
    return stringBuilder.toString();
}

Doküman düzenleme

Mevcut bir metin dokümanını düzenlemek için Depolama Erişim Çerçevesi'ni kullanabilirsiniz.

Aşağıdaki kod snippet'i, belirtilen URI tarafından temsil edilen belge içeriğinin üzerine yazar:

Kotlin

val contentResolver = applicationContext.contentResolver

private fun alterDocument(uri: Uri) {
    try {
        contentResolver.openFileDescriptor(uri, "w")?.use {
            FileOutputStream(it.fileDescriptor).use {
                it.write(
                    ("Overwritten at ${System.currentTimeMillis()}\n")
                        .toByteArray()
                )
            }
        }
    } catch (e: FileNotFoundException) {
        e.printStackTrace()
    } catch (e: IOException) {
        e.printStackTrace()
    }
}

Java

private void alterDocument(Uri uri) {
    try {
        ParcelFileDescriptor pfd = getActivity().getContentResolver().
                openFileDescriptor(uri, "w");
        FileOutputStream fileOutputStream =
                new FileOutputStream(pfd.getFileDescriptor());
        fileOutputStream.write(("Overwritten at " + System.currentTimeMillis() +
                "\n").getBytes());
        // Let the document provider know you're done by closing the stream.
        fileOutputStream.close();
        pfd.close();
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

Doküman silme

Bir dokümanın URI'sına sahipseniz ve Document.COLUMN_FLAGS öğesi SUPPORTS_DELETE öğesini içeriyorsa dokümanı silebilirsiniz. Örneğin:

Kotlin

DocumentsContract.deleteDocument(applicationContext.contentResolver, uri)

Java

DocumentsContract.deleteDocument(applicationContext.contentResolver, uri);

Eşdeğer bir medya URI'si al

getMediaUri() yöntemi, belirtilen belge sağlayıcı URI'sına eşdeğer bir medya mağazası URI'si sağlar. 2 URI aynı temel öğeyi ifade eder. Medya deposu URI'sini kullanarak paylaşılan depolama alanındaki medya dosyalarına daha kolay erişebilirsiniz.

getMediaUri() yöntemi ExternalStorageProvider URI'yı destekler. Yöntem, Android 12 (API düzeyi 31) ve sonraki sürümlerde MediaDocumentsProvider URI'lerini de destekler.

Sanal dosya açma

Uygulamanız, Android 7.0 (API düzeyi 25) ve sonraki sürümlerde Depolama Erişim Çerçevesi'nin sunduğu sanal dosyalardan yararlanabilir. Sanal dosyaların ikili bir temsili olmasa da uygulamanız, sanal dosyaların içeriklerini farklı bir dosya türüne zorlayarak veya ACTION_VIEW amaç işlemini kullanarak bu dosyaları görüntüleyerek açabilir.

Sanal dosyaları açmak için istemci uygulamanızın bunları işleyecek özel bir mantık içermesi gerekir. Dosyanın bayt gösterimi almak isterseniz (örneğin, dosyayı önizlemek için) doküman sağlayıcıdan alternatif bir MIME türü istemeniz gerekir.

Kullanıcı bir seçim yaptıktan sonra, dosyanın sanal olup olmadığını belirlemek için sonuç verilerindeki URI'yı aşağıdaki kod snippet'inde gösterildiği gibi kullanın:

Kotlin

private fun isVirtualFile(uri: Uri): Boolean {
    if (!DocumentsContract.isDocumentUri(this, uri)) {
        return false
    }

    val cursor: Cursor? = contentResolver.query(
            uri,
            arrayOf(DocumentsContract.Document.COLUMN_FLAGS),
            null,
            null,
            null
    )

    val flags: Int = cursor?.use {
        if (cursor.moveToFirst()) {
            cursor.getInt(0)
        } else {
            0
        }
    } ?: 0

    return flags and DocumentsContract.Document.FLAG_VIRTUAL_DOCUMENT != 0
}

Java

private boolean isVirtualFile(Uri uri) {
    if (!DocumentsContract.isDocumentUri(this, uri)) {
        return false;
    }

    Cursor cursor = getContentResolver().query(
        uri,
        new String[] { DocumentsContract.Document.COLUMN_FLAGS },
        null, null, null);

    int flags = 0;
    if (cursor.moveToFirst()) {
        flags = cursor.getInt(0);
    }
    cursor.close();

    return (flags & DocumentsContract.Document.FLAG_VIRTUAL_DOCUMENT) != 0;
}

Dokümanın sanal bir dosya olduğunu doğruladıktan sonra, dosyayı "image/png" gibi alternatif bir MIME türüne dönüştürmeye zorlayabilirsiniz. Aşağıdaki kod snippet'i, sanal bir dosyanın resim olarak temsil edilip edilemeyeceğinin nasıl kontrol edileceğini ve gösteriliyorsa sanal dosyadan giriş akışı alır.

Kotlin

@Throws(IOException::class)
private fun getInputStreamForVirtualFile(
        uri: Uri, mimeTypeFilter: String): InputStream {

    val openableMimeTypes: Array<String>? =
            contentResolver.getStreamTypes(uri, mimeTypeFilter)

    return if (openableMimeTypes?.isNotEmpty() == true) {
        contentResolver
                .openTypedAssetFileDescriptor(uri, openableMimeTypes[0], null)
                .createInputStream()
    } else {
        throw FileNotFoundException()
    }
}

Java

private InputStream getInputStreamForVirtualFile(Uri uri, String mimeTypeFilter)
    throws IOException {

    ContentResolver resolver = getContentResolver();

    String[] openableMimeTypes = resolver.getStreamTypes(uri, mimeTypeFilter);

    if (openableMimeTypes == null ||
        openableMimeTypes.length < 1) {
        throw new FileNotFoundException();
    }

    return resolver
        .openTypedAssetFileDescriptor(uri, openableMimeTypes[0], null)
        .createInputStream();
}

Ek kaynaklar

Belgelerin ve diğer dosyaların nasıl saklanacağı ve bu dosyalara nasıl erişileceği hakkında daha fazla bilgi için aşağıdaki kaynaklara başvurun.

Numuneler

Videolar