Search code examples
androidkotlinkotlin-android

How to load files on startup using a saved URI string in Android?


I tried to load images from a directory using Intent.ACTION_OPEN_DOCUMENT_TREE and it worked fine until I tried to save the URI I got to preferences. Then I could read it but there were no files in the directory (tree.listFiles() from the chosen folder was of size 0). I thought that the issue was that there was no access to that directory from the start, because when I chose a folder using startActivityForResult() it asked if I wanted to give the app permission to read files from that directory. But that wasn't the case, as I understood from asking for READ_EXTERNAL_STORAGE permission as it didn't solve the problem.

I worked out a minimal example that demonstrates what isn't working:

package com.example.loadingtest

import android.Manifest
import android.content.Intent
import android.content.SharedPreferences
import android.content.pm.PackageManager
import android.net.Uri
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.provider.DocumentsContract
import android.view.View
import android.widget.Button
import android.widget.TextView
import androidx.core.net.toUri
import androidx.documentfile.provider.DocumentFile

class FullscreenActivity : AppCompatActivity(), View.OnClickListener {
    lateinit var preferences: SharedPreferences
    var text = ""
    val pickCode = 1
    lateinit var textField:TextView
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_fullscreen)
        val permissionStorage = arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE)
        val requestExternalStorage = 1
        val permission = checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE)
        if(permission != PackageManager.PERMISSION_GRANTED){
            requestPermissions(permissionStorage, requestExternalStorage)
        }
        textField = findViewById(R.id.texttest)
        text = getString(R.string.txt_amount)
        val btn = findViewById<Button>(R.id.btntest)
        btn.setOnClickListener(this)
        preferences = getPreferences(MODE_PRIVATE)
        val path = preferences.getString("path","")
        if(path != null && path != ""){
            loadImages(path.toUri())
        }
    }
    fun loadImages(uri: Uri){
        val childrenUri = DocumentsContract.buildChildDocumentsUriUsingTree(uri, DocumentsContract.getTreeDocumentId(uri))
        val tree = DocumentFile.fromTreeUri(this, childrenUri) ?: return
        val files = tree.listFiles()
        textField.text = String.format(text, files.size)
    }
    override fun onClick(v: View?) {
        if(v == null) return
        val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
        startActivityForResult(intent, pickCode)
    }
    override fun onActivityResult(requestCode:Int, resultCode:Int, data: Intent?){
        super.onActivityResult(requestCode, resultCode, data)
        if(requestCode == pickCode){
            if(resultCode == RESULT_OK){
                data?.data.also{
                        uri ->
                    if(uri!=null){
                        loadImages(uri)
                        val editor = preferences.edit()
                        val str = uri.toString()
                        editor.putString("path", str)
                        editor.apply()
                    }
                }
            }
        }
    }
}

The layout is as simple as a button to pick the folder and a TextView to show the amount of files in that chosen directory. The TextView should also show that there are files in the directory that I loaded from preferences, but it does not. Layout file (not necessary to the problem, I suppose):

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="?attr/fullscreenBackgroundColor"
    android:theme="@style/ThemeOverlay.GuidedAttempt.FullscreenContainer"
    tools:context=".FullscreenActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="horizontal">

        <Button
            android:id="@+id/btntest"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Button" />

        <TextView
            android:id="@+id/texttest"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="TextView" />
    </LinearLayout>

</FrameLayout>

The android manifest file also includes <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>, so the app should(?) have access to external storage when the permission is granted.


Solution

  • You do not need READ_EXTERNAL_STORAGE.

    Just take persistable uri permission in onActivityResult in order to use the obtained uri later.