0

I am using Android 9 (Pie), API level 28. Here is my code for taking a picture (not sure if its a good design, I've never done mobile development before)

// this is a class that contains a part of the activity that can be either hidden or shown,
// kinda like a popup
    fun initCameraClick(activity: UserProfileActivity) {
        var uri:Uri? = null
        val takePicture = activity.registerForActivityResult(ActivityResultContracts.TakePicture()) { success: Boolean ->
            if (success) {
                Log.i("camerauri", uri.toString())
                this.profilepic.setImageURI(uri) // profilepic is an imagebutton
            }
        }
        this.cameraButton.setOnClickListener {
            takePicture.launch(uri)
        }
    }

While I can launch the camera, it keeps showing this after I take a picture: 1

What am I doing wrong? I can't find any stack trace in the run tab or the logcat tab

EDIT: here is the error from the logcat (com.android.camera2)

2021-11-07 14:32:44.036 6361-6361/com.android.camera2 E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.android.camera2, PID: 6361
    java.lang.NullPointerException
        at com.google.common.base.Preconditions.checkNotNull(Preconditions.java:210)
        at com.google.common.base.Optional.of(Optional.java:85)
        at com.android.camera.captureintent.state.StateSavingPicture.onEnter(StateSavingPicture.java:77)
        at com.android.camera.captureintent.stateful.StateMachineImpl.jumpToState(StateMachineImpl.java:62)
        at com.android.camera.captureintent.stateful.StateMachineImpl.processEvent(StateMachineImpl.java:110)
        at com.android.camera.captureintent.state.StateOpeningCamera$9.onClick(StateOpeningCamera.java:307)
        at android.view.View.performClick(View.java:6597)
        at android.view.View.performClickInternal(View.java:6574)
        at android.view.View.access$3100(View.java:778)
        at android.view.View$PerformClick.run(View.java:25885)
        at android.os.Handler.handleCallback(Handler.java:873)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at android.os.Looper.loop(Looper.java:193)
        at android.app.ActivityThread.main(ActivityThread.java:6669)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
qwerty_99
  • 640
  • 5
  • 20
  • You need to pass a valid `Uri` into `launch()`. Right now, you are passing in `null`. For example, you could use `FileProvider` and `getUriForFile()` to get a `Uri` to pass to the camera app, as seen in [this answer](https://stackoverflow.com/a/65526167/115145) and [this Medium post](https://medium.com/codex/how-to-use-the-android-activity-result-api-for-selecting-and-taking-images-5dbcc3e6324b). – CommonsWare Nov 07 '21 at 19:39
  • it worked, I had to add a provider_paths.xml file but it worked! Thanks a lot – qwerty_99 Nov 08 '21 at 03:44

1 Answers1

1

You need to pass sharable Uri value while launching takePicture.launch(uri).


Why sharable Uri?

we need sharable Uri because if we create a File in app-specific storage and pass Uri value of it, Android camera application cannot access it. This is because app-specific files are private and other applications cannot write or read data from it.

We need to setup FileProvider to create sharable Uri.


Configure FileProvider

[1] Create a xml file in res folder. (res/xml/file_paths.xml)

<?xml version="1.0" encoding="utf-8"?>
<paths>
    <external-cache-path
        name="images"
        path="/" />
</paths>

This file contains which app-specific directories should be used by FileProvider to create sharable Uri.

  • name : could be anything.
  • path : name of the directory which should be used by FileProvider. Here / means root of the directory.

[2] After creating xml file, define provider in AndroidManifest.xml

<provider
    android:name="androidx.core.content.FileProvider"
    android:authorities="com.learning.filestorage"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/file_paths"/>
</provider>

Here android:authorities is important property. We need to provide unique identifier here. Generally we will use applicationId here.


[3] Create a File in app-specific directory and fire intent

val cache = externalCacheDir ?: return

val file = File.createTempFile(System.currentTimeMillis().toString(), ".jpg", cache)
val uri = FileProvider.getUriForFile(this, "com.learning.filestorage", file)
if (uri != null) {
    takePictureFilePath = file.absolutePath
}

takePicture.launch(uri)
Dhaval
  • 2,724
  • 2
  • 24
  • 33