I am trying to take a picture from camera in Android and I want to save it with a custom file name using UUID (eg: f12b5700-1d92-11e9-ab14-d663bd873d93.jpg).
In the following code, in onActivityResult, I do get the f12b5700-1d92-11e9-ab14-d663bd873d93.jpg in photoPath but when I check the actual image on device, it is saved as timestamp.jpg (eg: 1548082653944.jpg). My question is how can I make the image to be saved with custom name?
private var photoURI: Uri? = null
private fun takePhoto()
{
val values = ContentValues()
values.put(MediaStore.Images.Media.TITLE, UUID.randomUUID().toString() + ".jpg")
photoURI = contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)
val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (intent.resolveActivity(packageManager) != null)
{
intent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI)
startActivityForResult(intent, PHOTO_REQUEST_CODE);
}
}
in onActivityResult handler, the code looks like this:
if (requestCode == PHOTO_REQUEST_CODE)
{
if (resultCode == Activity.RESULT_OK)
{
val proj = arrayOf(MediaStore.Images.Media.TITLE)
val cursor = contentResolver.query(photoURI!!, proj, null, null, null)
val index = cursor!!.getColumnIndex(MediaStore.Images.Media.TITLE)
cursor.moveToFirst()
photoPath = cursor.getString(index)
cursor.close()
Toast.makeText(this, photoPath, Toast.LENGTH_LONG).show()
}
else
{
capturedImage.setImageURI(null)
Toast.makeText(this, "Photo was not taken", Toast.LENGTH_SHORT).show()
}
}
With some struggle, I was able to solve the problem with the help of CommonsWare link:
https://github.com/commonsguy/cw-omnibus/tree/v8.13/Camera/FileProvider
Additionally I took help from these links:
https://guides.codepath.com/android/Accessing-the-Camera-and-Stored-Media
https://guides.codepath.com/android/Sharing-Content-with-Intents#sharing-files-with-api-24-or-higher
https://developer.android.com/training/camera/photobasics
https://developer.android.com/reference/android/os/Environment.html#getExternalStorageState(java.io.File)
The trick is to use FileProvider.
I had to add <provider> in AndroidManifest.xml
Working code looks like this:
if (Environment.getExternalStorageState() != Environment.MEDIA_MOUNTED)
{
Toast.makeText(this, "Storage is not available", Toast.LENGTH_LONG).show()
return
}
var imageName = UUID.randomUUID().toString() + ".jpg"
var output = File(Environment.getExternalStoragePublicDirectory(appName), imageName)
if (!output.parentFile.exists() && !output.parentFile.mkdir())
{
Toast.makeText(this, "Unable to create storage folder", Toast.LENGTH_LONG).show()
return
}
photoURI = FileProvider.getUriForFile(this, authority, output)
val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (intent.resolveActivity(packageManager) != null)
{
intent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI)
startActivityForResult(intent, PHOTO_REQUEST_CODE);
}
Provider:
<provider
android:authorities="com.domain.appname.fileprovider"
android:name="android.support.v4.content.FileProvider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/file_paths"/>
</provider>
file_paths.xml:
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="appname" path="appname" />
</paths>
Change com.domain.appname and appname accordingly.
Related
I have the following code below for exporting a room database and then attaching it to an email. Currently the user first has to choose where they want the data saved before it can be attached.
Is there a way that I can do this without first having to ask the user where to save the database?
Here is my code:
fun exportDatabase() {
val intent = Intent(Intent.ACTION_CREATE_DOCUMENT)
intent.type = "*/*" // this line is a must when using ACTION_CREATE_DOCUMENT
startActivityForResult(
intent,
DATABASE_EXPORT_CODE
)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
when (requestCode) {
DATABASE_EXPORT_CODE -> {
val userChosenUri = data?.data
val inStream = getDatabasePath("app_database").inputStream()
val outStream = userChosenUri?.let { contentResolver.openOutputStream(it) }
inStream.use { input ->
outStream.use { output ->
output?.let { input.copyTo(it) }
Toast.makeText(this, "Data exported successfully", Toast.LENGTH_LONG).show()
val emailIntent = Intent(Intent.ACTION_SEND)
//Set type to email
emailIntent.type = "vnd.android.cursor.dir/email"
var toEmail: String = "whatever#gmail.com"
emailIntent.putExtra(Intent.EXTRA_EMAIL, toEmail)
emailIntent.putExtra(Intent.EXTRA_STREAM, userChosenUri)
emailIntent.putExtra(Intent.EXTRA_SUBJECT, "Data for Training Log")
startActivity(Intent.createChooser(emailIntent, "Send Email"))
}
}
}
else ->
Log.d("D001", "onActivityResult: unknown request code")
}
}
You need to use FileProvider. But FileProvider doesn't support transferring database files directly (Check here).
This can handled with:
Solution 1:
Create a custom FileProvider class that supports copying database files:
class DBFileProvider : FileProvider {
fun getDatabaseURI(c: Context, dbName: String?): Uri? {
val file: File = c.getDatabasePath(dbName)
return getFileUri(c, file)
}
private fun getFileUri(context: Context, file: File): Uri? {
return getUriForFile(context, "com.android.example.provider", file)
}
}
And request the FileProvider in manifest:
<application>
....
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.android.example.provider"
android:exported="false"
android:enabled="true"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/provider_paths" />
</provider>
</application>
And create provider_paths under res\xml
<?xml version="1.0" encoding="utf-8"?>
<paths>
<files-path
name="databases"
path="../" />
</paths>
Then to send this database file through the email:
public static void backupDatabase(AppCompatActivity activity) {
Uri uri = new DBFileProvider().getDatabaseURI(activity, "app_database.db");
sendEmail(activity, uri);
}
private fun sendEmail(activity: AppCompatActivity, attachment: Uri) {
val emailIntent = Intent(Intent.ACTION_SEND)
//Set type to email
emailIntent.type = "vnd.android.cursor.dir/email"
val toEmail = "whatever#gmail.com"
emailIntent.putExtra(Intent.EXTRA_EMAIL, toEmail)
emailIntent.putExtra(Intent.EXTRA_STREAM, attachment)
emailIntent.putExtra(Intent.EXTRA_SUBJECT, "Data for Training Log")
activity.startActivity(Intent.createChooser(emailIntent, "Send Email"))
}
Solution 2:
Copy the database file to a temp file to a directory supported by FileProvider like filesDir:
Get the database file using getDatabasePath
Copy the database file to a storage directory that is supported by FileProvider
Create the Uri of the new copied file using the FileProvider
fun backupDatabase(activity: AppCompatActivity) {
// Get the database file
val dbFile = activity.getDatabasePath("app_database.db")
try {
// Copy database file to a temp file in (filesDir)
val parent = File(activity.filesDir, "databases_temp")
val file = File(parent, "myDatabase")
dbFile.copyTo(file)
// Get Uri of the copied database file from filesDir to be used in email intent
val uri = getUri(activity.applicationContext, file)
// Send an email
sendEmail(activity, uri)
} catch (e: IOException) {
e.printStackTrace()
}
}
private fun getUri(context: Context, file: File): Uri {
var uri = Uri.fromFile(file)
// Using FileProvider for API >= 24
if (Build.VERSION.SDK_INT >= 24) {
uri = FileProvider.getUriForFile(
context,
"com.android.example.provider", file
)
}
return uri
}
Use the same manifest of solution 1. And adjust provider_paths under res\xml with the created temp dir:
<?xml version="1.0" encoding="utf-8"?>
<paths>
<files-path
name="databases_temp"
path="/" />
</paths>
N.B: In both solutions, adjust the package name to yours.
FOR MODERATORS: I know there are already questions like this but all of those approaches endup giving bitmap through data.getExtra("data") which is actually just thumbnail. I want to get URI not bitmap and I need to get URI of ACTUAL IMAGE not its thumbnail with approach available in 2021. PLEASE CONSIDER THIS BEFORE TAGGING QUESTION AS DUPLICATE!
I am getting image from camera and its working fine on lower devices but its giving null in data when onActivityResult is called only in Android 10 and 11.
That's what I am doing
val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
startActivityForResult(intent, ACTION_REQUEST_CAMERA)
Here is my onActivityResult
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode == RESULT_OK && requestCode == ACTION_REQUEST_CAMERA) {
data?.data?.let { uri ->
Toast.makeText(context, "Got URI", Toast.LENGTH_LONG).show()
}
}
}
This approach worked for me
In Manifest file
<application>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.example.android.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/file_paths">
</meta-data>
</provider>
...
</application
created a file /res/xml/file_paths.xml and specified path in that
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-files-path name="my_images" path="Pictures" />
</paths>
In my activity
created a global variable var cameraPhotoFilePath: Uri? = null
this is how I started Camera acitivity for results
val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
val photoFile: File? = try {
createImageFileInAppDir()
} catch (ex: IOException) {
// Error occurred while creating the File
null
}
photoFile?.also { file ->
val photoURI: Uri = FileProvider.getUriForFile(
this,
"com.example.android.provider",
file
)
cameraPhotoFilePath = photoURI
intent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI)
}
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
startActivityForResult(intent, ACTION_REQUEST_CAMERA)
here is a helper function that i used in above code
#Throws(IOException::class)
private fun createImageFileInAppDir(): File {
val timeStamp: String = SimpleDateFormat("yyyyMMdd_HHmmss").format(Date())
val imagePath = getExternalFilesDir(Environment.DIRECTORY_PICTURES)
return File(imagePath, "JPEG_${timeStamp}_" + ".jpg")
}
At the end in onActivityResult thats how I got image Uri
if (resultCode == RESULT_OK && requestCode == ACTION_REQUEST_CAMERA) {
cameraPhotoFilePath?.let { uri ->
// here uri is image Uri that was captured by camera
}
}
You should pass your own uri (path) to intent with action MediaStore.ACTION_IMAGE_CAPTURE
putExtra(MediaStore.EXTRA_OUTPUT, FileProvider.getUriForFile(
applicationContext,
"$packageName.your_file_provider",
File("path/to")
))
addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
More info about FileProvider
Android developer have good documentation about how to get full sized photos . You can also get the uri of full size image .Please visit this link Get full size image
I am trying to take a photo using camera and then trying to save to gallery and decode the stream using BitmapFactory but it returns null and also photo taken is not saved to gallery.
So two things here i noticed is photo taken is not getting saved to GALLERY and decodeFile retuning false.
I have tried using BitmapFactory.decodeStream and also tried getting from Uri but nothing worked.
Here is my code:
Take Photo:
if (ActivityCompat.checkSelfPermission(context!!, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(context!!, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED)
ActivityCompat.requestPermissions(activity!!, arrayOf(Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE), photoWithCamera)
else
displayCamera()
Camera Display:
private fun displayCamera() {
Intent(MediaStore.ACTION_IMAGE_CAPTURE).also { takePictureIntent ->
// Ensure that there's a camera activity to handle the intent
takePictureIntent.resolveActivity(context!!.packageManager)?.also {
// Create the File where the photo should go
createImageFile()?.also {
uri = FileProvider.getUriForFile(context!!,"com.app.android.sample.fileprovider",it)
startActivityForResult(takePictureIntent, photoWithCamera)
}
}
}
}
private fun createImageFile(): File? = try {
File.createTempFile("test", ".png",context?.getExternalFilesDir(DIRECTORY_PICTURES)).apply {
newPicturePath = absolutePath
}
} catch (e: Exception) {
e.printStackTrace()
null
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode == Activity.RESULT_OK && data != null && (requestCode == photoWithCamera){
when (requestCode) {
photoWithCamera -> {
galleryAddPic()
setPic()
}
}
}
}
Decode Stream and Set pic (Not Working):
private fun setPic() {
// Get the dimensions of the View
val targetW: Int = headingImg.width
val targetH: Int = headingImg.height
val bmOptions = BitmapFactory.Options().apply {
// Get the dimensions of the bitmap
inJustDecodeBounds = true
val photoW: Int = outWidth
val photoH: Int = outHeight
// Determine how much to scale down the image
val scaleFactor: Int = min(photoW / targetW, photoH / targetH)
// Decode the image file into a Bitmap sized to fill the View
inJustDecodeBounds = false
inSampleSize = scaleFactor
inPurgeable = true
}
//decodeFile returns null here
BitmapFactory.decodeFile(newPicturePath, bmOptions)?.also { bitmap ->
headingImg.setImageBitmap(bitmap)
}
}
Adding photo taken to gallery(Not working):
private fun galleryAddPic() {
Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE).also { mediaScanIntent ->
val f = File(newPicturePath)
mediaScanIntent.data = Uri.fromFile(f)
context!!.sendBroadcast(mediaScanIntent)
}
}
Android Manifest:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
android:permissionGroup="android.permission-group.STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CAMERA" />
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/provider_paths"/>
</provider>
Provider Paths:
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path name="external_files" path="."/>
</paths>
The file created using createImageFile() is returning file as below:
/storage/emulated/0/Android/data/com.app.android.sample/files/Pictures/test4153865961621152704.png and I could see the file in the same location
Thanks,
Sindhu
You should use same package name
'com.app.android.sample.fileprovider'
which you are using while opening camera in manifest file - 'com.app.android.sample.fileprovider' instead
android:authorities="${applicationId}.fileprovider".
I forgot to add the file uri before starting the Camera intent. So i changed my code from
createImageFile()?.also {
uri = FileProvider.getUriForFile(
context!!,
"com.app.android.traderpro.etx.fileprovider",
it
)
startActivityForResult(takePictureIntent, photoWithCamera)
}
to this:
createImageFile()?.also {
uri = FileProvider.getUriForFile(
context!!,
"com.app.android.traderpro.etx.fileprovider",
it
)
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, uri)
startActivityForResult(takePictureIntent, photoWithCamera)
}
I faced the same issue and I fixed it by adding android:requestLegacyExternalStorage="true" under the application tag in the AndroidMaifest.xml file.
I am getting this msg speech that did not find (No such file or directory) the path is correct what could be wrong?
W/System.err: java.io.FileNotFoundException: /map.app.fileprovider/files/Pictures/JPEG_20190507_180502_4494360846236185755.jpg (No such file or directory)
LOG
W/System.err: java.io.FileNotFoundException: /map.app.fileprovider/files/Pictures/JPEG_20190507_180502_4494360846236185755.jpg (No such file or directory)
W/System.err: at java.io.FileInputStream.open0(Native Method)
at java.io.FileInputStream.open(FileInputStream.java:200)
W/System.err: at java.io.FileInputStream.<init>(FileInputStream.java:150)
at java.io.FileInputStream.<init>(FileInputStream.java:103)
W/System.err: at android.content.ContentResolver.openInputStream(ContentResolver.java:965)
at android.provider.MediaStore$Images$Media.getBitmap(MediaStore.java:888)
W/System.err: at map.app.fragments.ReportFragment.onActivityResult(ReportFragment.kt:274)
ReportFragmnet line error 274
val bitmap = MediaStore.Images.Media.getBitmap(context.contentResolver, Uri.parse("file://"+this.imageUri!!)) as Bitmap
path
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-files-path
name="files"
path="."/>
</paths>
manifest
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/paths" />
</provider>
method to capture the gallery image
fun openCamera() {
try {
val imageFile = createImageFile()
val callCameraIntent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
if(callCameraIntent.resolveActivity(activity.packageManager) != null) {
val authorities = activity.packageName + ".fileprovider"
this.imageUri = FileProvider.getUriForFile(context, authorities, imageFile)
callCameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri)
startActivityForResult(callCameraIntent, IMAGE_PICK_CODE)
}
} catch (e: IOException) {
Toast.makeText(context, "Could not create file!", Toast.LENGTH_SHORT).show()
}
}
onActivityResult
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == IMAGE_PICK_CODE && resultCode == RESULT_OK) {
try {
//Getting the Bitmap from Gallery
val bitmap = MediaStore.Images.Media.getBitmap(context.contentResolver, Uri.parse("file://"+this.imageUri!!)) as Bitmap
this.imgThumb!!.setImageBitmap(bitmap)
this.pictureTaken = true
} catch (e:IOException) {
e.printStackTrace()
}
} else {
Toast.makeText(context, "Error loading image", Toast.LENGTH_LONG)
}
}
#Throws(IOException::class)
fun createImageFile(): File {
val timeStamp: String = SimpleDateFormat("yyyyMMdd_HHmmss").format(Date())
val imageFileName: String = "JPEG_" + timeStamp + "_"
val storageDir: File = activity.getExternalFilesDir(Environment.DIRECTORY_PICTURES)
if(!storageDir.exists()) storageDir.mkdirs()
val imageFile = File.createTempFile(imageFileName, ".jpg", storageDir)
imageFilePath = imageFile.absolutePath
return imageFile
}
As the exception indicates, The path does not exist!!
As a bit of advice, try not to fight with exceptions, Just follow them, even if you're pretty sure!
Anyway this time its the
val authorities = activity.packageName + ".fileprovider"
part to blame because you have defined the provider in manifest with .provider suffix.
So change to:
val authorities = activity.packageName + ".provider"
It should be working by that change.
Cheers!
I've been reading through every post on here relating (as well as the documentation) to this topic and for some reason I just can't get it working. I get to the point where the user takes a photo, hits the checkmark to continue, and then the app crashes.
Specifically at this line:
val filepath = mFirebaseStorage.child("Users").child(prefs.UID).child(uri.lastPathSegment)
My code looks something like this:
onLaunchCamera - called when user selects "Camera" from alert box
private fun onLaunchCamera() {
val takePictureIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
//Ensure there's a camera activity to handle the intent
if (takePictureIntent.resolveActivity(packageManager) != null) {
var photoFile: File? = null
try {
photoFile = createImageFile()
} catch (e: IOException) {
//log error
Log.e(TAG, e.toString())
}
//continue only if file was successfully created!
if (photoFile != null) {
val photoURI = FileProvider.getUriForFile(this,
"com.android.projectrc.fileprovider",
photoFile)
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI)
startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE)
}
}
}
onActivityResult
override protected fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK) {
val progressDialog = indeterminateProgressDialog("Uploading...")
progressDialog.show()
Log.d(TAG,"URI:: ${photoURI}")
val uri = data.data
val filePath = mFirebaseStorage.child("Users").child(prefs.UID)
.child("ProfileImage").child(uri.lastPathSegment)
filePath.putFile(photoURI!!).addOnSuccessListener(OnSuccessListener <UploadTask.TaskSnapshot >() {
fun onSuccess(taskSnapshot : UploadTask.TaskSnapshot) {
toast("Upload Successful!")
progressDialog.dismiss()
}
}).addOnFailureListener(OnFailureListener () {
fun onFailure(e : Exception) {
Log.e(TAG, e.toString())
toast("Upload Failed!")
}
});
//val bundle = data.extras
}
}
createImageFile
private fun createImageFile(): File {
// Create an image file name
val timeStamp = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US)
val imageFileName = "JPEG_" + timeStamp + "_";
val storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES)
val image = File.createTempFile(
imageFileName, /* prefix */
".jpg", /* suffix */
storageDir /* directory */
);
// Save a file: path for use with ACTION_VIEW intents
mCurrentPhotoPath = image.absolutePath;
return image
}
AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.READ_PROFILE" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="ANDROID.PERMISSION.READ_EXTERNAL_STORAGE" />
<application
android:name=".App"
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/AppTheme">
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.android.projectrc.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/file_paths" />
</provider>
files_path.xml
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="my_images" path="Android/data/com.android.projectrc/files/Pictures" />
</paths>
Even photoURI shows up null when the console outputs it - I'm at quite a loss and would appreciate any help!
The answer to this related question explains that when a URI is passed as EXTRA_OUTPUT on the ACTION_IMAGE_CAPTURE intent, the URI is not returned as data on the intent parameter to onActivityResult().
This means you must save the URI in a class variable when it is generated so that it is available in onActivityResult(). It appears you already have photoURI declared as a class variable and that you intended to define it's value with this code in onLaunchCamera():
val photoURI = FileProvider.getUriForFile(this,
"com.android.projectrc.fileprovider",
photoFile)
But the val is creating a new instance of photoURI and the value is not stored in the class field as you want. Remove val.