Summary: Take a Picture Utilizing Camera Intent and Display the Photo with Correct Orientation (Works on Hopefully All Devices)

Summary: Take a picture utilizing Camera Intent and display the photo with correct orientation (works on hopefully all devices)

UPDATE: January 2nd, 2014:
I tried really hard to avoid implementing different strategies based on the device manufacturer. Unfortunately, I did not get around it. Going through hundreds of posts and talking to several developers, nobody found a solution that works on all devices without implementing device manufacturer specific code.

After I posted my solution here on StackOverflow, some developers asked me to publish my code on github. So here it is now: AndroidCameraUtil on github

The code was successfully tested on a wide variety of devices with Android API-Level >= 8. For a complete list, please see the Readme file on github.

The CameraIntentHelperActivity provides the main functionality, which is also described in more detail in the following.

Calling the default camera activity:

  • for Samsung and Sony devices: I call the camera activity with the method call to startActivityForResult. I only set the constant CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE. I do NOT set any other intent extras.
  • for all other devices: I call the camera activity with the method call to startActivityForResult as previously. This time, however, I additionally set the intent extra MediaStore.EXTRA_OUTPUT and provide an URI, where I want the image to be stored.

In both cases I remember the time the camera activity was started.


On camera activity result:

  1. Mediastore: First, I try to read the photo being captured from the MediaStore. Using a mangedQuery on the MediaStore content, I retrieve the latest image being taken, as well as its orientation property and its timestamp. If I find an image and it was not taken before the camera intent was called, it is the image I was looking for. Otherwise, I dismiss the result and try one of the following approaches.
  2. Intent extra: Second, I try to get an image Uri from intent.getData() of the returning intent. If this is not successful either, I continue with step 3.
  3. Default photo Uri: If all of the above mentioned steps did not work, I use the image Uri I passed to the camera activity.

At this point, I retrieved the photo Uri and its orientation which I pass to my UploadPhotoActivity.


Image processing

Please take a close look at my BitmapHelper class. It is based on the code described in detail in that tutorial.

Moreover, the shrinkBitmap method also rotates the image if required based on the orientation information extracted earlier.


I hope this is helpful to some of you.

Take photo via intent returns -1 as result code

override fun onActivityResult(requestCode: Int, resultCode: Int, resultData: Intent?) 

When onActivityResult returns -1 as return code, you are actually very lucky: -1 is nothing but Activity.RESULT_OK. Actually, with ACTION_IMAGE_CAPTURE intent, you don't care what the return code is; it's enough to check whether the temp file you opened in storageDir is not empty (an empty file is created in your app, and the image is stored there by a Camera app).

This said, I advice to use <external-files-path name="my_images" path="Pictures" />, to be less dependent on the implementation details of getExternalFilesDir().

If your activity has only one call to startActivityForResult(), you don't need to even check the requestCode.

Finally, in your scenario, requestData will probably be null. This parameter is only relevant when your ACTION_IMAGE_CAPTURE intent does not specify MediaStore.EXTRA_OUTPUT. Luckily, you need no information that requestData Intent could pass to you.

Camera Image Capture Does Not Return Data - on Some Device(s)

Android 5.0 has some extra filtering to handle the Intent. Therefore, you might have to handle it this way. You can give it a try as it has been changed in Camera API for 5.0

Part of it is here

static final int REQUEST_IMAGE_CAPTURE = 1;

private void dispatchTakePictureIntent() {
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);
}
}

More details can be found in the documentation for Android 5.0 API changes.

https://developer.android.com/training/camera-deprecated/photobasics#TaskPath

How to use stock camera app to take photo with Jetpack Compose?

You have to use the activity contracts, see this article for details

class ComposeFileProvider : FileProvider(
R.xml.filepaths
) {
companion object {
fun getImageUri(context: Context): Uri {
val directory = File(context.cacheDir, "images")
directory.mkdirs()
val file = File.createTempFile(
"selected_image_",
".jpg",
directory,
)
val authority = context.packageName + ".fileprovider"
return getUriForFile(
context,
authority,
file,
)
}
}
}

@Composable
fun ImagePicker(
modifier: Modifier = Modifier,
) {
var hasImage by remember {
mutableStateOf(false)
}
var imageUri by remember {
mutableStateOf<Uri?>(null)
}

val imagePicker = rememberLauncherForActivityResult(
contract = ActivityResultContracts.GetContent(),
onResult = { uri ->
hasImage = uri != null
imageUri = uri
}
)

val cameraLauncher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.TakePicture(),
onResult = { success ->
hasImage = success
}
)

val context = LocalContext.current
Box(
modifier = modifier,
) {
if (hasImage && imageUri != null) {
AsyncImage(
model = imageUri,
modifier = Modifier.fillMaxWidth(),
contentDescription = "Selected image",
)
}
Column(
modifier = Modifier
.align(Alignment.BottomCenter)
.padding(bottom = 32.dp),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Button(
onClick = {
imagePicker.launch("image/*")
},
) {
Text(
text = "Select Image"
)
}
Button(
modifier = Modifier.padding(top = 16.dp),
onClick = {
val uri = ComposeFileProvider.getImageUri(context)
imageUri = uri
cameraLauncher.launch(uri)
},
) {
Text(
text = "Take photo"
)
}
}
}
}

Why does an image captured using camera intent gets rotated on some devices on Android?

Most phone cameras are landscape, meaning if you take the photo in portrait, the resulting photos will be rotated 90 degrees. In this case, the camera software should populate the Exif data with the orientation that the photo should be viewed in.

Note that the below solution depends on the camera software/device manufacturer populating the Exif data, so it will work in most cases, but it is not a 100% reliable solution.

ExifInterface ei = new ExifInterface(photoPath);
int orientation = ei.getAttributeInt(ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_UNDEFINED);

Bitmap rotatedBitmap = null;
switch(orientation) {

case ExifInterface.ORIENTATION_ROTATE_90:
rotatedBitmap = rotateImage(bitmap, 90);
break;

case ExifInterface.ORIENTATION_ROTATE_180:
rotatedBitmap = rotateImage(bitmap, 180);
break;

case ExifInterface.ORIENTATION_ROTATE_270:
rotatedBitmap = rotateImage(bitmap, 270);
break;

case ExifInterface.ORIENTATION_NORMAL:
default:
rotatedBitmap = bitmap;
}

Here is the rotateImage method:

public static Bitmap rotateImage(Bitmap source, float angle) {
Matrix matrix = new Matrix();
matrix.postRotate(angle);
return Bitmap.createBitmap(source, 0, 0, source.getWidth(), source.getHeight(),
matrix, true);
}

Avoiding the preview after taking pictures with the integrated camera

There is no possibility to disable the preview programmatically. If something different is needed as the settings of the default camera, an own camera has to be implemented :-(

Can't receive Intent.extras() from Camera after making a photo

I would not expect data to be null there. However, there should not be any extras on that Intent. You only look for the "data" extra if you are not specifying EXTRA_OUTPUT. If you are specifying EXTRA_OUTPUT, you go get the photo from the path that you provided in EXTRA_OUTPUT, and you ignore the Intent delivered to onActivityResult().

With regards to the null data Intent itself, that may be something peculiar to the camera app that you are using. Please bear in mind that using ACTION_IMAGE_CAPTURE means that you are relying upon a third-party app to take the picture, and third-party apps can have bugs.



Related Topics



Leave a reply



Submit