Android 11 ActivityResult throwing only RESULT_CANCELED

Viewed 1116

We are selecting image/video files with this code

//Pick an image from storage
        Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
        intent.setType(type); //Can be image/* or video/*
        intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
        activityResultLauncher.launch(Intent.createChooser(intent, "Select Item"));

Then obtain the file Uri(s) on new ActivityResult API

protected final ActivityResultLauncher<Intent> activityResultLauncher = registerForActivityResult(
            new ActivityResultContracts.StartActivityForResult(),
            result -> {
                Log.wtf("WTF", result.toString());
                if (result.getResultCode() == Activity.RESULT_OK) {
                    // There are no request codes
                    Intent data = result.getData();

                    if (data == null) {
                        Toast.makeText(getContext(), R.string.unknown_error, Toast.LENGTH_SHORT).show();
                        return;
                    }

                    activityResult(true, data);
                }
                else
                    activityResult(false, null);
            });

Unfortunately after updating to Android 11 all we got is RESULT_CANCELED.

E/WTF: ActivityResult{resultCode=RESULT_CANCELED, data=null}
2 Answers

As what Ian said, you were not supposed to user Intent.createChooser with ACTION_OPEN_DOCUMENT though it works prior to Android 11.

Here is a helper class you use as a contract to select image/video/pdf using Activity Result API that will return you a list of Uri's. It can be configured for single or multiple selection of files.

Tested in android 10 and 11.

class SelectFileContract : ActivityResultContract<Pair<SELECTION_TYPE, Boolean>, List<Uri>?>() {
    var TAG = this.javaClass.simpleName
    override fun createIntent(context: Context, input: Pair<SELECTION_TYPE, Boolean>): Intent {

        var intent: Intent? = null
        intent = when (input.first) {
            SELECTION_TYPE.IMAGE -> Intent(
                Intent.ACTION_PICK,
                MediaStore.Images.Media.EXTERNAL_CONTENT_URI
            )
            SELECTION_TYPE.VIDEO -> Intent(
                Intent.ACTION_PICK,
                MediaStore.Video.Media.EXTERNAL_CONTENT_URI
            )
            SELECTION_TYPE.PDF -> {
                Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
                    type = "application/pdf"
                    addCategory(Intent.CATEGORY_OPENABLE)
                }
            }
        }
        if (input.second == true) {
            intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
        }

        return intent!!
    }

    override fun parseResult(resultCode: Int, intent: Intent?): List<Uri>? = when (resultCode) {

        Activity.RESULT_OK -> {
            val uriList = mutableListOf<Uri>()
            /**
             * in case of single selection the Uri will be inside [intent.data]
             */
            intent?.data?.let { uriList.add(it) }


            /**
             * in case of multiple selection the Uri will be inside [intent.clipData]
             */
            intent?.clipData?.let { clipData: ClipData ->
                for (i in 0 until clipData.itemCount) {
                    uriList.add(clipData.getItemAt(i).uri)
                }
            }

            Log.d(TAG, uriList.toString())
            uriList
        }
        else -> null


    }
}

enum class SELECTION_TYPE {
    IMAGE,
    VIDEO,
    PDF
}

Then in activity specific the contract:

 private val simpleContractRegistration =
        registerForActivityResult(SelectFileContract()) { result: List<Uri>? ->
            result?.let {
                Log.d(TAG, result.toString())
            }
        }

Then launch the registration:

binding.button.setOnClickListener {
            simpleContractRegistration.launch(
                Pair(
                    SELECTION_TYPE.IMAGE,
                    false
                )
            )
        }
Related