I added a destination file to save my images in app's created folder.
static final File imageRoot = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), appDirectoryName);
In cropIwa's documentation, it needs destinationUri
Here is what I have done:
cropIwaView.crop(new CropIwaSaveConfig.Builder(Uri.fromFile(imageRoot.getAbsoluteFile()))
.setCompressFormat(Bitmap.CompressFormat.PNG)
.setQuality(100) //Hint for lossy compression formats
.build());
ADDED #Khaled Lela
cropIwaView.crop(new CropIwaSaveConfig.Builder(getUriFromFile(this, new File(R.xml.file_paths + ".png")))
.setCompressFormat(Bitmap.CompressFormat.PNG)
.setQuality(100) //Hint for lossy compression formats
.build());
ADDED #Khaled Lela a saveCompleteListener of cropiwa.
cropIwaView.setCropSaveCompleteListener(new CropIwaView.CropSaveCompleteListener() {
#Override
public void onCroppedRegionSaved(Uri bitmapUri) {
addPicToGallery(CropProfilePicture.this, bitmapUri);
Toast.makeText(CropProfilePicture.this, "Done", Toast.LENGTH_SHORT).show();
finish();
}
});
Create temp file where the image should save
// Create the File where the photo should go
File photoFile = null;
try {
photoFile = createImageFile(context);
} catch (IOException ex) {
// Error occurred while creating the File
Timber.e("Can't create photoFile:%s",ex.getMessage());
}
private static File createImageFile(Context ctx) throws IOException {
// Create an image file name
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss",Locale.US).format(new Date());
String imageFileName = "JPEG_" + timeStamp + "_";
File storageDir = ctx.getExternalFilesDir(Environment.DIRECTORY_PICTURES);
return File.createTempFile(
imageFileName, /* prefix */
".png", /* suffix */
storageDir /* directory */
);
}
Generate Uri and use FileProvide when version LOLLIPOP or above
final Uri imageUri ;
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP)
imageUri = Uri.fromFile(photoFile); // file://
else
imageUri = getUriFromFile(context,photoFile); // FileProvider
Saving image logic
cropIwaView.setCropSaveCompleteListener(bitmapUri -> {
addPicToGallery(context, bitmapUri); // sendBroadcast to gallery to scan new added images...
});
cropIwaView.crop(new CropIwaSaveConfig.Builder(imageUri)
.setCompressFormat(Bitmap.CompressFormat.PNG)
.setQuality(100) //Hint for lossy compression formats
.build());
Use FileProvider with android version LOLLIPOP and above
private static Uri getUriFromFile(Context context, File newFile) {
return FileProvider.getUriForFile(context, context.getPackageName() + ".fileprovider", newFile);
}
Under app res add file_paths.xml
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path name="my_images" path="Android/data/com.your_package_id/files/Pictures" />
</paths>
manifest.xml
<application
...// other attributes
>
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.your_package_id.fileprovider"
android:readPermission="com.your_package_id.fileprovider.READ"
android:grantUriPermissions="true"
android:exported="false">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/file_paths" />
</provider>
</application>
add permission
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
Update
Update image on gallery.
private static void addPicToGallery(Context context, Uri contentUri) {
Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
mediaScanIntent.setData(contentUri);
context.sendBroadcast(mediaScanIntent);
}
Related
How to save bitmap to image file? (png/jpg any types don't care...)
I'm running device(hisilicon) & android app.
Android and device are communicating over sockets.
Device send Image (h.264) and show it on Android on TextureView.
I can get Bitmap from TextureView, using textureView.getBitmap().
I made a button button to save the picture, textureView.getBitmap().
<Button
android:id="#+id/btnSavePng"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="onBtnSavePng"
android:text="savePicture" />
and, onclick function is like under.
public String getCurrentTimeString() {
int yyyy = Calendar.getInstance().get(Calendar.YEAR);
int MM = Calendar.getInstance().get(Calendar.MONTH) + 1;
int dd = Calendar.getInstance().get(Calendar.DAY_OF_MONTH);
int hh = Calendar.getInstance().get(Calendar.HOUR);
int mm = Calendar.getInstance().get(Calendar.MINUTE);
int ss = Calendar.getInstance().get(Calendar.SECOND);
String result = yyyy+"-"+MM+"-"+dd+" "+hh+":"+mm+":"+ss;
return result;
}
public void onBtnSavePng(View view) {
try {
File storage = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM);
String fname = getCurrentTimeString() + ".jpg";
File tp = new File(storage, fname);
Bitmap bm = textureView.getBitmap();
tp.createNewFile(); // Result of File.createNewFile() ignored
FileOutputStream ot = new FileOutputStream(tp);
bm.compress(Bitmap.CompressFormat.JPEG, 100, ot);
ot.close();
} catch(Exception e) {
Log.d("onBtnSavePng", e.toString()); // java.io.IOException: Operation not permitted
}
}
I allow uses-permissions AndroidManifest.xml like under, and android:requestLegacyExternalStorage="true" is on application.
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.GET_TASKS" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
Is there any problem with my codes?
If so, how to save bitmap to png or jpg file?
I guess, my app don't access to directory.
Thank you for read my question.
Self Solved.
public void onBtnSavePng(View view) {
try {
String root = Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES).toString();
File myDir = new File(root + "/saved_images");
myDir.mkdirs();
String fname = getCurrentTimeString() + ".jpg";
File file = new File(myDir, fname);
FileOutputStream out = new FileOutputStream(file);
Bitmap bm = textureView.getBitmap();
bm.compress(Bitmap.CompressFormat.JPEG, 100, out);
out.flush();
out.close();
} catch( Exception e) {
Log.d("onBtnSavePng", e.toString());
}
I don't understand why my code is worked not yet.
Anyway, Thank you for all who helps me.
I'll try all of reply codes.
Thank you.
1.Check it if you don't request the runtime permission yet: https://developer.android.com/training/permissions/requesting
2.Or if your android is higher than 10:
https://developer.android.com/about/versions/11/privacy/storage#scoped-storage
After you update your app to target Android 11, the system ignores the requestLegacyExternalStorage flag.
Then you have to use SAF or MediaStore API to store the bitmap in the "public directory".
SAF:
https://developer.android.com/guide/topics/providers/document-provider
MediaStore API:
https://developer.android.com/reference/android/provider/MediaStore
public void onBtnSavePng(View view) {
try {
String fileName = getCurrentTimeString() + ".jpg";
ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.DISPLAY_NAME, fileName);
values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpg");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
values.put(MediaStore.MediaColumns.RELATIVE_PATH, "DCIM/");
values.put(MediaStore.MediaColumns.IS_PENDING, 1);
} else {
File directory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM);
File file = new File(directory, fileName);
values.put(MediaStore.MediaColumns.DATA, file.getAbsolutePath());
}
Uri uri = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
try (OutputStream output = getContentResolver().openOutputStream(uri)) {
Bitmap bm = textureView.getBitmap();
bm.compress(Bitmap.CompressFormat.JPEG, 100, output);
}
} catch (Exception e) {
Log.d("onBtnSavePng", e.toString()); // java.io.IOException: Operation not permitted
}
}
In kotlin
This is working for me->
fun saveMediaToStorage(bitmap: Bitmap, context: Context, onUriCreated: (Uri) -> Unit) {
var fos: OutputStream? = null
//Generating a file name
val filename = "${System.currentTimeMillis()}.jpg"
try {
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.Q) {
val resolver = context.contentResolver
val contentValues = ContentValues()
contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, "$filename.jpg")
contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "image/jpg")
val imageUri =
resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues)
fos = Objects.requireNonNull(imageUri)?.let {
resolver.openOutputStream(it)
}
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos)
Objects.requireNonNull(fos)
imageUri?.let { onUriCreated(it) }
} else {
//These for devices running on android < Q
val imagesDir =
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)
val image = File(imagesDir, filename)
fos = FileOutputStream(image)
fos.use {
//Finally writing the bitmap to the output stream that we opened
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, it)
}
onUriCreated(Uri.fromFile(image))
}
} catch (e: Exception) {
Log.d("error", e.toString())
}
}
String myDir =new File (Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS)+pdfDirectoryImage);
fileName = filename + ".png";
androidElevenPath = saveImages(mContext,finalBitmap,filename);
private static Uri saveImages(Context mContext, Bitmap bitmap, #NonNull String name) throws IOException {
boolean saved;
OutputStream fos;
File image = null;
Uri imageUri = null;
String imagesDir = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
ContentResolver resolver = mContext.getContentResolver();
ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, name);
contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "image/png");
contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, "DCIM" + pdfDirectoryImage);
imageUri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);
fos = resolver.openOutputStream(imageUri);
} else {
imagesDir = Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_DCIM).toString() + File.separator + pdfDirectoryImage;
File file = new File(imagesDir);
if (!file.exists()) {
file.mkdir();
}
image = new File(imagesDir, name + ".png");
fos = new FileOutputStream(image);
}
saved = bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);
fos.flush();
fos.close();
return imageUri;
}
In case you are on Android11+ and you have the storage manager permission granted and are working directly with files (and not the MediaStore API) the cause could be a ":" char (usually from a timestamp) in the name of the created file. Android 11 (unlike older Android versions) does not allow that.
I'm fairly new into saving media in android, and I'm wondering why this is giving me so much trouble. I am requiring photos taking by my app to be made public, so other apps (including the gallery) can find them.
The documentation states that it if you want an image to be accessible by other apps, you should save them on in the public external storage which is provided by getExternalStoragePublicDirectory(), with the DIRECTORY_PICTURES as the argument.
When I try to do this however, the IOException is thrown in my method, and I'm struggling to find out why.
File creation method:
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 = Environment.getExternalStoragePublicDirectory(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;
}
Photo capture method:
public void capturePhoto (View view) {
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
Toast.makeText(this, "Error", Toast.LENGTH_SHORT).show();
}
// Continue only if the File was successfully created
if (photoFile != null) {
Uri photoURI = FileProvider.getUriForFile(this, "com.auswide.auswidedashboard", photoFile);
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
startActivityForResult(takePictureIntent, IMAGE_REQUEST);
}
}
}
Permissions and Provider code in the Manifest
<uses-feature android:name="android.hardware.camera" android:required="false"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.auswide.auswidedashboard"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/file_paths"/>
</provider>
And finally the path required by the provider:
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path name="my_images" path="Android/data/com.auswide.auswidedashboard/files/Pictures"/>
</paths>
The exception message reads I/System.out: java.io.IOException: Permission denied', which simply confuses me more as I though I gave it the permissions in the manifest?
Any help would be greatly appreciated.
I'm trying to implement a camera activity where the user takes a picture and saves it to the phone.
Error:
java.lang.IllegalArgumentException: Failed to find configured root that contains /storage/emulated/0/Android/data/no.test.group_project/files/Pictures/JPEG_20200423_114453_750680396837474735.jpg
AndroidManifest.xml
android:name="androidx.core.content.FileProvider"
android:authorities="no.test.group_project.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-files-path name="my_images" path="Android/data/no.test/group_project/files/Pictures/" />
</paths>
Java
public void openCamera(View view) {
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) {
ex.printStackTrace();
}
if (photoFile != null) {
Uri photoURI = FileProvider.getUriForFile(this,
"no.test.group_project.fileprovider",
photoFile);
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
startActivityForResult(takePictureIntent, Utils.REQUEST_IMAGE_CAPTURE);
}
}
}
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 */
);
currentPhotoPath = image.getAbsolutePath();
return image;
}
I am getting above error when I try to get the Uri from
FileProvider.getUriForFile(this,
"no.test.group_project.fileprovider",
photoFile
);
Replace path="Android/data/asmund.thomas.group_project/files/Pictures/" with path=".".
EDIT: Facebook issue : https://developers.facebook.com/support/bugs/2434932356771915/
I'm trying to share a bitmap to an Instagram story sticker.
But it loads Instagram, but doesn't show any sticker, no background colors, only a black story screen.
What is wrong with this code ? I check if file was in the external data cache directory and that's good.
According to Facebook's Instagram documentation :
I added read/write rights to storage and a provider to my AndroidManifest.xml
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<provider
android:authorities="my.app.id.fileprovider"
android:name="androidx.core.content.FileProvider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/file_paths" />
</provider>
I added a file_paths.xml to share my external cache folder to other apps
<?xml version="1.0" encoding="utf-8"?>
<resources>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-cache-path name="share" path="."/>
</paths>
</resources>
I create a bitmap from a drawable, write it to a file on the external cache dir, and apply the Intent like example in the docs
public static void sendStickerToInstagram() {
Bitmap bm = BitmapFactory.decodeResource(context.getResources(), R.drawable.sticker);
File extStorageDirectory = context.getExternalCacheDir();
File stickerFile = new File(extStorageDirectory, "sticker.png");
try {
FileOutputStream outStream = new FileOutputStream(stickerFile);
bm.compress(Bitmap.CompressFormat.PNG, 100, outStream);
outStream.flush();
outStream.close();
} catch (IOException e) {
Log.e("TEST", e.getMessage());
}
Uri stickerUri = FileProvider.getUriForFile(activity, "my.app.id.fileprovider", stickerFile);
// Uri stickerUri = Uri.fromFile(stickerFile);
String linkUrl = "https://stackoverflow.com";
Intent intent = new Intent("com.instagram.share.ADD_TO_STORY");
intent.setType("image/*");
intent.putExtra("interactive_asset_uri", stickerUri);
intent.putExtra("content_url", linkUrl);
intent.putExtra("top_background_color", "#33FF33");
intent.putExtra("bottom_background_color", "#FF00FF");
grantUriPermission("com.instagram.android", stickerUri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
if (getPackageManager().resolveActivity(intent, 0) != null) {
startActivityForResult(intent, 0);
}
}
Most strange part is when I test to share my sticker as background, it works, so the file is loaded as espected, with all permissions.
Intent intent = new Intent("com.instagram.share.ADD_TO_STORY");
intent.setDataAndType(stickerUri, "image/*");
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.putExtra("content_url", linkUrl);
if (activity.getPackageManager().resolveActivity(intent, 0) != null) {
activity.startActivityForResult(intent, 0);
}
Any idea why I can't share a sticker ?
Don't worry... It's not you. It's Instagram. :P
Their most recent release of Instagram on Android broke the
com.instagram.share.ADD_TO_STORY intent.
Keep an eye on this thread for updates.
https://developers.facebook.com/support/bugs/2434932356771915/
I have seen this question asked previously, but before you mark this question as redundant let me just say that I've been trying to figure this out for three hours and I'm just confused. My goal is to take a photo, set it to an imageview, and upload it to Firebase Storage. In order to get the full size photo, I believe I have to save it to a local file, because the FileProvider takes a reference to a file and that's where the full size photo goes. Is that correct so far? I cannot for the life of me get the FileProvider to accomplish this.
After many attempts, this is what I have:
// It all starts here
public void onClick(View v) {
Log.d("DebugMySocks", "Change photo button clicked");
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
photoFile = null;
try {
photoFile = createImageFile();
} catch (IOException ex) {
// Error occurred while creating the File
}
// Continue only if the File was successfully created
if (photoFile != null) {
Uri photoURI = FileProvider.getUriForFile(getApplicationContext(),
getPackageName(),
photoFile);
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);
}
}
}
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
mCurrentPhotoPath = image.getAbsolutePath();
return image;
}
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");
mProfileImage.setImageBitmap(imageBitmap);
}
}
// in manifest file
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="${applicationId}"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/filepaths" />
</provider>
// in /xml/filepaths.xml
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="my_images" path="com.example.kevin.moresocksplease/files/Pictures" />
</paths>
and the error message is:
java.lang.IllegalArgumentException: Failed to find configured root that contains /storage/emulated/0/Android/data/com.example.kevin.moresocksplease/files/Pictures/JPEG_20171214_111337.jpg706457555.jpg
Replace:
<external-path name="my_images" path="com.example.kevin.moresocksplease/files/Pictures" />
with:
<external-files-path name="my_images" />