2

I can find many answers, such as this one, on how to create an intent that makes the user choose between camera and gallery with Java and the older API.

However, while it's relatively simple to do either of them via Kotlin with the new ActivityResultContracts API, I can't understand how to make the user choose between the two of them without making separate buttons for the two functions (which I would rather avoid).

How should I edit the latter linked answer's code to make the user choose from a system menu whether to use camera or gallery?

Jim
  • 63
  • 5

1 Answers1

6

You need to create your custom ActivityResultContract:

class TakePicturefromCameraOrGalley: ActivityResultContract<Unit, Uri?>() {

        private var photoUri: Uri? = null

        override fun createIntent(context: Context, input: Unit?): Intent {
            return openImageIntent(context)
        }

        override fun parseResult(resultCode: Int, intent: Intent?): Uri? {
            if (resultCode != Activity.RESULT_OK) return null
            return intent?.data ?: photoUri
        }

        private fun openImageIntent(context: Context): Intent {
            val camIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
            photoUri = createPhotoTakenUri(context)
            // write the captured image to a file
            camIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri)

            val gallIntent = Intent(Intent.ACTION_GET_CONTENT)
            gallIntent.type = "image/*"

            // look for available intents
            val info = ArrayList<ResolveInfo>()
            val yourIntentsList = ArrayList<Intent>()
            val packageManager = context.packageManager

            packageManager.queryIntentActivities(camIntent, 0).forEach{
                val finalIntent = Intent(camIntent)
                finalIntent.component = ComponentName(it.activityInfo.packageName, it.activityInfo.name)
                yourIntentsList.add(finalIntent)
                info.add(it)
            }

            packageManager.queryIntentActivities(gallIntent, 0).forEach {
                val finalIntent = Intent(gallIntent)
                finalIntent.component = ComponentName(it.activityInfo.packageName, it.activityInfo.name)
                yourIntentsList.add(finalIntent)
                info.add(it)
            }

            val chooser = Intent.createChooser(gallIntent, "Select source")
            chooser.putExtra(Intent.EXTRA_INITIAL_INTENTS, yourIntentsList.toTypedArray())

            return chooser

        }

        private fun createFile(context: Context): File {
            val timeStamp = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US).format(Date())
            val imageFileName = "IMG_" + timeStamp + "_"
            val storageDir = context.getExternalFilesDir(Environment.DIRECTORY_PICTURES) ?: throw IllegalStateException("Dir not found")
            return File.createTempFile(
                imageFileName,  /* prefix */
                ".jpg",  /* suffix */
                storageDir /* directory */
            )
        }

        private fun createPhotoTakenUri(context: Context): Uri {
            val file = createFile(context)
            return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID + ".provider", file)
            } else {
                Uri.fromFile(file)
            }
        }
    }

and you must have camera permission granted and File provider created in order to work correctly

AbrahamCuautle
  • 149
  • 1
  • 4
  • Just a couple questions about your code: 1. why do you just pass `gallIntent` to `Intent.createChooser()` if the goal is to make it choose between two? 2. Is the `return intent?.data ?: photoUri` line because one intent returns data as a parameter while the other returns an uri? – Jim Mar 03 '21 at 10:30
  • 1- I guess you want to implement `Intent.createChooser()` inside of `ActivityResultContract`. 2 - Because camIntent doesn't return an intent in `parseResult`. That`s why I passed photoUri to camIntent in order to get and uri in `registerForActivityResult()` last lambda parameter – AbrahamCuautle Mar 03 '21 at 15:37
  • Can you let me know how to use this class? – Varun Chandran Aug 01 '21 at 12:51
  • I've found a good way to pick from the gallery, using this guy code. So you need to override ActivityResultContract in order to specify intent type. https://matteinn.com/posts/activityresultcontracts/ – baderkhane Jan 02 '22 at 14:55