Android Image FIle variable is becoming null after image capture - java

The app I am currently developing has the feature of being able to capture an image, record a video, browse and upload a media file from gallery. Problem is, the app works well enough in emulator. Without any errors (checked with android 9+ emulators). But the app behaves unexpectedly while testing on physical device (Android 10). Unexpected, because, video recording and playing works fine, as well as browsing and uploading media from gallery. What fails is image capture. The thing is the image file is actually getting created, But I am getting the image file variable photoFile value as null in onActivityResult() method.
Here is the implementation:
manifest.xml
<provider android:name=".utils.MyFileProvider"
android:authorities="com.app.mtccrm.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
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path
name="external"
path="." />
<external-files-path
name="external_files"
path="." />
<cache-path
name="cache"
path="." />
<external-cache-path
name="external_cache"
path="." />
<files-path
name="files"
path="." />
</paths>
method to create image_file
public static File createImageFile(Context mContext, String name) throws IOException {
String imageFileName = name.replaceAll(" ", "_");
File storageDir = mContext.getExternalFilesDir(Environment.DIRECTORY_PICTURES);
File image = new File(storageDir + File.separator + imageFileName + ".png");
return image;
}
method responsible for opening the camera
private void openCamera() {
if (ActivityCompat.checkSelfPermission(AddNewFileActivity.this, Manifest.permission.CAMERA)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, REQUEST_PERMISSION_CODE_IMAGE);
} else {
Intent captureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
try {
photoFile = CommonMethod.createImageFile(this, fileName);
} catch (IOException ioe) {
ioe.printStackTrace();
}
//Toast.makeText(this, ""+photoFile.getAbsolutePath(), Toast.LENGTH_SHORT).show();
if(photoFile != null ) {
Uri photoURI = FileProvider.getUriForFile(this, BuildConfig.APPLICATION_ID + ".fileprovider", photoFile);
captureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
captureIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivityForResult(captureIntent, TAKE_PICTURE_CODE);
}else {
Toast.makeText(this, "Something went wrong while trying to create a file. Please try again", Toast.LENGTH_SHORT).show();
}
}
}
onActivityResult()
Toast.makeText(this, ""+photoFile, Toast.LENGTH_SHORT).show();
if (requestCode == TAKE_PICTURE_CODE && resultCode == RESULT_OK) {
if(photoFile == null) {
if(data != null) {
parseGalleryFile(data);
}
}else {
setImage(Uri.parse(photoFile.getAbsolutePath()));
}
}
The first Toast in onActivityResult() gives me null value. So somehow the photoFile variable is becoming null, however I am at a loss about the cause. Since, from the same activity using the same methods, recording of videos and browsing gallery works just fine. I thought it might have something to do with the activity getting paused when the camera is called. So, I added this
#Override
public void onSaveInstanceState(#NonNull Bundle outState, #NonNull PersistableBundle outPersistentState) {
super.onSaveInstanceState(outState, outPersistentState);
Toast.makeText(this, ""+photoFile, Toast.LENGTH_SHORT).show();
if(photoFile != null) {
outState.putString("FileId", photoFile.getAbsolutePath());
}
}
#Override
protected void onRestoreInstanceState(#NonNull Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
Toast.makeText(this, ""+savedInstanceState.getString("FileId"), Toast.LENGTH_SHORT).show();
if(savedInstanceState.getString("FileId") != null) {
if (photoFile == null) {
photoFile = new File(savedInstanceState.getString("FileId"));
}
}
}
Unfortunately, save and restore state methods are not getting called, so that means the activity is not being paused. So what could be the cause for photofile becoming null? what exactly am I doing wrong?
Thanks for the help.

Related

Open PDF after download with DownloadManager and FileProvider on Android Q (10)

targetSdkVersion: 30
In our App we have a feature, where we download files (mostly pdf) to the public download folder and start an intent afterwards to open it. Our code works fine for android apps with api >= 28 and >= 30. Just our app on Android 10 (sdkVersion 29) will try to open the document and instantly closes the activity that tried to display the pdf. The logcat shows following error:
22-03-17 14:23:42.486 12161-15168/? E/DisplayData: openFd: java.io.FileNotFoundException: open failed: EACCES (Permission denied)
22-03-17 14:23:42.486 12161-15168/? E/PdfLoader: Can't load file (doesn't open) Display Data [PDF : download.pdf] +ContentOpenable, uri: content://com.example.fileprovider/Download/download.pdf
If I grant the files permission in the app settings, it will work flawlessly but if I understood the android documentation correctly this should not be necessary since Android 10. Especially because there are no problems on Android 11 and Android 12 devices that do not have this permission. On all Android versions the file will be downloaded correctly and the user could manually open it from the download section of his device.
This is the Android Manifest section for the fileprovider
<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/filepaths" />
</provider>
The filepaths XML file
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-files-path
name="external_files"
path="." />
<external-path
name="Download"
path="Download/"/>
<files-path
name="files"
path="." />
<external-cache-path
name="external_cache"
path="." />
</paths>
This is the code with the DownloadManager usage to download the files
public static long downloadFile(Context context, String url, String fileName) {
DownloadManager downloadManager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
try {
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
request.setTitle(fileName)
.setDescription(Localization.getStringClient("file_download_progress"))
.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName)
.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
return downloadManager.enqueue(request);
} catch (Exception e) {
e.printStackTrace();
Toast.makeText(context, Localization.getStringClient("error_downloading_asset"), Toast.LENGTH_SHORT).show();
}
return -1;
}
The broadcast receiver that listens on the download progress
public class DownloadBroadcastReceiver extends BroadcastReceiver {
private long downloadId = -2;
public void setDownloadId(long downloadId) {
this.downloadId = downloadId;
}
#Override
public void onReceive(Context context, Intent intent) {
long id = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
if (id == downloadId) {
DownloadManager downloadManager = (DownloadManager) context.getSystemService(DOWNLOAD_SERVICE);
DownloadManager.Query query = new DownloadManager.Query();
query.setFilterById(id);
Cursor c = downloadManager.query(query);
if (c != null) {
c.moveToFirst();
int columnIndex = c.getColumnIndex(DownloadManager.COLUMN_STATUS);
if (DownloadManager.STATUS_SUCCESSFUL == c.getInt(columnIndex)) {
String uriString = c.getString(c.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI));
String mediaType = c.getString(c.getColumnIndex(DownloadManager.COLUMN_MEDIA_TYPE));
Uri fileUri = Uri.parse(uriString);
DownloadUtils.openFile(context, fileUri, mediaType);
} else if (DownloadManager.STATUS_FAILED == c.getInt(columnIndex)) {
Toast.makeText(context, Localization.getStringClient("error_downloading_asset"), Toast.LENGTH_SHORT).show();
}
c.close();
}
}
}
}
The code to open the file. Only on Android 10 the app closes the activity instantly again.
public static void openFile(Context context, Uri fileUri, String mediaType) {
File file = new File(fileUri.getPath());
Uri uri = FileProvider.getUriForFile(
context,
context.getApplicationContext().getPackageName() + ".fileprovider",
file
);
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(uri, mediaType);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
try {
context.startActivity(intent);
} catch (ActivityNotFoundException e) {
e.printStackTrace();
Toast.makeText(context, Localization.getStringClient("open_file_no_activity"), Toast.LENGTH_SHORT).show();
}
}
And a download call in the looks like this
#Override
public void startPDFDownload(String pdfDownloadUrl, String fileName) {
long downloadId = DownloadUtils.downloadFile(requireContext(), pdfDownloadUrl, fileName);
if (downloadId > -1) {
DownloadBroadcastReceiver receiver = new DownloadBroadcastReceiver();
receiver.setDownloadId(downloadId);
requireContext().registerReceiver(receiver, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
}
}
I think I have a false understanding of how file handling works in Android 10 but I do not know where I have to adjust the code or configuration. Help is really appreciated. Currently as a workaround we ask for the permission for WRITE_EXTERNAL_STORAGE to open downloaded files on Android 10. But I would prefer to do it the right way.
Solution:
I adjusted the BroadcastReceiver to the following code. I removed the LOCAL_URI from the cursor and used the URI from the DownloadManager method.
public class DownloadBroadcastReceiver extends BroadcastReceiver {
private long downloadId = -2;
public void setDownloadId(long downloadId) {
this.downloadId = downloadId;
}
#Override
public void onReceive(Context context, Intent intent) {
long id = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
if (id == downloadId) {
DownloadManager downloadManager = (DownloadManager) context.getSystemService(DOWNLOAD_SERVICE);
DownloadManager.Query query = new DownloadManager.Query();
query.setFilterById(id);
Cursor c = downloadManager.query(query);
if (c != null) {
c.moveToFirst();
int columnIndex = c.getColumnIndex(DownloadManager.COLUMN_STATUS);
if (DownloadManager.STATUS_SUCCESSFUL == c.getInt(columnIndex)) {
String mediaType = c.getString(c.getColumnIndex(DownloadManager.COLUMN_MEDIA_TYPE));
DownloadUtils.openFile(context, downloadManager.getUriForDownloadedFile(id), mediaType);
} else if (DownloadManager.STATUS_FAILED == c.getInt(columnIndex)) {
Toast.makeText(context, Localization.getStringClient("error_downloading_asset"), Toast.LENGTH_SHORT).show();
}
c.close();
}
}
}
}
And I adjusted the method to open the file with the uri to following code. I removed the FileProvider code and used the uri from the DownloadManager.
public static void openFile(Context context, Uri fileUri, String mediaType) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(fileUri, mediaType);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
try {
context.startActivity(intent);
} catch (ActivityNotFoundException e) {
e.printStackTrace();
Toast.makeText(context, Localization.getStringClient("open_file_no_activity"), Toast.LENGTH_SHORT).show();
}
}
The code of the other methods remains the same.
You should not use FileProvider to obtain an uri for your file.
You can get an uri from DownloadManager and use it to serve your file.
Code would be the same for all Android versions.
Not a single permission needed.

Save image using MediaStore, saved with wrong name and extension

I'm trying to use MediaStore API to save an image but it always save the file with this name
1637955326427.jpg and the size of the file is 0 B.
this is the code that i use:
OutputStream fos;
ContentResolver resolver = getBaseContext().getContentResolver();
ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.Images.Media.DISPLAY_NAME, "fileName.jpg");
contentValues.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
Uri imageUri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);
try {
fos = resolver.openOutputStream(Objects.requireNonNull(imageUri));
Bitmap bitmap = MediaStore.Images.Media.getBitmap(getContentResolver(),list.get(0).getDocfile().getUri());
if(bitmap!=null){
bitmap.compress(Bitmap.CompressFormat.JPEG,100,fos);
fos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
list.get(0).getDocFile() return a DocumentFile also i tested this on API 28
Thanks in advance
NOTICE this image already exist on the External Storage, just need to copy it.
EDIT: I delete this code fos = resolver.openOutputStream(imageUri); was duplucate. I do realy apologize. the file work fine but steal wrong name.
First lets take a look at getting write permissions for an existing image. Please note that the photopath does not necessarily exist yet but should point to the location you want your file to be saved.
if (!requestPhotoWritePermission(this, getImageUriFromFS(this, new File(photoPath)))) {
return;
} else {
takePicture();
}
This will form an overwrite request with the default camera
public static Uri getImageUriFromFS(Context c, File file) {
long id = getFilePathToImageID(file, c);
Uri fromUri = ContentUris.withAppendedId( MediaStore.Images.Media.EXTERNAL_CONTENT_URI,id);
return fromUri;
}
public static long getFilePathToImageID(File imagePath, Context context)
{
Uri mainUri;
Cursor cursor1 = context.getContentResolver().query(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
new String[]{MediaStore.Images.Media._ID},
MediaStore.Images.Media.DATA + "=? ",
new String[]{imagePath.getAbsolutePath()}, null);
long id = 0;
if (cursor1 != null && cursor1.moveToFirst()) {
id = cursor1.getLong(cursor1.getColumnIndex(MediaStore.MediaColumns._ID));
cursor1.close();
}
return id;
}
public static boolean requestPhotoWritePermission(Activity activity, Uri fromUri) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
boolean hasPermission = true;
if (activity.checkUriPermission(fromUri, Binder.getCallingPid(), Binder.getCallingUid(),
Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION) != PackageManager.PERMISSION_GRANTED) {
hasPermission = false;
}
List<Uri> uriList = new ArrayList<>();
uriList.add(fromUri);
if (!hasPermission) {
PendingIntent pi = MediaStore.createWriteRequest(activity.getContentResolver(), uriList);
try {
activity.startIntentSenderForResult(pi.getIntentSender(), REQUEST_OVERWRITE_PERMISSION_CODE, null, 0, 0, 0);
} catch (IntentSender.SendIntentException e) {
e.printStackTrace();
}
return false;
}
return true;
}
return true;
}
This will persist the permission to overwrite or create a new picture via the camera intent
Then in your activity:
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == 53) {
if (resultCode == RESULT_OK) {
//you'll want to take your picture here and possibly store the uri to your photo for later retrieval
takePicture();
}
}
This will take a picture if the permission request is accepted
To Take a picture using the default intent with a provider:
Declare your provider in the manifest
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.provider"
android:enabled="true"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/file_paths">
</meta-data>
</provider>
1.5: xml File paths file:
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-files-path
name="internal_images"
path="files/Pictures" />
<external-files-path
name="internal_images_alternate"
path="Pictures" />
<external-path
name="external"
path="Documents" />
<external-files-path
name="external_files"
path="Documents" />
<cache-path
name="cache"
path="Documents" />
<external-cache-path
name="external_cache"
path="Documents" />
<files-path
name="files"
path="Documents" />
</paths>
The above will give you permission to save to the documents folder. Replace as required.
Utilize the provider to take the picture
public static File dispatchTakePictureIntent(Activity activity, String photoPath) {
Intent takePictureIntent = getCameraIntentWithUpdatedPackages(activity);
Uri photoURI = Uri.fromFile(new File(photoPath));
Uri photoUri = FileProvider.getUriForFile(activity.getApplicationContext(), activity.getApplicationContext().getPackageName() + ".provider", new File(photoPath));
activity.grantUriPermission(photoURI.getAuthority(), photoUri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri);
//disable strict mode policies
StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
StrictMode.setVmPolicy(builder.build());
activity.startActivityForResult(takePictureIntent, REQUEST_TAKE_PHOTO);
return new File(photoPath);
return null;
}
EDIT:
I forgot something
public static Intent getCameraIntentWithUpdatedPackages(Context context) {
List<ResolveInfo> resolveInfo = new ArrayList<>();
final Intent capturePhoto = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
PackageManager pm = context.getPackageManager();
resolveInfo = pm.queryIntentActivities(capturePhoto, 0);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
// For Android 11 we need to add specific camera apps
// due them are not added during ACTION_IMAGE_CAPTURE scanning...
// resolveInfo.addAll(getCameraSpecificAppsInfo(context));
}
capturePhoto.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
return capturePhoto;
}
EDIT: Update by the name (in onActivityResult after requesting an overwrite permission)
Uri fromUri = MediaUtils.getImageUriFromFS(this, new File(from));
ContentResolver contentResolver = getContentResolver();
ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.Files.FileColumns.IS_PENDING, 1);
contentResolver.update(fromUri, contentValues, null, null);
contentValues.clear();
contentValues.put(MediaStore.Files.FileColumns.DISPLAY_NAME, new File(to).getName());
contentValues.put(MediaStore.Files.FileColumns.IS_PENDING, 0);
contentResolver.update(fromUri, contentValues, null, null);
I delete this code fos = resolver.openOutputStream(imageUri); was duplicate. the image work fine but steal wrong name.
About the Wrong name and extension, i think that MediaStore doesn't work fine with Android 9 and lower, because i tested the same code on Android 11 and it's working fine

Android Application Crashes while Capturing Full Size Picture

I am developing an Android Application that takes a picture and saves a full-size picture onto the device. I have mostly taken help from developers android site. When I run my code and try to take the picture the application crashes instantly. The code I am using is as follows:
static final int REQUEST_IMAGE_CAPTURE = 1;
static final int REQUEST_TAKE_PHOTO = 1;
private void dispatchTakePictureIntent() {
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
// Ensure that there's a camera activity to handle the intent
if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
// Create the File where the photo should go
File photoFile = null;
try {
photoFile = createImageFile();
} catch (IOException ex) {
// Error occurred while creating the File
System.out.println("Error in Dispatch Take Picture Intent");
}
// Continue only if the File was successfully created
if (photoFile != null) {
Uri photoURI = FileProvider.getUriForFile(this,
"com.example.android.fileprovider",
photoFile);
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
startActivityForResult(takePictureIntent, REQUEST_TAKE_PHOTO);
}
}
}
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK) {
//Bundle extras = data.getExtras();
//Bitmap imageBitmap = (Bitmap) extras.get("data");
//imageView.setImageBitmap(imageBitmap);
Toast.makeText(getApplicationContext(),"Working",Toast.LENGTH_LONG).show();
}
}
String currentPhotoPath;
private File createImageFile() throws IOException {
// Create an image file name
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String imageFileName = "JPEG_" + timeStamp + "_";
File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
File image = File.createTempFile(
imageFileName, /* prefix */
".jpg", /* suffix */
storageDir /* directory */
);
// Save a file: path for use with ACTION_VIEW intents
currentPhotoPath = image.getAbsolutePath();
return image;
}
private View.OnClickListener onCamClick() {
return new View.OnClickListener() {
#Override
public void onClick(View v) {
System.out.println("Starting Camera Activity");
//dispatchTakePictureIntent();
//Toast.makeText(getApplicationContext(),"Working",Toast.LENGTH_LONG).show();
dispatchTakePictureIntent();
}
};
}
When I run my code I get the following error:
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.zaeem.tia, PID: 15468
java.lang.IllegalArgumentException: Failed to find configured root that contains /storage/emulated/0/Android/data/com.zaeem.tia/files/Pictures/JPEG_20200301_003227_1043176843378455577.jpg
at androidx.core.content.FileProvider$SimplePathStrategy.getUriForFile(FileProvider.java:739)
at androidx.core.content.FileProvider.getUriForFile(FileProvider.java:418)
at com.zaeem.tia.app.activities.MainActivity.dispatchTakePictureIntent(MainActivity.java:121)
at com.zaeem.tia.app.activities.MainActivity.access$000(MainActivity.java:40)
at com.zaeem.tia.app.activities.MainActivity$1.onClick(MainActivity.java:164)
at android.view.View.performClick(View.java:7339)
at android.widget.TextView.performClick(TextView.java:14221)
at android.view.View.performClickInternal(View.java:7305)
at android.view.View.access$3200(View.java:846)
at android.view.View$PerformClick.run(View.java:27787)
at android.os.Handler.handleCallback(Handler.java:873)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7076)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:965)
filepath.xml file:
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-files-path name="my_images" path="Android/data/com.zaeem.tia.app.activities/files/Pictures" />
</paths>
Please advise regarding this matter.
Uri photoURI = FileProvider.getUriForFile(this,
"com.example.android.fileprovider",
photoFile);
Here, you are saying that the authority string for your FileProvider is com.example.android.fileprovider. Based on the error, that is not what you have on the <provider> element in the manifest.
add this in your manifest.xml (after ):
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="android.getqardio.com.gmslocationtest"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/provider_paths"/>
</provider>
Create provider_paths on Resources/xml, and add this content:
<?xml version="1.0" encoding="utf-8"?>
<paths>
<files-path
name="share"
path="external_files"/>
</paths>
Activity:
File imagePath = new File(getFilesDir(), "external_files");
imagePath.mkdir();
File imageFile = new File(imagePath.getPath(), "test.jpg");
// Write data in your file
Uri uri = FileProvider.getUriForFile(this, getPackageName(), imageFile);
Intent intent = ShareCompat.IntentBuilder.from(this)
.setStream(uri) // uri from FileProvider
.setType("text/html")
.getIntent()
.setAction(Intent.ACTION_VIEW) //Change if needed
.setDataAndType(uri, "image/*")
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(intent);

Taking the full-size image from a camera app

My App has an option to take photos from the camera. I'm using phone's default camera.
The problem is when I take the picture, in my onActivityResult, I get null from intent(data). so it shows the message Picture wasn't taken!".
Code to call Intent:
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// create Intent to take a picture and return control to the calling application
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
// Create a File reference to access to future access
photoFile = getPhotoFileUri(photoFileName);
// wrap File object into a content provider
// required for API >= 24
// See https://guides.codepath.com/android/Sharing-Content-with-Intents#sharing-files-with-api-24-or-higher
Uri fileProvider = FileProvider.getUriForFile(MainActivity.this, "com.example.android.fileprovider", photoFile);
intent.putExtra(MediaStore.EXTRA_OUTPUT, fileProvider);
// If you call startActivityForResult() using an intent that no app can handle, your app will crash.
// So as long as the result is not null, it's safe to use the intent.
if (intent.resolveActivity(getPackageManager()) != null) {
// Start the image capture intent to take photo
startActivityForResult(intent, REQUEST_IMAGE_CAPTURE);
}
}
});
}
onActivityResult:
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK && data != null) {
// by this point we have the camera photo on disk
Bitmap takenImage = BitmapFactory.decodeFile(String.valueOf(photoFile));
// RESIZE BITMAP, see section below
// Load the taken image into a preview
img.setImageBitmap(takenImage);
} else { // Result was a failure
Toast.makeText(this, "Picture wasn't taken!", Toast.LENGTH_SHORT).show();
}
}
Make a file for the photo:
public File getPhotoFileUri(String fileName) {
// Only continue if the SD Card is mounted
if (isExternalStorageAvailable()) {
// Get safe storage directory for photos
// Use `getExternalFilesDir` on Context to access package-specific directories.
// This way, we don't need to request external read/write runtime permissions.
File mediaStorageDir = new File(
getExternalFilesDir(Environment.DIRECTORY_PICTURES), APP_TAG);
// Create the storage directory if it does not exist
if (!mediaStorageDir.exists() && !mediaStorageDir.mkdirs()){
Log.d(APP_TAG, "failed to create directory");
}
// Return the file target for the photo based on filename
File file = new File(mediaStorageDir.getPath() + File.separator + fileName);
return file;
}
return null;
}
provider tag in AndroidManifest.xml
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.example.android.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="my_images" path="Android/data/com.example.houman.myapplication/files/Pictures" />
</paths>
After "Taking Photos Simply | Android Developers"
didn't help me so much, I tried Accessing the Camera and Stored Media | CodePath
but now I have this problem.
I get null from intent(data)
You are not using data, and in the EXTRA_OUTPUT scenario there is no need for the camera app to supply an Intent in the result. So, get rid of that check, by replacing:
if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK && data != null) {
with:
if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK) {

Show apps pictures in Android Gallery with Android Nougat

How do you grant the proper permissions and set the correct path in order to display an apps photos in the native Android Gallery app with FileProvider in Android Nougat?
Below is the current code. It will save photos locally, but the photos aren't displayed in the Android Gallery app.
CameraActivity.java
public class CameraPreviewActivity extends AppCompatActivity
implements CameraPreviewFragment.Callback {
...
#Override
protected void onCreate(Bundle savedInstanceState) {
...
showCamera();
}
public void showCameraPreviewFile() {
_photoFileUri = generateTimestampPhotoUri();
if (_photoFileUri != null) {
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, _photoFileUri);
startActivityForResult(intent, PHOTO_INTENT_WITH_FILENAME);
}
}
File getPhotoDirectory() {
File outputDir = null;
String externalStorageState = Environment.getExternalStorageState();
if (externalStorageState.equals(Environment.MEDIA_MOUNTED)) {
File picturesDir =
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
outputDir = new File(picturesDir, getString(R.string.app_name));
if (!outputDir.exists()) {
if (!outputDir.mkdirs()) {
Log.e(LOG_TAG, "outputDir.mkdirs: error");
outputDir = null;
}
}
}
return outputDir;
}
Uri generateTimestampPhotoUri() {
Uri photoUri = null;
File outputDir = getPhotoDirectory();
File photoFile = null;
if (outputDir != null) {
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String photoFileName = "IMG_" + timeStamp + ".jpg";
photoFile = new File(outputDir, photoFileName);
}
if (photoFile != null) {
photoUri = FileProvider.getUriForFile(
this,
this.getApplicationContext().getPackageName() + ".provider",
photoFile
);
}
return photoUri;
}
AndroidManifest.xml
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/provider_paths" />
xml/provider_paths.xml
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path name="MyApp" path="."/>
</paths>
There is no single "native Android Gallery app". There are ~2 billion Android devices, spread across thousands of device models. There are hundreds of pre-installed "gallery" apps on those devices, as device manufacturers routinely ship their own. There are also "gallery" apps available for users to install from app markets.
Their behaviors will vary. Many will use MediaStore to find the photos to view. You are not doing anything to arrange to have your image be indexed by the MediaStore. Use MediaScannerConnection and its scanFile() method, in onActivityResult(), to have the MediaStore update its catalog to include your image.

Categories