Unable to save image file in android oreo update. How to do it?
There is, in fact, a slight, subtle change in Permissions for apps running on and targeting API 26.
Previously, apps were automatically granted all permissions in a given group if at least one permission in that group had been granted by the user. This means that an app that had been granted the READ_EXTERNAL_STORAGE
would've had WRITE_EXTERNAL_STORAGE
immediately granted to it as well, regardless of whether WRITE_EXTERNAL_STORAGE
had been explicitly requested.
As of Oreo, for apps targeting API 26+, this has been corrected, and only those permissions that are explicitly requested will be granted. If the user has already granted a permission in the same group, then there will be no prompt for the new permission, but it still must be requested.
That was the problem, in this case. When the READ_EXTERNAL_STORAGE
permission was granted to your app on Nougat or below, you were automatically getting WRITE_EXTERNAL_STORAGE
, too, without having to request that one specifically. When you try the same file save procedure in Oreo, you aren't getting WRITE_EXTERNAL_STORAGE
automatically, so the write ultimately fails.
Simply add a specific request for WRITE_EXTERNAL_STORAGE
. If the user has already granted READ_EXTERNAL_STORAGE
, they won't be bothered with another prompt. Alternatively, you could request solely WRITE_EXTERNAL_STORAGE
from the start, which implicitly includes READ_EXTERNAL_STORAGE
, and would save you the need for two separate requests.
File read and write in Android 8.1 and above
For 8.0 and above only those permissions that are explicitly requested will be granted.
Did you add Read and Write request permission explicitly at runtime?
Unable to save image file in android oreo update. How to do it?
URI Location to save a photo
I created PictureUtils class for capture image from camera and Gallery. Here is below code
PictureUtils.java
public class PictureUtils {
private static String fileName;
public Activity activity;
public PictureUtils(Activity activity){
this.activity = activity;
}
public Uri openCameraIntent(int requestCode) {
Uri file = null;
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
file = FileProvider.getUriForFile(activity, BuildConfig.APPLICATION_ID + ".provider", getOutputMediaFile());
} else {
file = Uri.fromFile(getOutputMediaFile());
}
intent.putExtra(MediaStore.EXTRA_OUTPUT, file);
activity.startActivityForResult(intent, requestCode);
return file;
}
public void openGalleryIntent(int requestCode) {
Intent pickPhoto = new Intent(Intent.ACTION_PICK,
MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
pickPhoto.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
activity.startActivityForResult(pickPhoto, requestCode);
}
private static File getOutputMediaFile() {
File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) + "/RVRB");
if (!mediaStorageDir.exists()) {
if (!mediaStorageDir.mkdirs()) {
return null;
}
}
String timeStamp = String.valueOf(System.currentTimeMillis());
fileName = timeStamp + ".jpg";
return new File(mediaStorageDir.getAbsolutePath() + File.separator + fileName);
}
public ImagesData resultFromCamera(Intent data) {
File imageFile = null;
float rotationDegree = 0;
String exifOrientation;
ExifInterface exif = null;
Bitmap rotatedBitmap = null;
File storageDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) + "/RVRB");
boolean success = true;
if (!storageDir.exists()) {
success = storageDir.mkdirs();
}
if (success) {
imageFile = new File(storageDir, fileName);
}
try {
exif = new ExifInterface(imageFile.getAbsolutePath());
exifOrientation = exif.getAttribute(ExifInterface.TAG_ORIENTATION);
if (Integer.parseInt(exifOrientation) >= 0 && Integer.parseInt(exifOrientation) <= 1) {
rotationDegree = 0;
} else if (Integer.parseInt(exifOrientation) >= 2 && Integer.parseInt(exifOrientation) <= 4) {
rotationDegree = 180;
} else if (Integer.parseInt(exifOrientation) >= 7 && Integer.parseInt(exifOrientation) >= 8) {
rotationDegree = 270;
} else {
rotationDegree = 90;
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
Bitmap thumbnail = null;
if (data != null && data.getData() != null) {
try {
thumbnail = MediaStore.Images.Media.getBitmap(activity.getContentResolver(), data.getData());
Matrix matrix = new Matrix();
matrix.postRotate(rotationDegree);
Bitmap scaledBitmap = Bitmap.createScaledBitmap(thumbnail, thumbnail.getWidth(), thumbnail.getHeight(), true);
rotatedBitmap = Bitmap.createBitmap(scaledBitmap, 0, 0, scaledBitmap.getWidth(), scaledBitmap.getHeight(), matrix, true);
if (imageFile != null) {
OutputStream fOut = new FileOutputStream(imageFile);
rotatedBitmap.compress(Bitmap.CompressFormat.JPEG, 70, fOut);
fOut.close();
}
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
} else {
try {
thumbnail = MediaStore.Images.Media.getBitmap(activity.getContentResolver(), Uri.fromFile(imageFile));
Matrix matrix = new Matrix();
matrix.postRotate(rotationDegree);
Bitmap scaledBitmap = Bitmap.createScaledBitmap(thumbnail, thumbnail.getWidth(), thumbnail.getHeight(), true);
rotatedBitmap = Bitmap.createBitmap(scaledBitmap, 0, 0, scaledBitmap.getWidth(), scaledBitmap.getHeight(), matrix, true);
if (imageFile != null) {
OutputStream fOut = new FileOutputStream(imageFile);
rotatedBitmap.compress(Bitmap.CompressFormat.JPEG, 70, fOut);
fOut.close();
}
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
} else {
try {
Bitmap bitmap = null;
imageFile = new File(storageDir, fileName);
bitmap = MediaStore.Images.Media.getBitmap(activity.getContentResolver(), Uri.fromFile(imageFile));
if (bitmap != null && (bitmap.getHeight() < bitmap.getWidth())) {
Matrix matrix = new Matrix();
matrix.postRotate(rotationDegree);
Bitmap scaledBitmap = Bitmap.createScaledBitmap(bitmap, bitmap.getWidth(), bitmap.getHeight(), true);
rotatedBitmap = Bitmap.createBitmap(scaledBitmap, 0, 0, scaledBitmap.getWidth(), scaledBitmap.getHeight(), matrix, true);
if (imageFile != null) {
OutputStream fOut = new FileOutputStream(imageFile);
rotatedBitmap.compress(Bitmap.CompressFormat.JPEG, 70, fOut);
fOut.close();
}
} else {
rotatedBitmap = bitmap;
}
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
return new ImagesData(imageFile, rotatedBitmap);
}
public ImagesData imageFromGallery(Intent data) {
File imageFile = null;
float rotationDegree = 0;
Bitmap rotatedBitmap = null;
try {
Bitmap bm = null;
Uri selectedImage = null;
if (data != null && data.getData() != null) {
try {
selectedImage = data.getData();
bm = MediaStore.Images.Media.getBitmap(activity.getContentResolver(), selectedImage);
} catch (IOException e) {
e.printStackTrace();
}
} else {
try {
bm = (Bitmap) data.getExtras().get("data");
} catch (Exception e) {
e.printStackTrace();
}
}
String timeStamp = String.valueOf(System.currentTimeMillis());
String fileName = timeStamp + ".jpg";
File storageDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) + "/RVRB");
boolean success = true;
if (!storageDir.exists()) {
success = storageDir.mkdirs();
}
if (success) {
imageFile = new File(storageDir, fileName);
}
rotationDegree = getRotationFromURI(selectedImage, activity);
Matrix matrix = new Matrix();
matrix.postRotate(rotationDegree);
Bitmap scaledBitmap = Bitmap.createScaledBitmap(bm, bm.getWidth(), bm.getHeight(), true);
rotatedBitmap = Bitmap.createBitmap(scaledBitmap, 0, 0, scaledBitmap.getWidth(), scaledBitmap.getHeight(), matrix, true);
if (imageFile != null) {
OutputStream fOut = new FileOutputStream(imageFile);
rotatedBitmap.compress(Bitmap.CompressFormat.JPEG, 70, fOut);
fOut.close();
}
} catch (Exception e) {
e.printStackTrace();
}
return new ImagesData(imageFile, rotatedBitmap);
}
private int getRotationFromURI(Uri contentUri, Context mContext) {
Cursor cursor = null;
try {
String[] proj = new String[]{MediaStore.Images.ImageColumns.ORIENTATION};
cursor = mContext.getContentResolver().query(contentUri, proj, null, null, null);
assert cursor != null;
cursor.moveToFirst();
return cursor.getInt(0);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (cursor != null) {
cursor.close();
}
}
return 0;
}
public class ImagesData {
File imageFile = null;
Bitmap bitmap = null;
private ImagesData(File imageFile, Bitmap bitmap) {
this.imageFile = imageFile;
this.bitmap = bitmap;
}
public File getImageFile() {
return imageFile;
}
public void setImageFile(File imageFile) {
this.imageFile = imageFile;
}
public Bitmap getBitmap() {
return bitmap;
}
public void setBitmap(Bitmap bitmap) {
this.bitmap = bitmap;
}
}
}
Now you can call camera intent using below way on click of any button.
Global Declaration : Uri fileUri;
PictureUtils images = new PictureUtils(ArtistAddEditMembers.this);
fileUri = images.openCameraIntent(REQUEST_OPEN_CAMERA);
You can handel the result in onActivityResult
method using below way.
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == Activity.RESULT_OK) {
if (requestCode == REQUEST_OPEN_CAMERA) {
try {
PictureUtils images = new PictureUtils(ArtistAddEditMembers.this);
PictureUtils.ImagesData imagesData = images.resultFromCamera(data);
imageFile = imagesData.getImageFile();
finalBitmap = imagesData.getBitmap();
displayImagePreview(finalBitmap);
} catch (Exception e) {
e.printStackTrace();
}
} else if (requestCode == REQUEST_OPEN_GALLERY) {
try {
PictureUtils images = new PictureUtils(ArtistAddEditMembers.this);
PictureUtils.ImagesData imagesData = images.imageFromGallery(data);
imageFile = imagesData.getImageFile();
finalBitmap = imagesData.getBitmap();
displayImagePreview(finalBitmap);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
For Display image in imageview
public void displayImagePreview(Bitmap bitmap) {
Glide.with(mContext).load(bitmap).into(ivUserProfile);
}
or you can simply set image bitmap in imageview without using glide.
I handeled below case in above example.
- If version is >= N, the I am using
FileProvider.getUriForFile
andUri.fromFile
for lower version. - Display right rotation of Image instead of Display image after rotate.
Add below code in AndroidManefest.xml file under application tag for file provider
<application
......>
<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/filepaths" />
</provider>
</application>
Create filepaths.xml file under xml resource directory
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path name="external_files" path="."/>
</paths>
Make sure you popup and handel runtime permission for Camera and Write Internal Storage permission before call camera intent.
Image Save to SdCard for 6.0.1 Android Version
On Android 6.0+, you need to request runtime permission to write to external storage.
In order to request runtime permission to write to external storage:
public class MarshmallowPermission {
public static final int EXTERNAL_STORAGE_PERMISSION_REQUEST_CODE = 2;
public MarshmallowPermission() {
}
public boolean checkPermissionForExternalStorage(Activity activity) {
if(Build.VERSION.SDK_INT >= 23) {
int result = ContextCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE);
if(result == PackageManager.PERMISSION_GRANTED) {
return true;
} else {
return false;
}
} else {
return true;
}
}
public void requestPermissionForExternalStorage(Activity activity) {
if(ActivityCompat.shouldShowRequestPermissionRationale(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
Toast.makeText(activity,
"External Storage permission needed. Please allow in App Settings for additional functionality.",
Toast.LENGTH_LONG).show();
// user has previously denied runtime permission to external storage
} else {
ActivityCompat.requestPermissions(activity,
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
EXTERNAL_STORAGE_PERMISSION_REQUEST_CODE);
}
}
}
Then you can do
if(!marshmallowPermission.checkPermissionForExternalStorage(this)) {
marshmallowPermission.requestPermissionForExternalStorage(this);
} else {
// can write to external
}
And
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if(requestCode == MarshmallowPermission.EXTERNAL_STORAGE_PERMISSION_REQUEST_CODE) {
if(marshmallowPermission.checkPermissionForExternalStorage(this)) {
// can write to external
} else {
// runtime permission denied, user must enable permission manually
}
}
}
Screenshot image file is not visible in the gallery in Oreo
The Solution is using MediaScannerConnection
after saving your image to Memory.
// Tell the media scanner about the new file so that it is
// immediately available to the user.
MediaScannerConnection.scanFile(this,
new String[] { file.toString() }, null,
new MediaScannerConnection.OnScanCompletedListener() {
public void onScanCompleted(String path, Uri uri) {
Log.i("ExternalStorage", "Scanned " + path + ":");
Log.i("ExternalStorage", "-> uri=" + uri);
}
});
Check for referrence
Android Oreo (API 26) - Create dir in external storage
I have no problems running your existing code on a Nexus 5X running Android 8.0. Using adb shell ls /storage/emulated/0
, I see Chores/
, and inside there I see Processed Audio/
. This is for an app with WRITE_EXTERNAL_STORAGE
permission, including runtime permissions.
That being said, ideally, do not use string concatenation to create File
objects. Instead, use:
final File dir = new File(new File(Environment.getExternalStorageDirectory(), "Chords"), "Processed Audio");
Saving file shows Permission Denied though permission granted
You can try the following:
AsyncTask fileTask = new AsyncTask() {
@Override
protected Object doInBackground(Object[] objects) {
File directory = new File(Environment.getExternalStorageDirectory() + File.separator + "MyApplication");
if (!directory.exists()) {
directory.mkdirs();
}
Random generator = new Random();
int n = 10000;
n = generator.nextInt(n);
String name = " "+n+".jpg";
File pictureFile = new File(directory, name);
pictureFile.createNewFile();
try {
FileOutputStream out = new FileOutputStream(pictureFile);
finalBitmap.compress(Bitmap.CompressFormat.JPEG, 90, out);
out.close();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
};
fileTask.execute();
Refer this for help
android studio - manually download system image for emulator
In windows: First locate your android-sdk. By default it's in your C:\Users\Your.name\AppData\Local\
in it's root folder. where you can find: SDK Manager.exe, make a folder name it "system-images", my api 25 image is at system-images\android-25\google_apis\x86_64\Files
Hope you can Figure it out. Comment if you have any problem.
Related Topics
How Returns Xxxsize from Jcomponent(S) Added to the Jlabel
Math.Random, Only Generating a 0
How to Scroll More Than One Object at the Same Time
How to Add Mouselistener to Item on Java Swing Canvas
Java Interfaces/Implementation Naming Convention
How to Make a New List in Java
How to Use Jndi Datasource Provided by Tomcat in Spring
How to Initialize List<String> Object in Java
When Should We Use Observer and Observable
What's the Difference Between Session.Persist() and Session.Save() in Hibernate
Difference Between Java.Util.Random and Java.Security.Securerandom
How to Kill Currently Running Task in Android
Ksoap2 Org.Xmlpull.V1.Xmlpullparserexception Expected Start_Tag Error
Dx Bad Class File Magic (Cafebabe) or Version (0033.0000) with Adk14
Android Microsoft Office Library (.Doc, .Docx, .Xls, .Ppt, etc.)
How to Serialize Object to JSON
Change Icons of Checked and Unchecked for Checkbox for Android