Espresso test the camera intent when imageuri is passed as extra - android

I need to stub the camera intent by creating a image file at the path provided in the intent extra.
Espresso can only respond with activityresult. Where can i perform the operation to create the file at passed path from intent extra.
Code for launching camera
File destination = new File(Environment.getExternalStorageDirectory(), "app_name" + System.currentTimeMillis() + ".jpg");
imageUri = FileProvider.getUriForFile(getApplicationContext(), getApplicationContext().getPackageName() + ".fileprovider", destination);
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
startActivityForResult(intent, AppConstants.REQUEST_CODE_CAMERA);
Code for stubbing intent in test
Instrumentation.ActivityResult result = new Instrumentation.ActivityResult(Activity.RESULT_OK, null);
intending(hasAction(MediaStore.ACTION_IMAGE_CAPTURE)).respondWith(result);

Ismael answer is perfect. For those looking for solution in java, Here it is.
intending(hasAction(MediaStore.ACTION_IMAGE_CAPTURE)).respondWith(
new Instrumentation.ActivityResult(Activity.RESULT_OK, null));
IntentCallback intentCallback = new IntentCallback() {
#Override
public void onIntentSent(Intent intent) {
if (intent.getAction().equals("android.media.action.IMAGE_CAPTURE")) {
try {
Uri imageUri = intent.getParcelableExtra(MediaStore.EXTRA_OUTPUT);
Context context = InstrumentationRegistry.getTargetContext();
Bitmap icon = BitmapFactory.decodeResource(
context.getResources(),
R.mipmap.ic_launcher);
OutputStream out = getTargetContext().getContentResolver().openOutputStream(imageUri);
icon.compress(Bitmap.CompressFormat.JPEG, 100, out);
out.flush();
out.close();
} catch (IOException e) {
GenericUtility.handleException(e);
}
}
}
};
IntentMonitorRegistry.getInstance().addIntentCallback(intentCallback);
//Perform action here
onView(withId(R.id.tv_take_photo)).perform(click());

You need to create a IntentCallback to intercept the Uri value and save a sample image there.
Sample in Kotlin
intentCallback = IntentCallback {
if (it.action == "android.media.action.IMAGE_CAPTURE") {
it.extras.getParcelable<Uri>("output").run {
val inStream = Resources.getResource(sampleImageFileName).openStream()
val outStream = activity.contentResolver.openOutputStream(this)
ByteStreams.copy(inStream, outStream)
}
}
}
You need to register your callback before the intent trigger event
IntentMonitorRegistry.getInstance().addIntentCallback(intentCallback)
And don't to forget to unregister at end
IntentMonitorRegistry.getInstance().removeIntentCallback(intentCallback)

Ismael and Gupta's answers are correct. For those who want a complete example, I made a complete solution based on their example in Kotlin. The code below takes photo for multiple imageView and also verifies if the correct image is loaded in the respective imageview by checking the imageView.tag property. From the development code, the image name has to be set in the imageView.setTag(imageName)
private var imageName = "No Image Name"
#Test
fun verifyPhotoTaken() {
intending(hasAction(MediaStore.ACTION_IMAGE_CAPTURE)).respondWith(
ActivityResult(Activity.RESULT_OK, null))
takePhoto(R.id.imageview1, R.drawable.ic_launcher)
takePhoto(R.id.imageview2, R.drawable.some_image)
}
private fun takePhoto(imageViewId : Int, resourceId : Int) {
val cameraIntentCallback = intentCallback(resourceId)
IntentMonitorRegistry.getInstance().addIntentCallback(cameraIntentCallback)
onView(withId(imageViewId)).perform(click())
onView(withId(imageViewId)).check(matches(hasDrawable(imageName)))
IntentMonitorRegistry.getInstance().removeIntentCallback(cameraIntentCallback)
}
private fun intentCallback(resourceId : Int = R.drawable.ic_launcher) :IntentCallback {
return IntentCallback {
if (it.action == MediaStore.ACTION_IMAGE_CAPTURE) {
it.extras?.getParcelable<Uri>(MediaStore.EXTRA_OUTPUT).run {
imageName = File(it.getParcelableExtra<Parcelable>(MediaStore.EXTRA_OUTPUT).toString()).name
val context : Context = InstrumentationRegistry.getInstrumentation().targetContext
val outStream = context.contentResolver.openOutputStream(this)
val bitmap : Bitmap = BitmapFactory.decodeResource(context.resources, resourceId)
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outStream)
}
}
}
}
fun hasDrawable(drawableResource: String) : BoundedMatcher<View, ImageView> {
return object : BoundedMatcher<View, ImageView> (ImageView::class.java) {
override fun describeTo(description: Description?) {
description?.appendText("has drawable")
}
override fun matchesSafely(item: ImageView?): Boolean {
return item?.drawable != null && item.tag.toString().contains(drawableResource)
}
}
}

Related

Android: How to differentiate between multiple intents when using ActivityResultLauncher

I am creating a Intent chooser to choose between a the phone camera app and the gallery/file manager.
Intent chooserIntent = Intent.createChooser(clickPhoto(),"Set Image Using");
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS,openGallery());
startActivityForResult.launch(chooserIntent);
Click Photo method:
private Intent clickPhoto() {
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
ComponentName componentName = takePictureIntent.resolveActivity(requireActivity().getPackageManager());
if (componentName != null) {
try {
createImageFile();
mimeType = "image/*";
ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.DISPLAY_NAME, getNewFileName());
values.put(MediaStore.Images.Media.MIME_TYPE, mimeType);
values.put(MediaStore.Images.Media.RELATIVE_PATH, getImageDirectoryPath());
Uri imageUri = requireActivity().getContentResolver().insert(MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL), values);
if (imageUri != null) {
currentPhotoPath = imageUri.toString();
shareUri = imageUri;
}
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
// initRequestCode(takePictureIntent, requestCode);
} catch (IOException ioException) {
Toast.makeText(requireContext(), ioException.getMessage().toString(), Toast.LENGTH_LONG).show();
}
}
return takePictureIntent;
}
Open gallery method:
private Intent openGallery(){
mimeType = "image/*";
Intent intent = new Intent();
Uri collection = MediaStore.Video.Media.getContentUri(
MediaStore.VOLUME_EXTERNAL);
try {
intent =
new Intent(Intent.ACTION_PICK, collection).setType(mimeType);
intent.resolveActivity(requireActivity().getPackageManager());
// initRequestCode(intent, requestCode);
} catch (ActivityNotFoundException e) {
Toast.makeText(requireContext(), e.getLocalizedMessage(), Toast.LENGTH_LONG).show();
}
return intent;
}
The ActivityResultLauncher:
ActivityResultLauncher<Intent> startActivityForResult = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(),
result -> {
if (result.getResultCode() == Activity.RESULT_OK) {
//how to tell which intent the user selected ?
}
});
how do I know if the user took a piture using the camera or picked an image with the file picker ?
You can put an extra integer to each intent before returning them which you can access in result, say:
Global variables
final String SOURCE = "source";
final int SOURCE_CAMERA = 0;
final int SOURCE_GALLERY = 1;
final int SOURCE_UNKNOWN = 2;
For camera
...
takePictureIntent.putExtra(SOURCE, SOURCE_CAMERA);
return takePictureIntent;
For gallery
..
intent.putExtra(SOURCE, SOURCE_GALLERY);
return intent;
ActivityResultLauncher
if (result.getResultCode() == Activity.RESULT_OK) {
//Identify source
int mySource = result.getData().getIntExtra(SOURCE, SOURCE_UNKNOWN )
}
You can differentiate like this,
If one method is for camera intent,
private fun openCamera() {
val takePictureIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
if (takePictureIntent.resolveActivity(packageManager) != null) {
cameraImageFile = getTempFile()
cameraImageFile?.let {
val imageUri = FileProvider.getUriForFile(
this,
"${BuildConfig.APPLICATION_ID}.fileprovider",
it
)
cameraLauncher.launch(imageUri)
}
}
}
and another method is for gallery intent
private fun openGallery() {
galleryLauncher.launch("image/*")
}
Then you can define them like below in activity,
//Todo for gallery
private val galleryLauncher = registerForActivityResult(ActivityResultContracts.GetContent()) {
//code here for gallery
}
//Todo for camera
private val cameraLauncher = registerForActivityResult(ActivityResultContracts.TakePicture()) {
//code here for camera
}
That's it!
Declare some variables
companion object{
private const val CAMERA_ACTION =1
private const val GALLERY_ACTION =2
private const val CHOOSER_INTENT_ACTION =3
}
private var mActionTriggered =0
then follow
private fun openCamera() {
mActionTriggered = CAMERA_ACTION
}
private fun openGallery() {
mActionTriggered =GALLERY_ACTION
}
private fun openChooser() {
mActionTriggered =CHOOSER_INTENT_ACTION
}
onResult
private val startActivityForResult = registerForActivityResult(
StartActivityForResult()
) { result: ActivityResult ->
if (result.resultCode == RESULT_OK) {
when (mActionTriggered) {
CAMERA_ACTION -> {
mActionTriggered = 0
//TODO your logics
}
GALLERY_ACTION -> {
mActionTriggered = 0
//TODO your logics
}
else -> {
mActionTriggered = 0
//TODO your logics
}
}
}else{
mActionTriggered = 0
}
}
It feels a little overkill, but you can use the refinement mechanism of the chooser activity.
You need to implement the refinement activity yourself, but you can use the EXTRA_RESULT_RECEIVER that the chooser passes along to send back the result.
Intent refinementIntent = new Intent(this, RefinementActivity.class);
PendingIntent refinementPendingIntent = PendingIntent.getActivity(this, 0, refinementIntent, 0);
Intent clickPhotoIntent = clickPhoto();
Intent chooserIntent = Intent.createChooser(clickPhoto(),"Set Image Using");
chooserIntent.putExtra(
Intent.EXTRA_CHOOSER_REFINEMENT_INTENT_SENDER, refinementPendingIntent.getIntentSender());
chooserIntent.putExtra(Intent.EXTRA_RESULT_RECEIVER, new ResultReceiver() {
#Override
public void onReceive(int resultCode, Bundle resultData) {
Intent startedIntent = resultData.getParcelable("extra_selected_intent");
// Check which intent was selected.
// Note that this is called when the selection happened, before the
// started activity returned. Probably what you want to do here is
// cache the value in a field and then check it when you get
// the activity result.
}
});
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, openGallery());
startActivityForResult.launch(chooserIntent);
And then in RefinementActivity.java, you will need to send the selected intent back to the sender
#Override
public void onCreate(Bundle savedInstanceState) {
Intent selectedIntent = getIntent().getParcelableExtra(Intent.EXTRA_INTENT);
ResultReceiver receiver = getIntent().getParcelableExtra(Intent.EXTRA_RESULT_RECEIVER);
Bundle resultData = new Bundle();
resultData.putParcelable("extra_selected_intent", selectedIntent);
receiver.send(RESULT_OK, resultData);
startActivity(selectedIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT));
finish();
}
You can initialize Global Boolean value name isGallery and set it true in openGallery() and false in clickPhoto()
Like the code below:
private boolean isGallery ;
private Intent clickPhoto() {
isGallery = false;
}
private Intent openGallery() {
isGallery = true;
}
Now your ActivityResultLauncher will be like this:
ActivityResultLauncher<Intent> startActivityForResult =
registerForActivityResult(new ActivityResultContracts.StartActivityForResult(),
result -> {
if (result.getResultCode() == Activity.RESULT_OK) {
if (isGallery) {
// user picked an image with the file picker
} else {
// user took a picture using the camera
}
}
});

ActivityNotFoundException when accessing the camera using registerForActivityResult

My API 28 application throws this ActivityNotFoundException when trying to capture photos with the camera (both in the Emulator as well as with a real device) using registerForActivityResult:
"No Activity found to handle Intent { act=android.media.action.IMAGE_CAPTURE typ=image/jpg flg=0x3 clip={text/uri-list U:content://... "
`public class IntentHelper {
private static final String AUTHORITY = BuildConfig.APPLICATION_ID + ".fileprovider";
File output = null;
private final ActivityResultRegistry mRegistry;
private ActivityResultLauncher<Intent> takePhotoActivityResultLauncher;
private final Context context;
private final Activity activity;
private final FragmentOperationBinding binding;
public IntentHelper(Context context, Activity activity, FragmentOperationBinding binding, ActivityResultRegistry registry) {
this.mRegistry = registry;
this.context = context;
this.activity = activity;
this.binding = binding;
}
public void handleIntent() {
setUpLauncher();
createIntent();
}
private void setUpLauncher() {
takePhotoActivityResultLauncher = mRegistry.register("key",
new ActivityResultContracts.StartActivityForResult(),
new ActivityResultCallback<ActivityResult>() {
#Override
public void onActivityResult(ActivityResult result) {
if (result.getResultCode() == Activity.RESULT_OK) {
final int THUMBSIZE = 64;
Bitmap ThumbImage = ThumbnailUtils.extractThumbnail(BitmapFactory.decodeFile(output.getAbsolutePath()),
THUMBSIZE, THUMBSIZE);
binding.photoThumbnail.setImageBitmap(ThumbImage);
Intent returnToCallingActivityIntent = new Intent(context, OperationFragment.class);
Uri outputUri = FileProvider.getUriForFile(context, AUTHORITY, output);
returnToCallingActivityIntent.setDataAndType(outputUri, "image/jpeg");
returnToCallingActivityIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
}
});
}
private void createIntent() {//Create Intent
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (takePictureIntent.resolveActivity(activity.getPackageManager()) != null) {
// Create the File where the photo should go
try {
output = createImageFile();
} catch (IOException ex) {
// Error occurred while creating the File
Toast.makeText(context, ex.getMessage(), Toast.LENGTH_LONG).show();
}
takePictureIntent.setType("image/jpg");
Uri photoURI = FileProvider.getUriForFile(context, AUTHORITY, output);
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
takePictureIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
}
binding.photoThumbnail.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//Launch activity to get result
takePhotoActivityResultLauncher.launch(takePictureIntent);
}
});
}
private File createImageFile() throws IOException {
// Create an image file name
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String imageFileName = binding.getOperation().getIdentifier() + "_" + timeStamp;
File storageDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS + "/operationtimerecord");
File image = File.createTempFile(
imageFileName, /* prefix */
".jpg", /* suffix */
storageDir /* directory */
);
// Save a file: path for use with ACTION_VIEW intents
String currentPhotoPath = image.getAbsolutePath();
return image;
}
}`
I call the handleIntent method in an object of this custom IntentHelper class from a fragment, triggered by a click event. The ActivityResultRegistry parameter passed in the constructor is retrieved in that fragment from the main activity.
The application does create empty files in the corresponding folder while he device camera does not open.
The job worked fine before when I was using the deprecated startActivityForResult, so I assume that permissions, paths and dependencies are fine...
Remove the setType() call. And, consider replacing all of this with ActivityResultContracts.TakePicture().

Download a PDF into android device download folder Android

I have the following code in my activity that I use to generate a PDF file and save it into the device storage when I click a button I call saveFileToStorageIntent()
The problem that I am finding is that it is not downloaded automatically, instead, this opens another Document provider activity with save option. I want that the pdf file saves directly into Document folder from a single click on my application. What can I do to download the pdf directly to the device?
private fun saveFileToStorageIntent() {
val intent = Intent(Intent.ACTION_CREATE_DOCUMENT)
intent.addCategory(Intent.CATEGORY_OPENABLE)
intent.type = MimeTypeMap.getSingleton().getMimeTypeFromExtension("pdf")
intent.putExtra(Intent.EXTRA_TITLE, "invoice.pdf")
startActivityForResult(intent, CREATE_FILE)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == CREATE_FILE) {
if (resultCode == RESULT_OK && data != null) {
writePDFToFile(data.data, presenter.ticketResponse()!!)
}
}
}
private fun writePDFToFile(uri: Uri?, body: ResponseBody) {
var inputStream: InputStream? = null
var outputStream: OutputStream? = null
try {
val fileSize = body.contentLength()
val fileReader = ByteArray(fileSize.toInt())
var fileSizeDownloaded: Long = 0
inputStream = body.byteStream()
outputStream = contentResolver.openOutputStream(uri!!)
while (true) {
val read: Int = inputStream.read(fileReader)
if (read == -1) {
break
}
outputStream?.write(fileReader, 0, read)
fileSizeDownloaded += read.toLong()
}
outputStream?.flush()
} catch (e: Exception) {
Logger.print(TAG, e.message)
} finally {
if (inputStream != null) {
try {
inputStream.close()
} catch (e: IOException) {
e.printStackTrace()
}
}
if (outputStream != null) {
try {
outputStream.close()
} catch (e: IOException) {
e.printStackTrace()
}
}
}
}
Why are you not using DownloadManager for that?
try{
Uri uri = Uri.parse(downloadPath);
DownloadManager.Request request = new DownloadManager.Request(uri);
request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_MOBILE | DownloadManager.Request.NETWORK_WIFI); // Tell on which network you want to download file.
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED); // This will show notification on top when downloading the file.
request.setTitle("Downloading data..."); // Title for notification.
request.setVisibleInDownloadsUi(true);
request.addRequestHeader("Authorization", "bearer my bearertoken"));
request.setDestinationInExternalFilesDir(this, Environment.DIRECTORY_DOCUMENTS, id+".pdf");
((DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE)).enqueue(request); // This will start downloading
}catch(Exception e){
}
You can change the file path to Downloads in the above code.

Deprecated "getBitmap" with API 29. Any alternative codes?

My onActivityResult is not working because getBitmap is deprecated. Is there any alternative code to achieve this?
Here is the code that needs to be changed (any suggestions?):
val bitmap = MediaStore.Images.Media.getBitmap(contentResolver, selectedPhotoUri)
The getBitmap is crossed out in my tooling with a message that says it's deprecated.
This worked for me,
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if(requestCode == 1 && resultCode == Activity.RESULT_OK && data != null) {
val selectedPhotoUri = data.data
try {
selectedPhotoUri?.let {
if(Build.VERSION.SDK_INT < 28) {
val bitmap = MediaStore.Images.Media.getBitmap(
this.contentResolver,
selectedPhotoUri
)
imageView.setImageBitmap(bitmap)
} else {
val source = ImageDecoder.createSource(this.contentResolver, selectedPhotoUri)
val bitmap = ImageDecoder.decodeBitmap(source)
imageView.setImageBitmap(bitmap)
}
}
} catch (e: Exception) {
e.printStackTrace()
}
}
}
This worked well for me in java
ImageDecoder.Source source = ImageDecoder.createSource(this.getContentResolver(), pictureUri);
Bitmap bitmap = ImageDecoder.decodeBitmap(source);
You can use:
private fun getCapturedImage(selectedPhotoUri: Uri): Bitmap {
val bitmap = when {
Build.VERSION.SDK_INT < 28 -> MediaStore.Images.Media.getBitmap(
this.contentResolver,
selectedPhotoUri
)
else -> {
val source = ImageDecoder.createSource(this.contentResolver, selectedPhotoUri)
ImageDecoder.decodeBitmap(source)
}
}
Check the official doc:
This method was deprecated in API level 29.
loading of images should be performed through ImageDecoder#createSource(ContentResolver, Uri), which offers modern features like PostProcessor.
You can use this code for creating bitmap
Bitmap bitmap;
if (Build.VERSION.SDK_INT >= 29) {
ImageDecoder.Source source = ImageDecoder.createSource(getApplicationContext().getContentResolver(), imageUri);
try {
bitmap = ImageDecoder.decodeBitmap(source);
} catch (IOException e) {
e.printStackTrace();
}
} else {
try {
bitmap = MediaStore.Images.Media.getBitmap(getApplicationContext().getContentResolver(), imageUri);
} catch (IOException e) {
e.printStackTrace();
}
}
It was evident that the getBitmap API doesn't work with the latest Android SDK - 29. So, this worked for me
Uri contentURI = data.getData();
try {
imageView.setImageURI(contentURI);
} catch (Exception e) {
e.printStackTrace();
}
Please let me know if this doesn't work for any of you, shall other options!
I have created a class for loading a Bitmap from uri:
public class BitmapResolver {
private final static String TAG = "BitmapResolver";
#SuppressWarnings("deprecation")
private static Bitmap getBitmapLegacy(#NonNull ContentResolver contentResolver, #NonNull Uri fileUri){
Bitmap bitmap = null;
try {
bitmap = MediaStore.Images.Media.getBitmap(contentResolver, fileUri);
} catch (IOException e) {
e.printStackTrace();
}
return bitmap;
}
#TargetApi(Build.VERSION_CODES.P)
private static Bitmap getBitmapImageDecoder(#NonNull ContentResolver contentResolver, #NonNull Uri fileUri){
Bitmap bitmap = null;
try {
bitmap = ImageDecoder.decodeBitmap(ImageDecoder.createSource(contentResolver, fileUri));
} catch (IOException e) {
e.printStackTrace();
}
return bitmap;
}
public static Bitmap getBitmap(#NonNull ContentResolver contentResolver, Uri fileUri){
if (fileUri == null){
Log.i(TAG, "returning null because URI was null");
return null;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P){
return getBitmapImageDecoder(contentResolver, fileUri);
} else{
return getBitmapLegacy(contentResolver, fileUri);
}
}
}
Just to save you some time ...
ImageDecoder.createSource(this.getContentResolver(), pictureUri)
works fine, but to be able to use this code, mindSdkVersion should be at least 28.
Have you tried this?
val bitmap = ImageDecoder.createSource(contentResolver, uri)
hi freind you check api device
var Image_select: String? = null
var bitmap:Bitmap?=null
you show image set
binding?.ImAvator?.setImageURI(data!!.data)
try {
val uri: Uri? = data!!.data
bitmap = if(Build.VERSION.SDK_INT>=29){
val source: ImageDecoder.Source = ImageDecoder.createSource(requireActivity()
.contentResolver, uri!!)
ImageDecoder.decodeBitmap(source)
} else{
MediaStore.Images.Media.getBitmap(requireActivity().contentResolver, uri!!)
}
} catch (e: IOException) {
e.printStackTrace()
}
when upload image
compress bitmap send server
fun Camparse() {
val size = (bitmap!!.height * (812.0 / bitmap!!.width)).toInt()
val b = Bitmap.createScaledBitmap(bitmap!!, 812, size, true)
val by = ByteArrayOutputStream()
b.compress(Bitmap.CompressFormat.JPEG, 100, by)
val bytes = by.toByteArray()
Image_select = Base64.encodeToString(bytes, 0)
}
For deprecated MediaStore.Images.Media.getBitmap() in API level 29, You can use this code:
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(resultCode == Activity.RESULT_OK) {
if (requestCode == GALLERY_REQUEST) {
Uri selectedImage = data.getData();
try {
if (Build.VERSION.SDK_INT < 29) {
Bitmap bitmap = MediaStore.Images.Media.getBitmap(getActivity().getContentResolver(), selectedImage);
imageView2.setImageBitmap(bitmap);
} else {
ImageDecoder.Source source = ImageDecoder.createSource(getActivity().getContentResolver(), selectedImage);
Bitmap bitmap = ImageDecoder.decodeBitmap(source);
imageView2.setImageBitmap(bitmap);
}
} catch (IOException e) {
Toast.makeText(getContext(), R.string.error_read_image, Toast.LENGTH_LONG).show();
}
}
}
}
Regards.
if(result.resultCode == Activity.RESULT_OK && result.data != null {
binding?.ivImage?.setImageURI(result.data?.data)}
For anyone getting unsupported bitmap configuration : "Hardware" error or you need mutable bitmap for Canvas or reading pixels use this
ImageDecoder.decodeBitmap(
ImageDecoder.createSource(context.contentResolver, uri)
) { decoder, info, source ->
decoder.allocator = ImageDecoder.ALLOCATOR_SOFTWARE
decoder.isMutableRequired = true
}
This code works for my case:
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
try {
when (requestCode) {
//get the image with camera
SECTIONCAMERA -> {
val imageBitmap = data?.extras?.get("data") as Bitmap
ImageView_imagePerfil.setImageBitmap(imageBitmap)
}
//get the image in gallery
SECTIONGALLERY -> {
val imageUri = data?.data
ImageView_imagePerfil.setImageURI(imageUri) }
}
} catch (e: Exception){
e.printStackTrace()
}
}

Sharing an image in Android app using intent sends it as text file, not as a photo

I am trying to take a screenshot programatically and then share it to another app, using Intent. The problem that I face is that the photo is sent as a text file.
The button listener looks like this:
shareButton = rootView.findViewById(R.id.shareButton);
shareButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Bitmap screenshot = Screenshot.takeScreenshot(view.getRootView());
String filename = "eduMediaPlayerScreenshot";
String sharePath = Screenshot.storeScreenshot(screenshot, filename);
Intent intent = Screenshot.shareScreenshot(sharePath, view.getRootView());
startActivity(intent);
}
});
And the Screenshot class like this:
public class Screenshot {
public static Bitmap takeScreenshot(View view) {
view.setDrawingCacheEnabled(true);
view.buildDrawingCache(true);
Bitmap screenshot = Bitmap.createBitmap(view.getDrawingCache());
view.setDrawingCacheEnabled(false);
return screenshot;
}
public static String storeScreenshot(Bitmap screenshot, String filename) {
String path = Environment.getExternalStorageDirectory().toString() + "/" + filename;
OutputStream out = null;
File imageFile = new File(path);
try {
out = new FileOutputStream(imageFile);
screenshot.compress(Bitmap.CompressFormat.JPEG, 99, out);
out.flush();
} catch (FileNotFoundException e) {
Log.i("Exception:", "File not found.");
} catch (IOException e) {
Log.i("Exception:", "Cannot write to output file.");
} finally {
try {
if (out != null) {
out.close();
return imageFile.toString();
}
} catch (Exception e) {
Log.i("Exception:", "No output file to close.");
}
}
return null;
}
public static Intent shareScreenshot(String sharePath, View view) {
File file = new File(sharePath);
Uri uri = FileProvider.getUriForFile(view.getContext(),
BuildConfig.APPLICATION_ID + ".provider", file);
Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType("image/*");
intent.putExtra(Intent.EXTRA_STREAM, uri);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
return intent;
}
}
The shared photo looks like this:
not-the-best-photo
As Android will not recognize by default an image without extension, you should add .jpg to the end of the filename in the intent above. Without it, there should be a MIME type specified. More details on that here.
This is the code you might want to use.
shareButton = rootView.findViewById(R.id.shareButton);
shareButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Bitmap screenshot = Screenshot.takeScreenshot(view.getRootView());
String filename = "eduMediaPlayerScreenshot.jpg";
String sharePath = Screenshot.storeScreenshot(screenshot, filename);
Intent intent = Screenshot.shareScreenshot(sharePath, view.getRootView());
startActivity(intent);
}
});

Categories

Resources