android camera: onActivityResult() intent is null if it had extras - android

After searching a lot in all the related issues at Stack Overflow and finding nothing, please try to help me.
I created an intent for capture a picture. Then I saw different behavior at onActivityResult(): if I don't put any extra in the Intent (for small pics) the Intent in onActivityResult is ok, but when I put extras in the intent for writing the pic to a file, the intent in onActivityResult is null!
The Intent creation:
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
// without the following line the intent is ok
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(f));
startActivityForResult(takePictureIntent, actionCode);
Why is it null, and how can I solve it?

It happens the same to me, if you are providing MediaStore.EXTRA_OUTPUT, then the intent is null, but you will have the photo in the file you provided (Uri.fromFile(f)).
If you don't specify MediaStore.EXTRA_OUTPUT then you will have an intent which contains the uri from the file where the camera has saved the photo.
Don't know if it as a bug, but it works that way.
EDIT: So in onActivityResult() you no longer need to check for data if null. The following worked with me:
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case PICK_IMAGE_REQUEST://actionCode
if (resultCode == RESULT_OK && data != null && data.getData() != null) {
//For Image Gallery
}
return;
case CAPTURE_IMAGE_REQUEST://actionCode
if (resultCode == RESULT_OK) {
//For CAMERA
//You can use image PATH that you already created its file by the intent that launched the CAMERA (MediaStore.EXTRA_OUTPUT)
return;
}
}
}
Hope it helps

A sample written in Kotlin. You create a Uri for camera app, CameraFragment holds it until camera returns from saving your picture and gives it back to you in onActivityResult as you would expect.
CameraFragment.kt
Acts as an intermediary between consumer and camera app. Takes Uri as input and returns it in data Intent.
class CameraFragment : Fragment() {
companion object {
val TAG = CameraFragment::class.java.simpleName
private val KEY_URI = ".URI"
fun newInstance(uri: Uri, targetFragment: Fragment, requestCode: Int): CameraFragment {
val args = Bundle()
args.putParcelable(KEY_URI, uri)
val fragment = CameraFragment()
fragment.arguments = args
fragment.setTargetFragment(targetFragment, requestCode)
return fragment
}
}
private lateinit var uri: Uri
private var fired = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
retainInstance = true
fired = savedInstanceState?.getBoolean("fired") ?: false
if (!fired) {
val args = arguments
uri = args.getParcelable(KEY_URI)
val i = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
i.putExtra(MediaStore.EXTRA_OUTPUT, uri)
i.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
context.grantUriPermission(i, uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
startActivityForResult(i, targetRequestCode)
fired = true
}
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putBoolean("fired", fired)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == targetRequestCode) {
context.revokeUriPermission(uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
val newData = Intent()
newData.data = uri
targetFragment.onActivityResult(requestCode, resultCode, newData)
dismiss()
}
}
private fun dismiss() {
fragmentManager.beginTransaction().remove(this).commit()
}
}
/** Grant Uri permissions for all camera apps. */
fun Context.grantUriPermission(intent: Intent, uri: Uri, modeFlags: Int) {
val resolvedIntentActivities = packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
for (resolvedIntentInfo in resolvedIntentActivities) {
val packageName = resolvedIntentInfo.activityInfo.packageName;
grantUriPermission(packageName, uri, modeFlags);
}
}
Invoke camera intent
this is a fragment in your app which will trigger the camera. RC_CAMERA is your request code for this action.
val uri = /* Your output Uri. */
val f = CameraFragment.newInstance(uri, this, RC_CAMERA)
fragmentManager.beginTransaction().add(f, CameraFragment.TAG).commit()
Handle camera result
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
when(requestCode) {
RC_CAMERA -> {
if (resultCode == Activity.RESULT_OK) {
val uri = data?.data
// Do whatever you need.
}
}
}
}

Where did you create the ffor the Uri.fromFile(f)?
It must be a valid File object. Try to create it before the EXTRA_OUTPUT line.
File f = new File("valid path");
Try with something like this:
File file = new File(dataFile);
Uri outFileUri = Uri.fromFile(file);
Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
intent.putExtra(MediaStore.EXTRA_OUTPUT, outFileUri);
startActivityForResult(intent, TAKE_PHOTO);

use the following:
Bitmap bitmap = data.getExtras().getParcelable("data");
it works.

Related

onActivityResult not getting called after changing the intent for (Save the full-size photo)

Im following the official documentation to capture the image and gets its path.
https://developer.android.com/training/camera/photobasics
I have write 100% same code as the documentation say, but two weird things are happing when I write the simple intent
private fun dispatchTakePictureIntent() {
val takePictureIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
try {
startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE)
} catch (e: ActivityNotFoundException) {
// display error state to the user
}
}
The OnAcivityResult is getting called
But when I change the intent into
private fun dispatchTakePictureIntent() {
Intent(MediaStore.ACTION_IMAGE_CAPTURE).also { takePictureIntent ->
// Ensure that there's a camera activity to handle the intent
takePictureIntent.resolveActivity(packageManager)?.also {
// Create the File where the photo should go
val photoFile: File? = try {
createImageFile()
} catch (ex: IOException) {
// Error occurred while creating the File
...
null
}
// Continue only if the File was successfully created
photoFile?.also {
val photoURI: Uri = FileProvider.getUriForFile(
this,
"com.example.android.fileprovider",
it
)
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI)
startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE)
}
}
}
}
It does get called anymore.
Here is my OnAcivityResult code
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if(requestCode == OPEN_CAMERA)
if(resultCode == RESULT_OK && data != null) {
// not getting called
}
if(requestCode == OPEN_GALLERY)
if(resultCode == RESULT_OK && data != null) {
val image: Uri = data.data!!
openCropImageView(image)
}
if (resultCode == RESULT_OK && requestCode == UCrop.REQUEST_CROP) {
val uri = UCrop.getOutput(data!!)
val imgFile = File(uri?.path)
if (imgFile.exists()) {
val myBitmap = BitmapFactory.decodeFile(imgFile.absolutePath)
binding.previewImage.setImageBitmap(myBitmap)
}
}
}
any help, please.
Edit: Apparently its working fine on a pixel 5 running on android 11
emulator and its not working on Redmi note 5 running on android 8

How to load audio file in ListView?

I need to load random audio files from storage, into my ListView, on a button click. But I don't understand how can I do this. When I call intent, file manager open to pick file. My song must have title, artist name and url. But after clicking it doesn't add. This is my code. Don't scold me for a possibly dull question. I'm new at android developing.
private fun loadSong() {
val intent: Intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
type = "audio/*"
action = Intent.ACTION_GET_CONTENT
}
startActivityForResult(Intent.createChooser(intent, "Select Audio"), PICK_AUDIO)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == PICK_AUDIO && resultCode == RESULT_OK){
if (data != null) {
audioUri = data.data
}
}
adapter = SongsAdapter(listSongs)
twoTracksListView.adapter = adapter
}

Choose a pdf from storage and open it with a pdf app

I like to choose a pdf file from storage. Afterwards it should open the choosen pdf file with a pdf reader app, that the user chooses. I can choose a file, but afterwards the app crashes and it throws:
android.content.ActivityNotFoundException: No Activity found to handle Intent
I tried:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main_menu)
btnTravels.setOnClickListener {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT)
//val intent = Intent(Intent.ACTION_GET_CONTENT)
intent.type = "application/pdf"
intent.addCategory(Intent.CATEGORY_OPENABLE)
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
startActivityForResult(intent, R.integer.request_code_pdf)
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode == Activity.RESULT_OK) {
if (requestCode == R.integer.request_code_pdf) {
if (data != null) {
Log.i("gotresult", data.data.toString())
data.data?.also {uri ->
val intent = Intent(Intent.ACTION_VIEW)
intent.setDataAndType(uri,"document/pdf")
intent.flags = Intent.FLAG_ACTIVITY_NO_HISTORY
startActivity(intent)
}
}
}
}
}
The Log.i shows me this. It doesn't really look like a Filename:
content://com.android.providers.downloads.documents/document/msf%3A462052
Replace:
val intent = Intent(Intent.ACTION_VIEW)
intent.setDataAndType(uri,"document/pdf")
intent.flags = Intent.FLAG_ACTIVITY_NO_HISTORY
startActivity(intent)
with:
val intent = Intent(Intent.ACTION_VIEW, uri)
intent.flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
startActivity(intent)
document/pdf is not a valid MIME type, and you need to grant permission to the viewer app to read the content.
Also, remove intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true), since you are not handling multiple documents.
It doesn't really look like a Filename
It is not supposed to be a filename. It is supposed to be a Uri pointing to a document that the user selected via ACTION_OPEN_DOCUMENT.

uploading image from gallery and camera to server

I need to save the image that is taken from camera or image taken from gallery in kotlin. for now I've done this part and i'm finding other examples in kotlin but i'm unable to find. In other examples, images were send in multipart which was coded in java.
These are listeners
private val cameraRequest = 1888
private val pickImage = 100
private var imageUri: Uri? = null
takePhoto.setOnClickListener {
val cameraIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
startActivityForResult(cameraIntent, cameraRequest)
}
choosePicture.setOnClickListener {
val gallery = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.INTERNAL_CONTENT_URI)
startActivityForResult(gallery, pickImage)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == cameraRequest) {
val photo: Bitmap = data?.extras?.get("data") as Bitmap
takePhoto.setImageBitmap(photo)
}
if (resultCode == RESULT_OK && requestCode == pickImage) {
imageUri = data?.data
if (imageUri != null) {
choosePicture.setImageURI(imageUri)
}
}
}
You can use this library for making a multipart request: https://github.com/gotev/android-upload-service
Also, it will handle app behaviour for you which includes upload-retry on slow internet connection and many more.

Passing extras with Intent.createChooser

I am using following code to select picture from gallery. I want to track the int value from this intent.
val intent = Intent()
intent.type = "image/*"
intent.action = Intent.ACTION_GET_CONTENT
intent.putExtra("Position", 1)
startActivityForResult(Intent.createChooser(intent, "Select Picture"), SELECT_PICTURE)
But I am getting only default value[0] when trying to get the passed value from intent in onActivityResult.
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode == RESULT_OK) {
if (requestCode == SELECT_PICTURE) {
val selectedImageURI = data?.data
val position = data?.getIntExtra("Position", 0)
}
}
}
So my doubt is, is it possible to track the values through intent chooser? If yes, how?
you have to use requestCode in this line:
startActivityForResult(Intent.createChooser(intent, "Select Picture"), requestCode)
// for example use SELECT_PICTURE_POSITION_1 as requestCode
then you get it back here:
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode == RESULT_OK) {
when (requestCode) {// use your request code here
SELECT_PICTURE_POSITION_0 -> {
val selectedImageURI = data?.data
val position = data?.getIntExtra("Position", 0)
}
SELECT_PICTURE_POSITION_1 -> {
val selectedImageURI = data?.data
val position = data?.getIntExtra("Position", 1)
}
SELECT_PICTURE_POSITION_2 -> {
val selectedImageURI = data?.data
val position = data?.getIntExtra("Position", 2)
}
//other conditions here
}
}
}
use multiple request codes for your needs.
is it possible to track the values through intent chooser?
No. Your problem has nothing to do with the chooser. You would have the same results with startActivityForResult(intent), SELECT_PICTURE).
There is no requirement for the activity being started by startActivityForResult() to return to you any extras that you happen to have put in that Intent.

Categories

Resources