How to get requestCode from Activity Result API
Asked Answered
B

1

16

I am very new to Android Kotlin programming. I am making an app to create, read and delete file using Storage Access Framework. I did it by implementing deprecated startActivityForResult intent to perform file read, create, delete tasks. I tried to implement it using Activity Result API but I got stuck in request code which I could not find it. In new implementation, there is resultCode but no requestCode.

My implemetation is below. How I can I implement the following using Activity Result API ?

How can I get request code from Activity Result API ?

fun createFile()
{
    val filename: String = etFileName.text.toString()
    val intent = Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
        type = "text/*"
        addCategory(Intent.CATEGORY_OPENABLE)
        putExtra(Intent.EXTRA_TITLE, filename)
    }
    startActivityForResult(intent, WRITE_REQUEST_CODE)
}


fun readFile(){
    etFileName.setText("")
    val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
        addCategory(Intent.CATEGORY_OPENABLE)
        type = "text/*"
    }
    startActivityForResult(intent, READ_REQUEST_CODE)
}

fun deleteFile(uri: Uri){
    val filename = queryName(contentResolver, uri)
    DocumentsContract.deleteDocument(contentResolver, uri)
    Toast.makeText(applicationContext, "Done deleting $filename", Toast.LENGTH_SHORT).show()
}

fun writeText(uri: Uri) : String{
    //var writetext: String = "typing high speed"
    var writeStream = contentResolver.openFileDescriptor(uri, "w")
    val filename: String = queryName(contentResolver, uri)
    etFileName.setText(filename)
    var fileOutputStream = FileOutputStream(writeStream?.fileDescriptor)
    fileOutputStream.write(filename.toByteArray())
    fileOutputStream.close()
    return filename.toString()
}

fun openFileContent(uri: Uri): String{
    val inputStream = contentResolver.openInputStream(uri)
    val reader = BufferedReader(InputStreamReader(inputStream))
    val stringBuilder = StringBuilder()

    val filename = queryName(contentResolver, uri)
    etFileName.setText(filename)
    var currentline = reader.readLine()

    while (currentline != null) {
        stringBuilder.append(currentline + "\n")
        currentline = reader.readLine()
    }
    inputStream?.close()
    val str =  stringBuilder.toString()
    Log.d("Uri Read", ": $str")
    return str
}

private fun queryName(resolver: ContentResolver, uri: Uri): String {
    val returnCursor: Cursor = resolver.query(uri, null, null, null, null)!!
    val nameIndex: Int = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)
    returnCursor.moveToFirst()
    val name: String = returnCursor.getString(nameIndex)
    returnCursor.close()
    return name
}

override fun onActivityResult(requestCode: Int, resultCode: Int, resultData: Intent?) {
    super.onActivityResult(requestCode, resultCode, resultData)

    if (requestCode == READ_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
        val uri =resultData?.data
        Log.d("Uri Read", "$uri")
        if (uri != null) {
            val readfile: String = openFileContent(uri)
            etContent.setText(readfile)
        }
    } else if (requestCode == WRITE_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
        resultData?.data?.also { uri ->
            Log.i("Uri Write", "Uri: $uri")   // 1
            val write = writeText(uri)   // 2
            etContent.setText(write)
        }
    } else if (requestCode == DELETE_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
        val uri =resultData?.data
        Log.d("Uri Delete", "$uri")
        if (uri != null) {
            deleteFile(uri)
        }
    }
}

Since the above method is deprecated, I tried to implement it using Intent launcher, but there is no reference to requestCode anywhere so I am stuck here. It is as shown below :

val startforFile: ActivityResultLauncher<Intent> = registerForActivityResult(ActivityResultContracts.StartActivityForResult()){
    result: ActivityResult ->

    // could not find reference to requestCode here

    if (requestCode == READ_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
        val uri =resultData?.data
        Log.d("Uri Read", "$uri")
        if (uri != null) {
            val readfile: String = openFileContent(uri)
            etContent.setText(readfile)
        }
    } else if (requestCode == WRITE_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
        resultData?.data?.also { uri ->
            Log.i("Uri Write", "Uri: $uri")   // 1
            val write = writeText(uri)   // 2
            etContent.setText(write)
        }
    } else if (requestCode == DELETE_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
        val uri =resultData?.data
        Log.d("Uri Delete", "$uri")
        if (uri != null) {
            deleteFile(uri)
        }
    }
}

Can anyone show me how it is done correctly ?

Bisque answered 8/6, 2021 at 12:22 Comment(0)
C
30

There are two ways with ActivityResultContracts.StartActivityForResult.

  1. Make multiple requests by registering multiple listeners:
val startForFileRead: ActivityResultLauncher<Intent> = registerForActivityResult(ActivityResultContracts.StartActivityForResult()){
    result: ActivityResult ->
    if (resultCode == Activity.RESULT_OK) {
        val uri =resultData?.data
        Log.d("Uri Read", "$uri")
        if (uri != null) {
            val readfile: String = openFileContent(uri)
            etContent.setText(readfile)
        }
    } 
}

val startForFileWrite: ActivityResultLauncher<Intent> = registerForActivityResult(ActivityResultContracts.StartActivityForResult()){
    result: ActivityResult ->
    if (resultCode == Activity.RESULT_OK) {
        resultData?.data?.also { uri ->
            Log.i("Uri Write", "Uri: $uri")   // 1
            val write = writeText(uri)   // 2
            etContent.setText(write)
        }
    } 
}

val startForFileDelete: ActivityResultLauncher<Intent> = registerForActivityResult(ActivityResultContracts.StartActivityForResult()){
    result: ActivityResult ->
    if (resultCode == Activity.RESULT_OK) {
        val uri =resultData?.data
        Log.d("Uri Delete", "$uri")
        if (uri != null) {
            deleteFile(uri)
        }
    } 
}
  1. Put an extra in your Intent returned from the other activity. Unpack it as your request code.
const val REQUEST_CODE = "REQUEST_CODE"
//...

val startForFile: ActivityResultLauncher<Intent> =
    registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
    val requestCode = result.data?.extras?.getInt(REQUEST_CODE)
    if (requestCode == null) {
        Log.e("FileResultLauncher", "No REQUEST_CODE was returned in data intent.")
        return@registerForActivityResult
    }
    val uri = result.data?.data
    if (uri == null || result.resultCode != RESULT_OK) {
        Log.i("FileResultLauncher", "No Uri returned or result wasn't OK.")
        return@registerForActivityResult
    }
    when (requestCode) {
        READ_REQUEST_CODE -> {
            Log.d("Uri Read", "$uri")
            val readfile: String = openFileContent(uri)
            etContent.setText(readfile)
        }
        WRITE_REQUEST_CODE -> {
            Log.i("Uri Write", "Uri: $uri")   // 1
            val write = writeText(uri)   // 2
            etContent.setText(write)
        }
        DELETE_REQUEST_CODE -> {
            Log.d("Uri Delete", "$uri")
            deleteFile(uri)
        }
    }
}
Cung answered 8/6, 2021 at 13:28 Comment(6)
Thanks for your help. I followed method (1) [registering multiple listeners] and it worked. Slight modification that I made was to get uri by adding extra .data to the end of result?.data? . I think that result?.data returned only intent. So I made it --> val uri = result?.data?.data.Bisque
The second way doesn't work!Colored
@Colored I tested it and it worked fine. If you're using the request code in the second activity, make sure you add it as an extra in the Intent you pass to launch(), so you can unpack it from the received intent in the second activity. And to set it in the result, add an Intent with the request code extra in your call to setResult().Cung
@Cung and what about system settings activities? In addition, the single-activity approach is popular these days.Colored
You don't usually open system settings activities for a response, do you? If there are any that do give a response, I suppose you have to stick with the old activity for result methods despite deprecation for now, until Jetpack provides contracts for them. The single-activity approach is indeed a good idea when you can use it. Occasionally it's not great for modularity, like if you want to open your own barcode scanner that has a completely different UI with no action bar, etc. Makes more sense to have that in a separate activity.Cung
There is an ActivityResultContract class you can implement if there's a system settings (or another app) activity you need a response from.Bathyscaphe

© 2022 - 2024 — McMap. All rights reserved.