Can't create folder on external storage on android - java

My development phone is a Nexus 5, running Android 4.4.2.
In my application, I am attempting to create a folder on external storage that will store debug information for my application. Basically it will contain all the commands executed by the application, so that when a user encounters a problem, I have the option of having them send me the information from the debug folder to analyse.
I started off by trying to write a file to the folder, but found there was an error creating the folder. At first I was using mkdir(), then I moved onto mkdirs() which also didn't work.
I have <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> in my manifest.
Here is the code for creating the folder:
File folder = new File(Environment.getExternalStorageDirectory() + "/DebugData");
String path = folder.getPath();
if(!folder.mkdirs() || !folder.exists()){
Log.e(LOG_TAG, path + " failed");
} else {
Log.d(LOG_TAG, path + " succeeded");
}
Here is what I have also tried:
//Check SD card state
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state) || !Environment.MEDIA_MOUNTED.equals(state)) {
Log.e(LOG_TAG, "Error: external storage is read only or unavailable");
} else {
Log.d(LOG_TAG, "External storage is not read only or unavailable");
}
This returns that the external storage is not read only or unavailable.
I have also tried different paths, such as File folder = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "Folder1");
This is where it became really confusing.
I tried development on different phones. Firstly, I grabbed a Galaxy S4 GT-i9505 running Android 4.2.2 and it worked. I was able to create the folders and write to them. This showed me that the code was working. Also the path returned by running the code on the S4 and Nexus 5 was the same.
Then I thought it may be android version specific. So I grabbed a Nexus 4 with Android 4.4.2 and the code worked on it as well. Created the folders and allowed me to write to them.
None of the phones are rooted and are all stock standard. There's no special applications or anything I can think of settings wise on the Nexus 5 that would cause permissions problems. The connection is set to Media Device (MTP).
EDIT:
I should add that I have tried the follow which also did not work:
Writing a file to the root directory of the external storage
Creating the file in the external storage root directory and writing to it
Creating a folder in a path outlined and writing a file to it
Creating the file in the path outlined and writing to it
I am confused as to what is causing this, is there anything else I can test or change to fix the issue?
EDIT 2:
Turns out the issue was due to, I think, indexing.
Basically all of the other devices I tested on, allowed me to reconnect the USB connection and view the created files and folders.
For some reason my Nexus 5 doesn't index the folders/files, even though they exist.
I downloaded a different 3rd party file explorer application and noticed all the folders and files were there.
So to view these folders and files via USB debugging, I have to restart the phone in order to re-index them, which seems quite annoying but it is better than it not working at all.
Thanks.

In terms of this being an indexing issue with the Nexus, this worked for me:
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);
}
});
You should call it straight after creating and saving the file. By using the scanner, I was able to see newly created files and directories simply by replugging the device in.
According to the docs:
MediaScannerConnection provides a way for applications to pass a newly
created or downloaded media file to the media scanner service. The
media scanner service will read metadata from the file and add the
file to the media content provider.
Hope this helps someone else.

Turns out the issue was due to, I think, indexing.
Basically all of the other devices I tested on, allowed me to reconnect the USB connection and view the created files and folders.
For some reason my Nexus 5 doesn't index the folders/files, even though they exist.
I downloaded a different 3rd party file explorer application and noticed all the folders and files were there.
So to view these folders and files via USB debugging, I have to restart the phone in order to re-index them, which seems quite annoying but it is better than it not working at all.

For android sdk version 23 and above you should check if the user has granted permission of external storage.
private void createFolder() {
if (isStoragePermissionGranted()) {
File folder = new File(Environment.getExternalStorageDirectory()+ File.separator + "DebugData");
if(!folder.exists()){
folder.mkdir();
}
}
public boolean isStoragePermissionGranted() {
if (Build.VERSION.SDK_INT >= 23) {
if (checkSelfPermission(android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
== PackageManager.PERMISSION_GRANTED) {
return true;
} else {
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
return false;
}
}
else { //permission is automatically granted on sdk<23 upon installation
return true;
}
}
The code above worked for me and I hope it will work for you.

Had the same problem. Turned out I was testing on Android 6 and did not check for runtime WRITE_EXTERNAL_STORAGE permission.

First, new File() is just create object for file connect.
you want use this file, file is exists or createNewFile().
If you want use this file to create directory, check file is exists, isDirectory() after then mkDir().
Second, check Environment.getExternalStorageDirectory is variable path.
You can use DDMS or ADB for work.
Additionaly, I think you add permission for read external storage for something error.

If you are running an Android device with api >= 23, you need to request permission from user before you call mkdir() function.
My previous answer with example code can be found
https://stackoverflow.com/a/38694026/5089713

For Android 10+ add following into manifest:
<application
android:requestLegacyExternalStorage="true"
...
as asking for Manifest.permission.WRITE_EXTERNAL_STORAGE is no more enough.

Typically, I do this:
/**
* Return a dedicated directory named "MyApp" at the top of external storage
*/
public static file getDataDir() {
File sdcard = Environment.getExternalStorageDirectory();
if( sdcard == null || !sdcard.isDirectory() ) {
// TODO: warning popup
Log.w(TAG, "Storage card not found " + sdcard);
return null;
}
File dataDir = new File(sdcard, "MyApp");
if( !confirmDir(dataDir) ) {
// TODO: warning popup
Log.e(TAG, "Unable to create " + dataDir);
return null;
}
return dataDir;
}
private static boolean confirmDir(File dir) {
if (dir.isDirectory()) return true; // already exists
if (dir.exists()) return false; // already exists, but is not a directory
return dir.mkdirs(); // create it
}
Also, add this permission to your manifest:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
If you want private storage (typically under /data/) for your app (not on sdcard), then look into getDir(), getFilesDir(), fileList(), openFileInput(), openFileOutput(), etc.
There are also helper functions to get private directories within the sdcard for API 8 and above: getExternalFilesDir(), getExternalCacheDir(), etc.

Related

Set image changes when I move from one fragment to another, How to keep it forever even when I restart the app?

please help I could not add code, it is throwing error , I'm new.
ActivityResultLauncher<Intent> picActivityResultLauncher = registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
new ActivityResultCallback<ActivityResult>() {
#Override
public void onActivityResult(ActivityResult result) {
if (result.getResultCode() == Activity.RESULT_OK) {
Intent data = result.getData();
// your operation....
Uri pic = data.getData();
profile.setImageURI(pic);
}
}
}
It depends on the device's android version. If it's 10 or lower than that, then you can simply save the file path in the Sharedpreference so you can access it later and load the image from there (You can do it in android 10 with requestLegacyExternalStorage of course or you can go with the 2nd option which I provided below).
But, if you're writing the code for android 11 or higher, then there are only three standard ways to do it.
1. Using SAF (Storage Access Framework) :-
You can get the storage access permission of that perticular folder everytime you pick an image from there. But this is not the best option when your app is doing it multiple times. (what if it's photo editor app or social media or something like that?!)
2. Manage all files permission :-
You can go with the All files access permission but it's also too much for the small task and you also have to give clarification to google play when your app has that permission. So it's also the very last option.
3. Accessing from internal app directories - THE BEST WAY! :-
You can go with this option with almost every app!
All you have to do is just take read storage permission, access the file using file descriptor, write it to the internal app directory (it can be either external files directory or cache directory), then you'll have a lifetime access of the image. You can save the path to Sharedpreference and access it anytime.
If you want to save edited image to the gallery then it will also be easy because you already have both read and write permission to that image saved in internal app directory.
That's it. I know the answer is lengthy but it's worth it. :)

Trying to make a folder in internal storage android [duplicate]

I'm unable to create directory in android 10. It's working on devices till android Oreo.
I tried two ways for creating folders.
Using File.mkdir():
File f = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/Pastebin");
if (!f.isFile()) {
if (!(f.isDirectory())) {
success = f.mkdir();
}
Here, the variable success is always false which means the directory isn't created.
Using Files.createDirectory():
File f = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/Pastebin");
if (!f.isFile()) {
if (!(f.isDirectory())) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
try {
Files.createDirectory(Paths.get(f.getAbsolutePath()));
} catch (IOException e) {
e.printStackTrace();
Toast.makeText(getApplicationContext(), R.string.unable_to_download, Toast.LENGTH_LONG).show();
}
} else {
f.mkdir();
}
}
which causes this exception:
pzy64.pastebinpro W/System.err: java.nio.file.AccessDeniedException: /storage/emulated/0/Pastebin
pzy64.pastebinpro W/System.err: at sun.nio.fs.UnixFileSystemProvider.createDirectory(UnixFileSystemProvider.java:391)
pzy64.pastebinpro W/System.err: at java.nio.file.Files.createDirectory(Files.java:674)
I've implemented the run-time permissions and
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
are all set.
As was first disclosed back in March 2019, you no longer have access by default to arbitrary locations on external storage or removable storage on Android 10+. This includes Environment.getExternalStorageDirectory() and other methods on Environment (e.g., getExternalStoragePublicDirectory().
For Android 10 and 11, you can add android:requestLegacyExternalStorage="true" to your <application> element in the manifest. This opts you into the legacy storage model, and your existing external storage code will work.
Otherwise, your choices are:
Use methods on Context, such as getExternalFilesDir(), to get at directories on external storage into which your app can write. You do not need any permissions to use those directories on Android 4.4+. However, the data that you store there gets removed when your app is uninstalled.
Use the Storage Access Framework, such as ACTION_OPEN_DOCUMENT and ACTION_CREATE_DOCUMENT.
If your content is media, you can use MediaStore to place the media in standard media locations.
For Android 10, you can add
android:requestLegacyExternalStorage="true"
to your element in the manifest. This opts you into the legacy storage model, and your existing external storage code will work. This fix will not work on Android R and higher though, so this is only a short-term fix.
There are more restrictions in Android API 30
you can only write in your app-specific files
File dir_ = new File(context.getFilesDir(), "YOUR_DIR");
dir_.mkdirs();
or in the external storage of your app Android/data
File dir_ = new File(myContext.getExternalFilesDir("FolderName"),"YOUR_DIR");
UPDATE
this answer provided another solution https://stackoverflow.com/a/65744517/8195076
UPDATE
another way is to grant this permission in manifest
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
like this answer https://stackoverflow.com/a/66968986/8195076
This works for me and I think it's functional on Android 10>
ContentResolver resolver = getContentResolver();
ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES + "/Folder Example");
String path = String.valueOf(resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues));
File folder = new File(path);
boolean isCreada = folder.exists();
if(!isCreada) {
folder.mkdirs();
}
You can use public directory to save files in Android 11 like this:
dir = new File(Environment.getExternalStoragePublicDirectory(DIRECTORY_DOCUMENTS).getPath()
+ "/foldername");
if (!dir.exists()) {
dir.mkdir();
Toast.makeText(getApplicationContext(), "not exist", Toast.LENGTH_SHORT).show();
}
Since Q beta 4 it's possible to opt-out of that feature by:
targeting api 28 (or lower)
using requestLegacyExternalStorage manifest attribute:
<manifest ... >
<!-- This attribute is "false" by default on apps targeting Android Q. -->
<application android:requestLegacyExternalStorage="true" ... >
...
</application>
</manifest>
only use
android:requestLegacyExternalStorage="true"
in manifests

How to create custom directory "/storage/emulated/0/TestDir" in Android 10? [duplicate]

I'm unable to create directory in android 10. It's working on devices till android Oreo.
I tried two ways for creating folders.
Using File.mkdir():
File f = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/Pastebin");
if (!f.isFile()) {
if (!(f.isDirectory())) {
success = f.mkdir();
}
Here, the variable success is always false which means the directory isn't created.
Using Files.createDirectory():
File f = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/Pastebin");
if (!f.isFile()) {
if (!(f.isDirectory())) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
try {
Files.createDirectory(Paths.get(f.getAbsolutePath()));
} catch (IOException e) {
e.printStackTrace();
Toast.makeText(getApplicationContext(), R.string.unable_to_download, Toast.LENGTH_LONG).show();
}
} else {
f.mkdir();
}
}
which causes this exception:
pzy64.pastebinpro W/System.err: java.nio.file.AccessDeniedException: /storage/emulated/0/Pastebin
pzy64.pastebinpro W/System.err: at sun.nio.fs.UnixFileSystemProvider.createDirectory(UnixFileSystemProvider.java:391)
pzy64.pastebinpro W/System.err: at java.nio.file.Files.createDirectory(Files.java:674)
I've implemented the run-time permissions and
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
are all set.
As was first disclosed back in March 2019, you no longer have access by default to arbitrary locations on external storage or removable storage on Android 10+. This includes Environment.getExternalStorageDirectory() and other methods on Environment (e.g., getExternalStoragePublicDirectory().
For Android 10 and 11, you can add android:requestLegacyExternalStorage="true" to your <application> element in the manifest. This opts you into the legacy storage model, and your existing external storage code will work.
Otherwise, your choices are:
Use methods on Context, such as getExternalFilesDir(), to get at directories on external storage into which your app can write. You do not need any permissions to use those directories on Android 4.4+. However, the data that you store there gets removed when your app is uninstalled.
Use the Storage Access Framework, such as ACTION_OPEN_DOCUMENT and ACTION_CREATE_DOCUMENT.
If your content is media, you can use MediaStore to place the media in standard media locations.
For Android 10, you can add
android:requestLegacyExternalStorage="true"
to your element in the manifest. This opts you into the legacy storage model, and your existing external storage code will work. This fix will not work on Android R and higher though, so this is only a short-term fix.
There are more restrictions in Android API 30
you can only write in your app-specific files
File dir_ = new File(context.getFilesDir(), "YOUR_DIR");
dir_.mkdirs();
or in the external storage of your app Android/data
File dir_ = new File(myContext.getExternalFilesDir("FolderName"),"YOUR_DIR");
UPDATE
this answer provided another solution https://stackoverflow.com/a/65744517/8195076
UPDATE
another way is to grant this permission in manifest
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
like this answer https://stackoverflow.com/a/66968986/8195076
This works for me and I think it's functional on Android 10>
ContentResolver resolver = getContentResolver();
ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES + "/Folder Example");
String path = String.valueOf(resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues));
File folder = new File(path);
boolean isCreada = folder.exists();
if(!isCreada) {
folder.mkdirs();
}
You can use public directory to save files in Android 11 like this:
dir = new File(Environment.getExternalStoragePublicDirectory(DIRECTORY_DOCUMENTS).getPath()
+ "/foldername");
if (!dir.exists()) {
dir.mkdir();
Toast.makeText(getApplicationContext(), "not exist", Toast.LENGTH_SHORT).show();
}
Since Q beta 4 it's possible to opt-out of that feature by:
targeting api 28 (or lower)
using requestLegacyExternalStorage manifest attribute:
<manifest ... >
<!-- This attribute is "false" by default on apps targeting Android Q. -->
<application android:requestLegacyExternalStorage="true" ... >
...
</application>
</manifest>
only use
android:requestLegacyExternalStorage="true"
in manifests

Settings file in android application

At first, I need an advice how to store private settings in Android application. This settings will contain server/login/password/clientId which are required by application to log into REST API. Settings could be changed in the future, so I need solution which can "easly" update it on all devices - app will be used on +700 client phones. Phones does not have SD Card.
My concept is to store this settings in a hashed text file. I can send prepared setting file to Client, then Client copy it to device memory via USB to the given location - new folder in the main storage where Alarms, DCIM etc are.
Is this a good solution ?
Example methods to check storage - on my test phone both returning false:
public boolean isExternalStorageWritable() {
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
return true;
}
return false;
}
public boolean isExternalStorageReadable() {
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state) || Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
return true;
}
return false;
}
How to save file in android external memory (not SD Card) ?
Great doc here :
https://developer.android.com/training/basics/data-storage/files.html
Please read it to understand that the external storage does not mean SD card for android. And that even a phone without sd card will have a external storage
You need this permission in your manifest :
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
(for me the read permission works as well...)
Then for newer android versions you need to ask the permission
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (PackageManager.PERMISSION_GRANTED != this.checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, READ_EXTERNAL_STORAGE_RIGHT_REQUEST_CODE);
}
}
Then I use getExternalFilesDir to get my application directory to store some files.
https://developer.android.com/reference/android/content/Context.html#getExternalFilesDir(java.lang.String)
But since you don't want removeable storage you may be better off with getFilesDir https ://developer.android.com/reference/android/content/Context.html#getFilesDir()
PS: I just noticed the doc says you don't need any permission to access your application directory since KitKat. May take a look at that later...

sendBroadCast(Intent.ACTION_MEDIA_MOUNTED) not working [duplicate]

Because I want to make sure the MediaStore has the latest information without having to reboot I'd like to trigger the MediaScanner using the popular way I found on SO
context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED,
Uri.parse("file://" + Environment.getExternalStorageDirectory())));
This works fine on my Samsung S2 w/ICS but not on my Nexus 7 w/JellyBean. Logcat shows this on my Nexus 7:
WARN/ActivityManager(480): Permission denied: checkComponentPermission() owningUid=10014
WARN/BroadcastQueue(480): Permission Denial: broadcasting Intent { act=android.intent.action.MEDIA_MOUNTED dat=file:///storage/emulated/0 flg=0x10 } from com.example.foo.bar (pid=17488, uid=10046) is not exported from uid 10014 due to receiver com.android.providers.downloads/.DownloadReceiver
INFO/ActivityManager(480): Start proc com.google.android.music:main for broadcast com.google.android.music/.store.MediaStoreImportService$Receiver: pid=17858 uid=10038 gids={50038, 3003, 1015, 1028}
INFO/MusicStore(17858): Database version: 50
INFO/MediaStoreImporter(17858): Update: incremental Added music: 0 Updated music: 0 Deleted music: 0 Created playlists: 0 Updated playlists: 0 Deleted playlists: 0 Inserted playlist items: 0 Deleted playlist items: 0 Removed orphaned playlist items: 0
The last line sounds encouraging in theory, but the values are always 0 even after new files had been pushed to the SD card (via adb push). On my older device (S2) it does remount the SD card.
I've added the following permissions to my AndroidManifest.xml but it behaves the same as without those permissions:
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
Any ideas/alternatives?
Edit 1:
Note that I don't know any file paths of new or modified or deleted files. I just want to make sure the MediaStore is up-to-date.
Here's the sample code based on CommonsWare's answer:
MediaScannerConnection.scanFile(activity, new String[]{path}, null,
new MediaScannerConnection.OnScanCompletedListener() {
#Override
public void onScanCompleted(final String path, final Uri uri) {
Log.i(TAG, String.format("Scanned path %s -> URI = %s", path, uri.toString()));
}
});
Even though in most of the cases, where one knows the files to be added/updated/etc. to the MediaStore, one should follow CommonsWare's answer, I wanted to post the my solution where I need to do it the rough way because I don't know the file paths. I use this mostly for testing/demoing:
Uri uri = Uri.fromFile(Environment.getExternalStorageDirectory());
activity.sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED, uri));
BTW, no permissions are necessary for either solution.
using the popular way I found on SO
Faking ACTION_MEDIA_MOUNTED broadcasts has never been an appropriate solution IMHO.
Any ideas/alternatives?
Use MediaScannerConnection, such as via its scanFile() static method.
My answer is a little late, but it might help those, who save a new file, and would like to extend the media store by just that file on Android Kitkat: On Android Kitkat the intent ACTION_MEDIA_MOUNTED is blocked for non-system apps (I think, because scanning the whole filesystem is pretty expensive). But it is still possible to use the intent ACTION_MEDIA_SCANNER_SCAN_FILE to add a file to the media store:
File f = new File(path to the file you would like to add to the media store ...);
try {
Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
Uri uri = Uri.fromFile(f);
mediaScanIntent.setData(uri);
sendBroadcast(mediaScanIntent);
} catch(Exception e) {
...
}

Categories