Take picture with camera in android 10 - android

With all of the changes in android 10 that are related to scoped storage, how can we open the camera. I saw some tutorial that suggest using a file provider, but I don't really quite get it.
Can you provide a snippet of code to launch the camera intent, and receive the taken image ?
Edit :
In this article : https://medium.com/#arkapp/accessing-images-on-android-10-scoped-storage-bbe65160c3f4
He provided this code :
fun takePicture(context: Activity, imageName: String) {try {
val capturedImgFile = File(
context.getExternalFilesDir(Environment.DIRECTORY_PICTURES),
imageName)
captureImgUri = FileProvider.getUriForFile(
context,
context.applicationContext.packageName + ".my.package.name.provider",
capturedImgFile)val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE).also {
it.putExtra(MediaStore.EXTRA_OUTPUT, captureImgUri)
it.putExtra("return-data", true)
it.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
context.startActivityForResult(it, REQUEST_CODE_TAKE_PICTURE)
}
} catch (e: ActivityNotFoundException) {e.printStackTrace()}
}#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode != RESULT_OK) {return}
if (requestCode == REQUEST_CODE_TAKE_PICTURE) {
/*We cannot access the image directly so we again create a new File at different location and use it for futher processing.*/
Bitmap capturedBitmap = getBitmap(this, captureImgUri)
/*We are storing the above bitmap at different location where we can access it.*/ val capturedImgFile = File(
getExternalFilesDir(Environment.DIRECTORY_PICTURES),
getTimestamp() + "_capturedImg.jpg"); convertBitmaptoFile(capturedImgFile, capturedBitmap)/*We have to again create a new file where we will save the processed image.*/ val croppedImgFile = File(
getExternalFilesDir(Environment.DIRECTORY_PICTURES),
getTimestamp() + "_croppedImg.jpg"); startCrop(
this,
Uri.fromFile(capturedImgFile),
Uri.fromFile(croppedImgFile))}
super.onActivityResult(requestCode, resultCode, data)
}
I have two questions:
What does this line do :
it.putExtra("return-data", true)
in OnActivityResulty, why didn't he just use the uri directly, he first created a file and then parsed the file into an uri, what is this for ? how does it have to do android 10 scoped storage ?

What does this line do : it.putExtra("return-data", true)
Nothing, usually. It's possible that there are some camera apps that will do something when receiving this undocumented and (generally) unsupported Intent extra.
in OnActivityResulty, why didn't he just use the uri directly
He does, in earlier steps. By the end, he wants to use uCrop to allow the user to crop the image. Perhaps he wants to hold onto both the original and the cropped photo.

Related

Why doesn't the ACTION_IMAGE_CAPTURE Intent return the URI for the saved image?

I'm trying to follow the logic in the Android code here, which explains how to accomplish a "Get Image from Camera" intent. I'm hoping one of you smart people of SO can help me understand WHY Google developed the intent this way. First the function that kicks off the Intent:
val REQUEST_TAKE_PHOTO = 1
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_TAKE_PHOTO)
}
}
}
}
Notice that this code refers to another function, one that creates a file and filename:
var currentPhotoPath: String
#Throws(IOException::class)
private fun createImageFile(): File {
// Create an image file name
val timeStamp: String = SimpleDateFormat("yyyyMMdd_HHmmss").format(Date())
val storageDir: File = getExternalFilesDir(Environment.DIRECTORY_PICTURES)
return File.createTempFile(
"JPEG_${timeStamp}_", /* prefix */
".jpg", /* suffix */
storageDir /* directory */
).apply {
// Save a file: path for use with ACTION_VIEW intents
currentPhotoPath = absolutePath
}
}
Finally if you have an intent and have done Android development before you know you need a OnActivityResult function, so here's one:
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) {
if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK) {
val imageBitmap = data.extras.get("data") as Bitmap
imageView.setImageBitmap(imageBitmap)
}
}
So if you run this code it will fail which is the genesis for my question and I'm hoping someone, perhaps someone on the Android dev team can explain to me why this popular intent returns null data.
The apparent logic flow for this is the following:
Kick off the ACTION_IMAGE_CAPTURE intent
Create a file to save the image into
Save that file URI to your own variable, because you'll need it later
Add the file name as an extra to the intent
Start the intent
Intent callback OnActivityResult returns but has NULL data passed back with it.
Remember that URI you saved off earlier, just use that as your return data.
It just seems strange that the callback data is null and I found it really confusing. Is there another intent I'm should be using? Why isn't the intent returning me some useful data, like the URI for the file it just created?
If you specified MediaStore.EXTRA_OUTPUT, the image taken will be written to that path, and no data will given to onActivityResult.
You can read the image from what you specified.
Also pls check your permissions.

Android capture and use image not working

I've written code in Android to capture an image and then use that image as follows:
private static Intent i;
final static int cons = 0;
private static final int CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE = 100;
public void takePicture()
{
i= new Intent(MediaStore.ACTION_IMAGE_CAPTURE); //Open camera
startActivityForResult(i, cons);
}
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE) {
if (resultCode == RESULT_OK) {
Log.d("Testing if condition", "Test"); //This code does not execute
Bundle ext = data.getExtras();
Log.d("Upload image", ext.toString());
sendForUpload(data); // function to upload file to server
} else if (resultCode == RESULT_CANCELED) {
// User cancelled the image capture
} else {
// Image capture failed, advise user
}
}
}
This code lets me take a photo and it saves to the SD card. However, I'm not sure if the file is sent to the sendForUpload function where I've handled getting the path and uploading the file. In fact nothing inside the if (resultCode == RESULT_OK) block works. How do I use the image captured from this activity?
Well, you have a few problems.
First, you are calling startActivityForResult(i, cons);. cons is 0. You are trying to compare 0 with CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE. Since CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE is 100, that will not work.
Second, you are calling data.getExtras().toString() (split over two Java statements). I would expect this to return a value like android.os.Bundle#34334143. If that is what you want to upload, fine. I am guessing that you want to upload the image. Since you did not include EXTRA_OUTPUT in your ACTION_IMAGE_CAPTURE Intent, you get a Bitmap of the thumbnail image taken by the camera via data.getParcelableExtra("data").
Third, you are making inappropriate assumptions:
This code lets me take a photo and it saves to the SD card.
There is no requirement for the camera app to save the image anywhere, let alone to removable storage. In fact, I would argue that this is a bug in your camera app. That is not surprising, as ACTION_IMAGE_CAPTURE implementations have bugs.
where I've handled getting the path and uploading the file
There is no path. You did not include EXTRA_OUTPUT in your ACTION_IMAGE_CAPTURE Intent, and so all you get back is the thumbnail Bitmap.
I'm using this to put the image in a ImageView:
Uri uri = data.getData();
Bitmap bitmap = MediaStore.Images.Media.getBitmap(getContentResolver(), uri);
You can use bitmap or uri objects for upload or show your picture..
Hope it helps you.

How to change the directory of SquareCamera library?

I am using SquareCamera library (https://github.com/boxme/SquareCamera) for taking square picture.The problem I am facing is that SquareCamera is creating its own folder where taken pics are getting stored. I want these pics to store in my own folder. I don't know how to achieve that. I am very new to android. Below is the code where instead of default camera I am calling its own class.
public void onLaunchCamera(View view) {
// create Intent to take a picture and return control to the calling application
Intent intent = new Intent(this,CameraActivity.class);
// Start the image capture intent to take photo
startActivityForResult(intent, CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE);
And this is the onActivityResult method
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE) {
if (resultCode == RESULT_OK) {
Uri takenPhotoUri = data.getData();
Bitmap takenImage = BitmapFactory.decodeFile(takenPhotoUri.getPath());
imageView.setImageBitmap(takenImage);
I thought about saving this bitmap into my own folder but I couldn't think how to delete the created directory of SquareCamera.
So I found the solution. I added the library as a module in my app. Referring (https://www.youtube.com/watch?v=1MyBO9z7ojk). And there I changed the source code a little bit and now it's working perfect.
I'm a bit long in the tooth at Android and am not 100% with the new Uri methods of file access enforced since KitKat. For conventional file access you can get a private writeable file using.
private static final File OUTPUT_DIR = Environment.getExternalStorageDirectory();
FileOutputStream fos;
void yourMethodBeginsHere() {
String outputPath = new File(OUTPUT_DIR, "test.png").toString();
try {
fos = new FileOutputStream(outputPath, false);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
//Work with file
}
If you need a truly external file path please refer to the excellent answer already existing at https://stackoverflow.com/a/26765884/5353361 which deals fully with the new Uri based system of permissions and the integrated file explorer.

Android Video Intent not saving video to desired location

I have this code:
(This is code that call the video intent)
Intent videoIntent = new Intent(android.provider.MediaStore.ACTION_VIDEO_CAPTURE);
videoIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(getOutputMediaFile()));
videoIntent.putExtra(MediaStore.EXTRA_DURATION_LIMIT , 5);
videoIntent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 0);
startActivityForResult(videoIntent, VIDEO_REQUEST);
(Rest of code)
private static final int VIDEO_REQUEST = 2888;
public static File getOutputMediaFile(){
File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES), "FolderApp");
if (! mediaStorageDir.exists()){
if (! mediaStorageDir.mkdirs()){
//Cant create folder!!
return null;
}
}
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
File mediaFile mediaFile = new File(mediaStorageDir.getPath() + "_VID_" + timeStamp + ".3gp");
} else {
return null;
}
return mediaFile;
}
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == VIDEO_REQUEST && resultCode == Activity.RESULT_OK) {
//OK!! but not create a video file returned in getOutputMediaFile()
}
}
The video is not saving it to desired location similar thing I did try with Image capture and it worked.
The video is not saving it to desired location
There is no requirement that a camera app follow the rules of ACTION_IMAGE_CAPTURE or ACTION_VIDEO_CAPTURE. Some will honor a specific requested output location. Some will not, and will return where they chose to store the image/video in the Uri contained in the result Intent delivered on onActivityResult(). Some may be completely broken and not tell you anything about where the image/video was stored, even if there was one stored.
I did try with Image capture and it worked
My guess is that you did not try it on the hundreds of camera applications that come pre-installed on devices or are available on the Play Store, Amazon AppStore for Android, BlackBerry World, the Nokia X Store, the Yandex store, etc.
If you wish to rely upon third-party camera apps, your best course of action is:
Specify a desired output location
In onActivityResult(), see if the output was written where you requested, and if so, use it
Otherwise, in onActivityResult(), obtain the Uri from the Intent, and if it is not null, use it
Otherwise, tell the user to download a better camera app, perhaps linking them to one that you know works well with your app
Your code looks ok. Maybe you can try:
Environment.DIRECTORY_MOVIES
instead of
Environment.DIRECTORY_PICTURES
for your video location... but it's just a guess
The way that always works for me:
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if(data == null) {
//CANNOT GET URI
return;
}
File source = new File(data.getData().getPath());
File destination = new File(PATH_TO_YOUR_DESIRE_LOCATION,FILENAME);
//MOVE FILE
if(source.renameTo(destination)) {
//success
} else {
//something's wrong
}
}
Also if you set MediaStore.EXTRA_OUTPUT parameter to android.provider.MediaStore.ACTION_VIDEO_CAPTURE intent, then
getData() inside onActivityResult will always return null

How to open file save dialog in android?

I have a web service which give me a byte[] array according to image id . I want to convert these byte[] to file and store a file on android where user want like save file dialog box with file same format exactly it has.
Since this is the top result in google when you search for that topic and it confused me a lot when I researched it, I thought I add an update to this question.
Since Android 19 there IS a built in save dialog. You dont event need any permission to do it (not even WRITE_EXTERNAL_STORAGE).
The way it works is pretty simple:
//send an ACTION_CREATE_DOCUMENT intent to the system. It will open a dialog where the user can choose a location and a filename
Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("YOUR FILETYPE"); //not needed, but maybe usefull
intent.putExtra(Intent.EXTRA_TITLE, "YOUR FILENAME"); //not needed, but maybe usefull
startActivityForResult(intent, SOME_INTEGER);
...
//after the user has selected a location you get an uri where you can write your data to:
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if(requestCode == SOME_INTEGER && resultCode == Activity.RESULT_OK) {
Uri uri = data.getData();
//just as an example, I am writing a String to the Uri I received from the user:
try {
OutputStream output = getContext().getContentResolver().openOutputStream(uri);
output.write(SOME_CONTENT.getBytes());
output.flush();
output.close();
}
catch(IOException e) {
Toast.makeText(context, "Error", Toast.LENGTH_SHORT).show();
}
}
}
More here:
https://developer.android.com/guide/topics/providers/document-provider
The Android SDK does not provide its own file dialog, therefore you have to build your own.
You cant create a save file dialog but you can save files from ur application to android sd card with the help of below links
http://android-er.blogspot.com/2010/07/save-file-to-sd-card.html
http://www.blackmoonit.com/android/filebrowser/intents#intent.pick_file.new
First, you should create a dialog intent for saving the file, After selection by the user, you can write on that directory and specified the file without any read/write permissions. ( Since Android 19 )
Source:https://developer.android.com/training/data-storage/shared/documents-files#create-file
// Request code for creating a PDF document.
private final int SAVE_DOCUMENT_REQUEST_CODE = 0x445;
private File targetFile;
private void createFile() {
Uri reportFileUri = FileProvider.getUriForFile(getApplicationContext(), getPackageName() + ".provider", targetFile);
Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("application/pdf");
intent.putExtra(Intent.EXTRA_TITLE, targetFile.getName());
// Optionally, specify a URI for the directory that should be opened in
// the system file picker when your app creates the document.
intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, pickerInitialUri);
startActivityForResult(intent, SAVE_DOCUMENT_REQUEST_CODE );
}
#Override
protected void onActivityResult(int requestCode, int resultCode, #Nullable
Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == SAVE_DOCUMENT_REQUEST_CODE && resultCode == RESULT_OK){
Uri uri = data.getData();
saveFile(uri);
}
}
private void saveFile(Uri uri) {
try {
OutputStream output = getContentResolver().openOutputStream(uri);
FileInputStream fileInputStream = new FileInputStream(targetFile);
byte[] bytes = new byte[(int) targetFile.length()];
fileInputStream.read(bytes, 0, bytes.length);
output.write(bytes);
output.flush();
output.close();
Log.i(TAG, "done");
} catch (IOException e) {
Log.e(TAG, "onActivityResult: ", e);
}
}
#JodliDev already provided the accepted answer, however, startActivityForResult is now deprecated, so I want to provide my solution here using registerForActivityResult(ActivityResultContracts.CreateDocument())
First register a ActivityResultLauncher where you define what should happen with the result. We'll get the uri back that we can use for our OutpuStream. But make sure to initialize it at the beginning, otherwise you will get:
Fragments must call registerForActivityResult() before they are created (i.e. initialization, onAttach(), or onCreate()).
private var ics: String? = null
private val getFileUriForSavingICS = registerForActivityResult(ActivityResultContracts.CreateDocument()) { uri ->
if(ics.isNullOrEmpty())
return#registerForActivityResult
try {
val output: OutputStream? =
context?.contentResolver?.openOutputStream(uri)
output?.write(ics?.toByteArray())
output?.flush()
output?.close()
} catch (e: IOException) {
Toast.makeText(context, "Error", Toast.LENGTH_SHORT).show()
}
}
Then just call your ActivityResultLauncher with .launch(...) wherever it is needed.
getFileUriForSavingICS.launch("filename.txt")
And that's about it ;-)
You can also have a closer look at ActivityResultContracts.CreateDocument(). This method provides the document saving dialog, but there are other helpful functions inside (like for starting a camera intent). Check out:
https://developer.android.com/reference/androidx/activity/result/contract/ActivityResultContracts
for the possible ActivityResultContracts
Or https://developer.android.com/training/basics/intents/result for some more training material and also some information how a custom contract could be created!

Categories

Resources