I'm attempting to read a plain text file in external storage, but I'm getting an exception related to permissions. The goal is to read a plain text file from the Download folder in the sdcard and show the contents in a TextView. I'm getting a permissions related error. This is what I've got so far:
AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.android.externalstoragesample">
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.ExternalStorageSample">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
MainActivity.kt:
package com.example.android.externalstoragesample
import android.Manifest
import android.app.Activity
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.widget.Button
import android.widget.TextView
import java.io.File
import androidx.core.app.ActivityCompat
import android.content.pm.PackageManager
private const val TAG = "LogTag"
private const val OPEN_DOCUMENT_REQUEST_CODE = 3
// Storage Permissions
private const val REQUEST_EXTERNAL_STORAGE = 1
private val PERMISSIONS_STORAGE = arrayOf(
Manifest.permission.READ_EXTERNAL_STORAGE
)
class MainActivity : AppCompatActivity() {
private lateinit var importButton : Button
private lateinit var outputText : TextView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
importButton = findViewById(R.id.importButton)
outputText = findViewById(R.id.outputText)
importButton.setOnClickListener {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
type = "text/plain"
addCategory(Intent.CATEGORY_OPENABLE)
}
Log.v(TAG, "Calling startActivityForResult...")
startActivityForResult(intent, OPEN_DOCUMENT_REQUEST_CODE)
Log.v(TAG, "startActivityForResult has been called")
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == OPEN_DOCUMENT_REQUEST_CODE) {
if (resultCode == Activity.RESULT_OK) {
Log.v(TAG, "Let's check what we have")
data?.data?.also { documentUri ->
val path = documentUri.path
Log.v(TAG, "Document URI is $documentUri with path $path")
Log.v(TAG, "Verifying storage permissions...")
verifyStoragePermissions()
Log.v(TAG, "Creating File object...")
val file = File(path!!.substring(documentUri.path!!.indexOf(':') + 1))
Log.v(TAG, "Reading file...")
val fileContent = file.readLines()
var readContent = ""
for (line in fileContent) {
readContent += line + "\n"
}
Log.v(TAG, "Read content: $readContent")
outputText.text = readContent
}
} else {
Log.v(TAG, "onActivityResult resultCode is not OK=$resultCode")
}
} else {
Log.v(TAG, "onActivityResult unknown requestCode=$requestCode")
}
}
private fun verifyStoragePermissions() {
// Check if we have read permission
val permission = ActivityCompat.checkSelfPermission(
this,
Manifest.permission.READ_EXTERNAL_STORAGE
)
if (permission != PackageManager.PERMISSION_GRANTED) {
Log.v(TAG, "We don't have permission so prompt the user")
ActivityCompat.requestPermissions(
this,
PERMISSIONS_STORAGE,
REQUEST_EXTERNAL_STORAGE
)
} else {
Log.v(TAG, "We have permission")
}
}
}
When I open the app, I press the button and the file picker pops up. As soon as I choose the file hello.txt the app crashes. This is what I see as the output in Android Studio:
V/LogTag: Calling startActivityForResult...
V/LogTag: startActivityForResult has been called
D/EGL_emulation: eglMakeCurrent: 0xdf6be6c0: ver 3 0 (tinfo 0xd40ae700)
V/LogTag: Let's check what we have
V/LogTag: Document URI is content://com.android.providers.downloads.documents/document/raw%3A%2Fstorage%2Femulated%2F0%2FDownload%2Fhello.txt with path /document/raw:/storage/emulated/0/Download/hello.txt
Verifying storage permissions...
V/LogTag: We have permission
Creating File object...
V/LogTag: Reading file...
D/AndroidRuntime: Shutting down VM
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.android.externalstoragesample, PID: 9541
java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=3, result=-1, data=Intent { dat=content://com.android.providers.downloads.documents/document/raw:/storage/emulated/0/Download/hello.txt flg=0x43 }} to activity {com.example.android.externalstoragesample/com.example.android.externalstoragesample.MainActivity}: java.io.FileNotFoundException: /storage/emulated/0/Download/hello.txt: open failed: EACCES (Permission denied)
at android.app.ActivityThread.deliverResults(ActivityThread.java:4845)
at android.app.ActivityThread.handleSendResult(ActivityThread.java:4886)
at android.app.servertransaction.ActivityResultItem.execute(ActivityResultItem.java:51)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2016)
at android.os.Handler.dispatchMessage(Handler.java:107)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7356)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)
Caused by: java.io.FileNotFoundException: /storage/emulated/0/Download/hello.txt: open failed: EACCES (Permission denied)
at libcore.io.IoBridge.open(IoBridge.java:496)
at java.io.FileInputStream.<init>(FileInputStream.java:159)
at kotlin.io.FilesKt__FileReadWriteKt.forEachLine(FileReadWrite.kt:190)
at kotlin.io.FilesKt__FileReadWriteKt.readLines(FileReadWrite.kt:219)
at kotlin.io.FilesKt__FileReadWriteKt.readLines$default(FileReadWrite.kt:217)
at com.example.android.externalstoragesample.MainActivity.onActivityResult(MainActivity.kt:65)
at android.app.Activity.dispatchActivityResult(Activity.java:8110)
at android.app.ActivityThread.deliverResults(ActivityThread.java:4838)
at android.app.ActivityThread.handleSendResult(ActivityThread.java:4886)
at android.app.servertransaction.ActivityResultItem.execute(ActivityResultItem.java:51)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2016)
at android.os.Handler.dispatchMessage(Handler.java:107)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7356)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)
Caused by: android.system.ErrnoException: open failed: EACCES (Permission denied)
at libcore.io.Linux.open(Native Method)
at libcore.io.ForwardingOs.open(ForwardingOs.java:167)
at libcore.io.BlockGuardOs.open(BlockGuardOs.java:252)
at libcore.io.ForwardingOs.open(ForwardingOs.java:167)
at android.app.ActivityThread$AndroidOs.open(ActivityThread.java:7255)
at libcore.io.IoBridge.open(IoBridge.java:482)
at java.io.FileInputStream.<init>(FileInputStream.java:159)
at kotlin.io.FilesKt__FileReadWriteKt.forEachLine(FileReadWrite.kt:190)
at kotlin.io.FilesKt__FileReadWriteKt.readLines(FileReadWrite.kt:219)
at kotlin.io.FilesKt__FileReadWriteKt.readLines$default(FileReadWrite.kt:217)
at com.example.android.externalstoragesample.MainActivity.onActivityResult(MainActivity.kt:65)
at android.app.Activity.dispatchActivityResult(Activity.java:8110)
at android.app.ActivityThread.deliverResults(ActivityThread.java:4838)
at android.app.ActivityThread.handleSendResult(ActivityThread.java:4886)
at android.app.servertransaction.ActivityResultItem.execute(ActivityResultItem.java:51)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2016)
at android.os.Handler.dispatchMessage(Handler.java:107)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7356)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)
I/Process: Sending signal. PID: 9541 SIG: 9