I am trying to save public files on the external storage.
I am following the example from Android Developers page:
https://developer.android.com/training/data-storage/files#PublicFiles
First I tried to create a directory "mydocuments" in the public directory DIRECTORY_DOCUMENTS. The code is as simple as
TextView tv= findViewById(R.id.myTextview);
// Get the directory for the user's public pictures directory.
File documents= Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_DOCUMENTS);
tv.append("\n documents directory=" + documents);
File file = new File(documents, "mydocuments");
tv.append("\n\nDirectory to be created=" + file);
if (file.exists())
tv.append("\n\nFile already exists:" + file.getAbsolutePath());
else{
if (!file.mkdirs())
tv.append("\n\nDirectory not created:" + file.getAbsolutePath());
I added permissions in the manifest file
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE">
</uses-permission>
However when I run this in a device (be virtual or physical) the result is always the same:
documents directory=/storage/emulated/0/Documents
Directory to be created=/storage/emulated/0/Documents/mydocuments
Directory not created:/storage/emulated/0/Download/mydocuments
How to make the Documents directory writeable?
Thank you in advance.
You are #1278 with this problem this year.
For Android 6+ you have to add code to ask the user to confirm the permissions you requested in manifest file.
Google for runtime permissions.
You could also go to the settings of your app and toggle the storage permission to on.
Thanks to #greenapps suggestion I solved my problem. This is the simplest solution I found to write in the Documents directory on the SD card, as explained in
https://developer.android.com/training/permissions/requesting
I have copied some of that here:
1.- First ask for user permissions somewhere at the begining of the code:
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
MY_PERMISSIONS);
where I previously defined the constant
final int MY_PERMISSIONS=1;
2.- Then implement my code above to write on the SD card inside the method
#Override
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
switch (requestCode) {
case MY_PERMISSIONS: {
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// permission was granted, yay! Do the
// task you need to do.
writeSD(); // this is the code to write on the SD card shown above
} else {
// permission denied, boo! Disable the
// functionality that depends on this permission.
tv.append("\n\n No permission given");
}
return;
}
// other 'case' lines to check for other
// permissions this app might request
}
}
This solves the issue for me. The first time it is executed, a dialog opens asking for the permissions. After that It is allowed to read the contents of the sdcard and to write in the public directories DOCUMENTS, PICTURES, DOWNLOADS, etc.
Related
Imagepicker package says
No configuration required - the plugin should work out of the box.
It is no longer required to add
android:requestLegacyExternalStorage="true" as an attribute to the
tag in AndroidManifest.xml, as image_picker has been
updated to make use of scoped storage.
reading images from gallery.
so I think I need to ask some permission from the user as playstore also says this
New package is just working and not asking for any permission.
What permissions I need to explicitly ask
And I don't want to save it on any external directory I just want to upload image to firebase storage
Edit: image picker is not asking any permission from the user is this wrong
Permission needed to read and write files in the android are.
These permission are required to be added to your AndroidManifest.xml
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
In your scenario, you don't need to do anything as this is already handled by the library
https://pub.dev/packages/image_picker
Above mentioned library doesn't save the image in external storage.
Note: Images and videos picked using the camera are saved to your
application's local cache, and should therefore be expected to only be
around temporarily. If you require your picked image to be stored
permanently, it is your responsibility to move it to a more permanent
location.
For more info you can refer to this link
https://guides.codepath.com/android/Accessing-the-Camera-and-Stored-Media#accessing-stored-media
Update : How image picking is handled internally in image_picker for Android
For Gallery pick it opens in inbuild file picker intent using ACTION_GET_CONTENT(about action get content)
When opening file using ACTION_GET_CONTENT - Because the user is involved in selecting the files or directories that your app can access, this mechanism doesn't require any system permissions. You can read more about when permission is needed and when not in google docs
Intent pickImageIntent = new Intent(Intent.ACTION_GET_CONTENT);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
pickImageIntent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
}
pickImageIntent.setType("image/*");
activity.startActivityForResult(pickImageIntent, REQUEST_CODE_CHOOSE_MULTI_IMAGE_FROM_GALLERY);
and copies the result URI in temp file in cache directory and return the path
String extension = getImageExtension(context, uri);
inputStream = context.getContentResolver().openInputStream(uri);
file = File.createTempFile("image_picker", extension, context.getCacheDir());
file.deleteOnExit();
outputStream = new FileOutputStream(file);
if (inputStream != null) {
copy(inputStream, outputStream);
success = true;
}
For Camera library request the camera permission android.permission.CAMERA from the user and save the camera image in app cache directory.
private void handleCaptureImageResult(int resultCode) {
if (resultCode == Activity.RESULT_OK) {
fileUriResolver.getFullImagePath(
pendingCameraMediaUri != null
? pendingCameraMediaUri
: Uri.parse(cache.retrievePendingCameraMediaUriPath()),
new OnPathReadyListener() {
#Override
public void onPathReady(String path) {
handleImageResult(path, true);
}
});
return;
}
// User cancelled taking a picture.
finishWithSuccess(null);
}
This code is as per version image_picker: ^0.8.4+4 code present on their github page - image picker code
You’ll have to add the above-mentioned permissions to your AndroidManifest.xml file. That is still required to access user storage.
I am trying to read a file from the download folder on Android Q by doing this:
File downloadDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
File f = new File(downloadDir, "some-existing-file");
if(!f.exists()) {
throw new IllegalStateException();
}
Uri furi = Uri.fromFile(f);
try {
ParcelFileDescriptor des = getContentResolver().openFileDescriptor(
furi, "r", null);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
I also set android:requestLegacyExternalStorage="true" in the manifest, and requested WRITE_EXTERNAL_STORAGE and READ_EXTERNAL_STORAGE runtime permissions.
The file exists but when trying to open the file descriptor, an exception arises:
java.io.FileNotFoundException: open failed: EACCES (Permission denied).
I read the changes made in Android Q storage, but could not figure out how can I just read the file without having user interaction.
Thanks!
From Android 6 (Marshmallow) some of critical permissions should be granted at runtime by the user so he/she will know what things can your app access.
this link is all what you need
EDIT
add these to manifest
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
then add this code to your oncreate method
if (android.os.Build.VERSION.SDK_INT >= 23 && (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) !=
PackageManager.PERMISSION_GRANTED)) {
ActivityCompat.requestPermissions(
this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
2000);
}
this will popup the user a dialog to agree permissions. if the user agrees onRequestPermissionsResult method will be called, so override onRequestPermissionsResult like this
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED && requestCode == 2000) {
//do what you want with files
}
}
just pay attention that I set request code to 2000. it's optional.
This method was deprecated in API level 29. To improve user privacy, direct access to shared/external storage devices is deprecated. When an app targets Build.VERSION_CODES.Q, the path returned from this method is no longer directly accessible to apps. Apps can continue to access content stored on shared/external storage by migrating to alternatives such as Context#getExternalFilesDir(String), MediaStore, or Intent#ACTION_OPEN_DOCUMENT.
Copied from Android documents.docs
After too many hours of trying to figure out what the hell is going on, I managed to find a solution.
I addded to the manifest:
android:requestLegacyExternalStorage="true"
When rebuilding and reinstalling directly from Android Studio, the build system or android runtime does not register a change in the manifest, and the request for requestLegacyExternalStorage does not register.
I fixed it by completely uninstalling the app: adb uninstall com.example.app and installing it again from android studio.
My app doesn't seem to have writing permissions on my external sd card. I can read from it, but writing does only work on the internal one (sdcard0).
When i try
File f = new File("/storacge/sdcard1/Test/file.mp3");
boolean b = f.delete();
then b is false and the file still exists. But i can play it with MediaPlayer.
I have <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> in my Manifest
What am i doing wrong?
1)You need to ask for runtime permissions
2)Don't assume that the path is /storage/sdcard1. That varies between devices. Request the name via getExternalFilesDirs
If you're working on Android 6.0 or higher you will have to request for permissions exactly where you need them.
Create a method in your activity as such:
public void askForWritePermissions(){
//If android marshmellow or higher
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.M){
//If permissions are not granted
if(ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)!= PackageManager.PERMISSION_GRANTED ){
requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},2);
}
}
}
And call the above method on whichever event you need. After the user grants access or doesn't grant access to the permissions, onRequestPermissionsResult() is called so make sure you handle that method appropriately.
I need to write a file of type .QFS to android external SD card. I need mime type to use documentFile.createFile(mimeType, File name) command to create file.
Can anyone please help me with either mimeType or is there any other way to create a file in android version 5.0 and above in external sd card without using the DocumentFile.createFile command.
This blog says - getExternalFilesDirs() and getExternalCacheDirs() will return directories that we can use on “real” external storage.
Add this in your activity for requesting runtime permission:
if (ActivityCompat.checkSelfPermission(YourActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
requestPermissions(new String[]{ Manifest.permission.WRITE_EXTERNAL_STORAGE},
REQUEST_FOR_STORAGE);//REQUEST_FOR_STORAGE=1111
} else {
//Do your stuff here
}
...
#Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
if(requestCode == REQUEST_FOR_STORAGE){
//Do your stuff here
}
}
Or may be by removing and resetting the sdcard write permission in the Android manifest file.
FYI: Unable to create file on removeable sdcard in Android 5.0.1 and How to access external memory in Android Lollipop and above
Also check this post.
My app stores files in its internal storage directory (/Android/data/com.mycompany.myapp/files, as returned by getFilesDir()), and I would like to allow users to access those files directly from a file management app on their mobile device or the Android File Transfer desktop appplication.
The Storage Options developer guide says:
By default, files saved to the internal storage are private to your application and other applications cannot access them (nor can the user).
"By default" implies that I can change the default permissions to allow users to access these files, but I can't find any documentation of how to do that. Currently the com.mycompany.myapp directory is hidden when I browse the /Android/data directory with a file management app.
I'm currently saving file data like this:
String fileName = "myfile.jpg";
String filePath = getFilesDir() + File.separator + fileName;
FileOutputStream fileStream = new FileOutputStream(filePath);
fileData.writeTo(fileStream); // fileData is a ByteArrayOutputStream
fileStream.close();
I tried setting the permissions of the individual files to world-readable, but this didn't make the directory visible:
FileOutputStream fileStream = this.app.openFileOutput(fileName, Context.MODE_WORLD_READABLE);
I also checked the documentation for the AndroidManifest file and didn't see anything there. Does anyone know how I can do this?
I took a closer look at the result of getFilesDir() vs getExternalFilesDir() and found that getFilesDir() returns /data/data/[packagename]/files while getExternalFilesDir() returns /Android/data/[packagename]/files. I thought the app files I was browsing in /Android/data were the internal storage directories, but now I see that those are actually the external storage directories.
If the internal storage directories are indeed never available to regular users, I wish the documentation said that instead of saying they are not available "by default." To me at least, saying "by default" implies that the default behavior can be changed.
Anyway, I tested and confirmed that if I delete my app, files saved to the getExternalFilesDir() are deleted automatically. So that meets my need for a storage location that is clearly connected with the app (uses an app-specific directory name and is deleted with the app) but is accessible to users for occasional manual file management.
Here's a comparison that might be helpful to someone else reading this:
getFilesDir() - creates an app-specific directory; hidden from users; deleted with the app
getExternalFilesDir() - creates an app-specific directory; accessible to users; deleted with the app
getExternalStoragePublicDirectory() - uses a shared directory (e.g., Music); accessible to users; remains when the app is deleted
I think you are getting confused by the developers documentation, I can't blame you, it's not the best documentation I've ever read. Anyway, Android provides two ways of saving files to the file system:
Using the Internal Storage
Using the External Storage
The Internal Storage is ALWAYS available and is ONLY accessible to your app, no other app in the system can access the files your app saved to this partition.
Now, I think what you need is the External Storage because it is "world-readable" and can be accessed by anyone and any any apps. The downside is that it may not be available since the user could mount it on a USB device - which can be removed by the user at anytime.
You shouldn't use neither MODE_WORLD_WRITEABLE nor MODE_WORLD_READABLE as per the documentation states, because it is very dangerous. Furthermore, these constants have been deprecated since API Level 17
Recommendation
If you need to make your files public, then save them to the External Storage. You will need to declare a permission in your manifest file to avoid your app from crashing everytime it access the External Storage...
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
because the External Storage might not be available you will need to determine the state of it before you perform any operation, otherwise your app will crash...
public enum StorageState{
NOT_AVAILABLE, WRITEABLE, READ_ONLY
}
public StorageState getExternalStorageState() {
StorageState result = StorageState.NOT_AVAILABLE;
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
return StorageState.WRITEABLE;
}
else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
return StorageState.READ_ONLY;
}
return result;
}
There is more information on the documentation and things that you should be aware of. For example, you can give your app ownership of these files so that when your app is uninstalled the system can automatically delete these files. For more info please refer to the documentation on the Android Developers Site
add this to manifest
then add this in your activity:
private static final String RequieredPermission = Manifest.permission.READ_EXTERNAL_STORAGE;
then call HandlePermission() where your require to check for permission, usually in Oncreate method of an activity. These functions are then required:
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults) {
Log.d("amir", "onRequestPermissionsResult: called");
switch (requestCode) {
case REQUEST_PERMISSION_READING_STATE:
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Toast.makeText(MainActivity.this, "Permission Granted!", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(MainActivity.this, "Permission Denied!", Toast.LENGTH_SHORT).show();
}
}
}
private void HandlePermission() {
int permissionCheck = ContextCompat.checkSelfPermission(
this, RequieredPermission);
if (permissionCheck != PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.shouldShowRequestPermissionRationale(this,
RequieredPermission)) {
showExplanationaboutPermission("Permission Needed", "Rationale", RequieredPermission, REQUEST_PERMISSION_READING_STATE);
} else {
requestPermission(RequieredPermission, REQUEST_PERMISSION_READING_STATE);
}
} else {
Toast.makeText(MainActivity.this, "Permission (already) Granted!", Toast.LENGTH_SHORT).show();
}
}
private void showExplanationaboutPermission(String title,
String message,
final String permission,
final int permissionRequestCode) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(title)
.setMessage(message)
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
requestPermission(permission, permissionRequestCode);
}
});
builder.create().show();
}
private void requestPermission(String permissionName, int permissionRequestCode) {
ActivityCompat.requestPermissions(this,
new String[]{permissionName}, permissionRequestCode);
}