I'm trying to save an image on the public storage directory using the below code. However, when this is saving to storage/emulated/0 and not to the public Pictures folder. I have used basically this exact code in another app and it's worked fine. Does anyone know why .getExternalStoragePublic directory is not returning the accessible /Pictures/ and instead returning storage/emulated/0 ?
I have "uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" in my Manifest file
File backupFile;
File appFolder;
String path= Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).getAbsolutePath() + "/CustomFolder";
appFolder = new File(path);
if (!appFolder.exists())
appFolder.mkdir();
String fileName = "picture.jpg"
backupFile = new File(appFolder, fileName);
FileOutputStream output = null;
try {
output = new FileOutputStream(backupFile);
output.write(bytes);
} catch (IOException e) {
e.printStackTrace();
} finally {
mImage.close();
if (null != output) {
try {
output.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Nevermind, I figured it out. It turns out just having "uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" in the Manifest file is insufficient, and I had to put the following code before it to make it work:
int CAMERA_PERMISSION_REQUEST_CODE = 2;
int result = ContextCompat.checkSelfPermission(getActivity().getApplicationContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE);
if (result != PackageManager.PERMISSION_GRANTED){
if (ActivityCompat.shouldShowRequestPermissionRationale(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE)){
Toast.makeText(getActivity().getApplicationContext(), "External Storage permission needed. Please allow in App Settings for additional functionality.", Toast.LENGTH_LONG).show();
} else {
ActivityCompat.requestPermissions(getActivity(),new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},CAMERA_PERMISSION_REQUEST_CODE);
}
}
Related
I am writing a new Application on Android 11 (SDK Version 30) and I simply cannot find an example on how to save a file to the external storage.
I read their documentation and now know that they basicly ignore Manifest Permissions (READ_EXTERNAL_STORAGE and WRITE_EXTERNAL_STORAGE). They also ignore the android:requestLegacyExternalStorage="true" in the manifest.xml application tag.
In their documentation https://developer.android.com/about/versions/11/privacy/storage they write you need to enable the DEFAULT_SCOPED_STORAGE and FORCE_ENABLE_SCOPED_STORAGE flags to enable scoped storage in your app.
Where do I have to enable those?
And when I've done that how and when do I get the actual permission to write to the external storage? Can someone provide working code? I want to save .gif, .png and .mp3 files. So I don't want to write to the gallery.
Thanks in advance.
Corresponding To All Api, included Api 30, Android 11 :
public static File commonDocumentDirPath(String FolderName)
{
File dir = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R)
{
dir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS) + "/" + FolderName);
}
else
{
dir = new File(Environment.getExternalStorageDirectory() + "/" + FolderName);
}
// Make sure the path directory exists.
if (!dir.exists())
{
// Make it, if it doesn't exit
boolean success = dir.mkdirs();
if (!success)
{
dir = null;
}
}
return dir;
}
Now, use this commonDocumentDirPath for saving file.
A side note from comments, getExternalStoragePublicDirectory with certain scopes are now working with Api 30, Android 11. Cheers! Thanks to CommonsWare hints.
You can save files to the public directories on external storage.
Like Documents, Download, DCIM, Pictures and so on.
In the usual way like before version 10.
**Simplest Answer and Tested ( Java ) **
private void createFile(String title) {
Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("text/html");
intent.putExtra(Intent.EXTRA_TITLE, title);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, Uri.parse("/Documents"));
}
createInvoiceActivityResultLauncher.launch(intent);
}
private void createInvoice(Uri uri) {
try {
ParcelFileDescriptor pfd = getContentResolver().
openFileDescriptor(uri, "w");
if (pfd != null) {
FileOutputStream fileOutputStream = new FileOutputStream(pfd.getFileDescriptor());
fileOutputStream.write(invoice_html.getBytes());
fileOutputStream.close();
pfd.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
/////////////////////////////////////////////////////
// You can do the assignment inside onAttach or onCreate, i.e, before the activity is displayed
String invoice_html;
ActivityResultLauncher<Intent> createInvoiceActivityResultLauncher;
#Override
protected void onCreate(Bundle savedInstanceState) {
invoice_html = "<h1>Just for testing received...</h1>";
createInvoiceActivityResultLauncher = registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
result -> {
if (result.getResultCode() == Activity.RESULT_OK) {
// There are no request codes
Uri uri = null;
if (result.getData() != null) {
uri = result.getData().getData();
createInvoice(uri);
// Perform operations on the document using its URI.
}
}
});
I'm using this method and it really worked for me
I hope I can help you. Feel free to ask me if something is not clear to you
Bitmap imageBitmap;
OutputStream outputStream ;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
{
ContentResolver resolver = context.getContentResolver();
ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME,"Image_"+".jpg");
contentValues.put(MediaStore.MediaColumns.MIME_TYPE,"image/jpeg");
contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH,Environment.DIRECTORY_PICTURES + File.separator+"TestFolder");
Uri imageUri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,contentValues);
try {
outputStream = resolver.openOutputStream(Objects.requireNonNull(imageUri) );
imageBitmap.compress(Bitmap.CompressFormat.JPEG,100,outputStream);
Objects.requireNonNull(outputStream);
Toast.makeText(context, "Image Saved", Toast.LENGTH_SHORT).show();
} catch (Exception e) {
Toast.makeText(context, "Image Not Not Saved: \n "+e, Toast.LENGTH_SHORT).show();
e.printStackTrace();
}
}
manifest file (Add Permission)
<uses-permission android:name="android.permission.ACCESS_MEDIA_LOCATION" />
I want to achieve that after I click the BACKUP button, the mydb.db file from my app database folder will be copied to device internal memory, where a New folder should be created and the file put inside it.
My code what I tried:
backupbtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
try {
File data = Environment.getDataDirectory();
File extdata = Environment.getExternalStorageDirectory();
String backupDBPath = "/Newfolder/mydb.db";
File ddir = new File(Environment.getExternalStorageDirectory()
+ "/Newfolder/");
DirectoryExist(ddir);
String currentDBPath ="/data/mypackage/databases/mydb.db";
File currentDB = new File(data, currentDBPath);
File backupDB = new File(extdata, backupDBPath);
if (currentDB.exists()) {
FileChannel src = new FileInputStream(currentDB).getChannel();
FileChannel dst = new FileOutputStream(backupDB).getChannel();
dst.transferFrom(src, 0, src.size());
src.close();
dst.close();
Toast.makeText(getApplicationContext(), "Backup is successful ", Toast.LENGTH_SHORT).show();
}
} catch (IOException e) {
e.printStackTrace();
}
}
});
private void DirectoryExist(File destination) {
if (!destination.isDirectory()) {
if (destination.mkdirs()) {
Log.d("Carpeta creada", "....");
} else {
Log.d("Carpeta no creada", "....");
}
}
}
However I have this error: java.io.FileNotFoundException: /storage/emulated/0/Newfolder/mydb.db (No such file or directory)
UPDATE: After investigation I found out, it is a storage permission issue.
If I set permissions manually in app settings, it is working. But when I try to set permissions in the app, it still says, permission denied:
if(!checkPermissionForReadExtertalStorage())
{
try {
requestPermissionForReadExtertalStorage();
} catch (Exception e) {
e.printStackTrace();
}
}
public boolean checkPermissionForReadExtertalStorage() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
int result = this.checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE);
return result == PackageManager.PERMISSION_GRANTED;
}
return false;
}
public void requestPermissionForReadExtertalStorage() throws Exception {
try {
ActivityCompat.requestPermissions((Activity) this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
READ_STORAGE_PERMISSION_REQUEST_CODE);
} catch (Exception e) {
e.printStackTrace();
throw e;
}
}
The popup with permissions comes up, but still, seems it doesn't allows permissions.
In manifest I have:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
However the WRITE_EXTERNAL_STORAGE is not used anymore for android 10+, so not sure if I need to keep it there.
And I have also <application android:requestLegacyExternalStorage="true"
But as I read: Starting in Android 11, apps that use the scoped storage model can access only their own app-specific cache files.
So it seems, there is no option to create any folder in local storage to backup files anymore except the app folder. One option seems to be via MediaStore API, but using this for backup one small db file seems to be an overkill.
I'm trying to copy file from within my application to the SD card, but I get the error eacces (permission denied). The OS is Android M and I have allowed runtime Storage permissions (checked in app info). I have also set the uses-permission in AndroidManifest.xml
<application>...</application>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
Doesn't work if I copy to SD card
Source: data/user/0/com.example.myapp/cache/SomeFile.txt
Destination: /storage/1032-2568/SomeFolder/
Error: java.io.FileNotFoundException: /storage/1032-2568/SomeFolder/SomeFile.txt: open failed: EACCES (Permission denied)
Works if I copy to internal storage
Source: data/user/0/com.example.myapp/cache/SomeFile.txt
Destination: /storage/emulated/0/SomeFolder/
Code to copy file from source to destination
/*
* Below are the parameters I have tried
*
* inputPath - data/user/0/com.example.myapp/cache or data/user/0/com.example.myapp/cache/
* inputFile - /SomeFile.txt or SomeFile.txt
* outputPath - /storage/1032-2568/SomeFolder/ or /storage/1032-2568/SomeFolder
*/
public static void copyFile(String inputPath, String inputFile, String outputPath) {
InputStream in = null;
OutputStream out = null;
try {
//create output directory if it doesn't exist
File dir = new File (outputPath);
if (!dir.exists()) {
dir.mkdirs();
}
in = new FileInputStream(inputPath + inputFile);
out = new FileOutputStream(outputPath + inputFile);
byte[] buffer = new byte[1024];
int read;
while ((read = in.read(buffer)) != -1) {
out.write(buffer, 0, read);
}
in.close();
// write the output file (You have now copied the file)
out.flush();
out.close();
}
catch (FileNotFoundException fnfe1) {
/* I get the error here */
Log.e("tag", fnfe1.getMessage());
}
catch (Exception e) {
Log.e("tag", e.getMessage());
}
}
ES File Explorer
I saw that ES File Explorer also cannot write anything on the SD Card on Redmi devices. Here's a video with solution. Following the steps worked for ES Explorer on my device. Can this be done programmatically?
As suggested by #CommonsWare here we have to use the new Storage Access Framework provided by android and will have to take permission from user to write SD card file as you said this is already written in the File Manager Application ES File Explorer.
Here is the code for Letting the user choose the "SD card" :
startActivityForResult(new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE), requestCode);
which will look somewhat like this :
And get the Document path in pickedDirand pass further in your copyFile block
and use this path for writing the file :
public void onActivityResult(int requestCode, int resultCode, Intent resultData) {
if (resultCode != RESULT_OK)
return;
else {
Uri treeUri = resultData.getData();
DocumentFile pickedDir = DocumentFile.fromTreeUri(this, treeUri);
grantUriPermission(getPackageName(), treeUri, Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
getContentResolver().takePersistableUriPermission(treeUri, Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
copyFile(sdCard.toString(), "/File.txt", path + "/new", pickedDir);
}
}
public void copyFile(String inputPath, String inputFile, String outputPath, DocumentFile pickedDir) {
InputStream in = null;
OutputStream out = null;
try {
//create output directory if it doesn't exist
File dir = new File(outputPath);
if (!dir.exists()) {
dir.mkdirs();
}
in = new FileInputStream(inputPath + inputFile);
//out = new FileOutputStream(outputPath + inputFile);
DocumentFile file = pickedDir.createFile("//MIME type", outputPath);
out = getContentResolver().openOutputStream(file.getUri());
byte[] buffer = new byte[1024];
int read;
while ((read = in.read(buffer)) != -1) {
out.write(buffer, 0, read);
}
in.close();
// write the output file (You have now copied the file)
out.flush();
out.close();
} catch (FileNotFoundException fnfe1) {
/* I get the error here */
Log.e("tag", fnfe1.getMessage());
} catch (Exception e) {
Log.e("tag", e.getMessage());
}
}
You need to add permission request run time in Android 6.0 (API Level 23) and up, here is the official docs
This is the code for WRITE_EXTERNAL_STORAGE
if (checkSelfPermission(android.Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
Log.d(TAG,"Permission is granted");
return true;
}
Ask for permission else like this
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_CODE);
I have also got that problem but i solved by use the request the permission in run time and after forcefully give the permission.After the permission in App info of Android device. after declare the permission in manifest =>go to setting of your device => go to app info => go to permission =>
and finally allow the permission . just remember i just talking about after api level 22 means from marshmallow.
Its seems the runtime permission are implemented correctly but the issues seems from the device
If you are using Redmi than you have to manually allow the permission of specific app in Redmi security settings
This link shows how to enable permission in redmi security
After Android 4.3 on some devices, you can't get direct write access to FileSystem on SDcard.
You should use storage access framework for that.
I can see that you are copying the entire content of one file and trying to write the same to another file. I could suggest a better way to do this :
Assuming that you already checked for file existence
StringWriter temp=new StringWriter();
try{
FileInputStream fis=new FileInputStream(inputFile+inputPath);
int i;
while((i=fis.read())!=-1)
{
temp.write((char)i);
}
fis.close();
FileOutputStream fos = new FileOutputStream(outputPath, false); // true or false based on opening mode as appending or writing
fos.write(temp.toString(rs1).getBytes());
fos.close();
}
catch (Exception e){}
This code worked for my app...Let me know if this is working for you or not..
You can't copy or Delete files & Folder on external storage using third party app. like [file explorer].
It's data policy updated after KITKAT Version.
If only allow on system apps. So you can use an original file explorer (Come from ROM).
IF you need to use 3rd party app then ROOT your device. (Root permission is required)
This one has me stumped (Android 4.3)
I have the right permission:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
... I'm explicitly creating a path to /storage/sdcard0/path/to/data.csv and the FileOutputStream doesn't throw any exceptions.
I tried reading the path via Environment.getExternalStorageDirectory() but that has some "emulated" path.
In either case, I can't see the files on the filesystem ...
What's more confusing is that MEDIA_MOUNTED_READ_ONLYis mounted_ro ... but this isn't the case .
It looks like another app us using /mnt/extSdCard... I tried hard coding that in my app but that doesn't work either. Even the directory is missing. This is very confusing.
I'm calling mkdirs() on this of course and the FileOutputStream is created....
What am I doing wrong?
Try the following code, and see where it fails?
try {
File mPath = new File(android.os.Environment.getExternalStorageDirectory(),"path/to");
if (!mPath.exists()) {
if(!mPath.mkdirs()) {
Log.e(getClass().getName(), "Can't Create Folder: "+mPath.getName());
}
}
File mLogFile = new File(mPath, "data.csv");
FileWriter mFileWriter = null;
if(mPath.canWrite()) {
mFileWriter = new FileWriter(mLogFile, true);
mBufferedWriter = new BufferedWriter(mFileWriter);
}
else {
Log.e(getClass().getName(), "Can't write under Folder: "+mPath.getName());
}
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
I've been using the way the system saves screenshots to save my bitmaps to the disk and gallery. This works in Android 4.2 and before but not in Android 4.3.
Relevant code :
Uri imageUri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
OutputStream out = resolver.openOutputStream(uri);
Full code here.
In 4.3 (new Nexus 7) however, I get a FileNotFoundException on the second line. I couldn't see any changes in 4.3 relevant to this on the website.
So what is the right way to save an image to the disk and gallery?
Verified :
storage is mounted with this method
imageUri is not null (usually something like "content://media/external/images/media/2034")
manifest has permission android.permission.WRITE_EXTERNAL_STORAGE
This is the way I save bitmaps to the Gallery:
Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
Uri contentUri; //instantiate Uri with location of image
mediaScanIntent.setData(contentUri);
context.sendBroadcast(mediaScanIntent);
In your manifest file try with change target sdk to 18.-
<uses-sdk android:minSdkVersion="7"
android:targetSdkVersion="18"/>
It might solve your prob(May not). In 4.3 JELLY_BEAN_MR2, android did couple of changes and android clearly written that Your app might misbehave in a restricted profile environment. so please look at http://developer.android.com/about/versions/android-4.3.html
I have these permission in my Manifest.
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
But I am using a Target SDK version 15. Is there a requirement that you have to use a target SDK 18?
BTW:
Here is a sample code for downloading profile pictures from Facebook:
private class DownloadProfilePicTask extends AsyncTask<Void,String,String> {
Bitmap profilePic;
String fileName;
String id;
String type;
URL img_value;
public DownloadProfilePicTask(String i,String ty)
{
id = i;
if(id==null)
{
//Log.v("Id is null", "Error");
}
//Log.v("Download Profile Pic Task initialized for id:",id);
type = ty;
}
#Override
protected String doInBackground(Void...param) {
String root = Environment.getExternalStorageDirectory().toString();
if(root==null)
{
return null;
}
try{
profilePic = BitmapFactory.decodeStream(img_value.openConnection().getInputStream());
}
catch (IOException e) {
e.printStackTrace();
}
if(profilePic == null)
{
//Log.v("profilePic is null", "Error");
}
//Log.v("Root for saving images",root );
File myDir = new File(root + "/saved_images");
myDir.mkdirs();
fileName = root + "/saved_images/" + id + ".png";
//Log.v("filename is ",fileName);
File file = new File (fileName);
fileName = file.getPath();
if (file.exists ()) file.delete ();
try {
FileOutputStream out = new FileOutputStream(file);
profilePic.compress(Bitmap.CompressFormat.PNG, 90, out);
out.flush();
out.close();
} catch (Exception e) {
e.printStackTrace();
}
return id;
}
#Override
protected void onPreExecute()
{
try
{
img_value = new URL("http://graph.facebook.com/"+id+"/picture?type=" + type);
}
catch (MalformedURLException e) {
e.printStackTrace();
}
}
#Override
protected void onPostExecute(String result) {
}
}
and then I just call:
new DownloadProfilePicTask(id,type).execute();
to download and automatically save images.
Note: You will have to play with filePath a bit for exact location.
There some changes in the fileSystem on Android 4.3 to start to avoid dev. to directly write in "/sdcard" or "/mnt/sdcard" but use the android ExternalStorage system. (http://source.android.com/devices/tech/storage/index.html)
N.B. : ExternalStorage can be an internal memory :p
For your case, have you tryed to use a method based on getExternalStorage ?
(like this : Find an external SD card location)