I am creating an app that requier an app-specific file that I called "conf.cfg" for example. this file need to be read by my application to create some object etc... the body of the file look like that :
#activation, level, type, regex or array
0, "critic", 0,"\\d{4}\\w\\d{3}"
1, "critic", 1, [word1,word2]
1,"minor", 0,"\\d{2}-\\w{3}-\\d{4}\\s?\\/?\\s?\\d{2}:\\d{2}"
Doing my research I found that there is two type of Storage in android :
Internal storage :
Internal storage is best when you want to be sure that neither the user nor other apps can access your files.
External storae :
External storage is the best place for files that don't require access restrictions and for files that you want to share with other apps or allow the user to access with a computer.
As I want the user to be able to edit/download/upload/USE this file, External Storage seems to be a good choice. However on Developper Android they said :
Caution: The external storage might become unavailable if the user removes the SD card or connects the device to a computer. And the files are still visible to the user and other apps that have the READ_EXTERNAL_STORAGE permission. So if your app's functionality depends on these files or you need to completely restrict access, you should instead write your files to the internal storage.
Caution: Files on external storage are not always accessible, because users can mount the external storage to a computer for use as a storage device. So if you need to store files that are critical to your app's functionality, you should instead store them on internal storage.
As this file need to be always available and is critical to my app's functionality
So... Internal Storage seems to be better. But I need the user to see and be able to use the file. And here I'm stuck.
Anyone has an idea of where and how to put/create this file ?
EDIT : following #greenapps answer
heer is a piece of code I've wrote. I use the getExternalFilesDir(null) command to write and store my file
String folderName = "Innovation";
String confFileName = "conf.txt";
String commentSymbol = "#";
String commentLine = commentSymbol + " activation, level, type , regex or array";
File storage = getExternalFilesDir(null);
File folder = new File(storage, folderName);
File conf = new File(folder, confFileName);
Log.d(TAG, "Folder action!");
if (!folder.exists()) {
if (folder.mkdirs()) {
Log.d(TAG, "Created : " + folder.getAbsolutePath());
} else {
Log.e(TAG, "folder not created!");
}
}
Log.d(TAG, "File action!");
if (!conf.exists()) {
try {
Log.d(TAG, "opening...");
FileOutputStream fos = new FileOutputStream(conf);
fos.write(commentLine.getBytes());
fos.close();
Log.d(TAG, "Created : " + conf.getAbsolutePath());
} catch (IOException e) {
e.printStackTrace();
}
}
if (conf.exists()) {
Log.d(TAG, "File exist at : " + conf.getAbsolutePath());
}
the file is created, as shown by the last log
Created : /storage/emulated/0/Android/data/com.aralex.innovation/files/Innovation/conf.txt
But when I search the file with the native file explorer application of the phone, I can't find it. I can go to the file folder but the folder "Innovation/" is hidden.
This is a problem because I want the file to be visible.
Phone : Samsung s7, s7edge, s9+
Default File Explorer Icon
Default File Explorer Oppened
Well I finally found an answer myself.
On this post Android create folders in Internal Memory #prodev specify that Environment.getExternalStorageDirectory() is a good place, because the file will be accessible and :
note that ExternalStorage in Environment.getExternalStorageDirectory() does not necessarily refers to sdcard, it returns phone primary storage memory
it requires permissions (only for build version >= M) :
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_INTERNAL_STORAGE" />
So here is a code to answer my problem (it ask permission on runtime) :
private ArrayList<Rule> ruleList;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
[...]
// Check for the storage permission before accessing the camera. If the
// permission is not granted yet, request permission.
if (hasPermissions(this, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE)
|| Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
ruleList = createRules();
} else {
requestStoragePermission();
}
}
private boolean hasPermissions(Context context, String... permissions) {
if (context != null && permissions != null) {
for (String permission : permissions) {
Log.d(TAG, "Checking permission : " + permission);
if (ActivityCompat.checkSelfPermission(context, permission) != PackageManager.PERMISSION_GRANTED) {
Log.w(TAG, "not granted : " + permission);
return false;
} else {
Log.d(TAG, "granted : " + permission);
}
}
}
return true;
}
/**
* Handles the requesting of the storage permission. This includes
* showing a "Snackbar" errorMessage of why the permission is needed then
* sending the request.
*/
private void requestStoragePermission() {
Log.w(TAG, "Storage permission is not granted. Requesting permission");
final String[] permissions = new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE};
if (!ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
ActivityCompat.requestPermissions(this, permissions, RC_HANDLE_EXTERNAL_PERM);
return;
}
final Activity thisActivity = this;
View.OnClickListener listener = view -> ActivityCompat.requestPermissions(thisActivity, permissions,
RC_HANDLE_EXTERNAL_PERM);
Snackbar.make(findViewById(android.R.id.content), R.string.permission_storage_rationale,
Snackbar.LENGTH_INDEFINITE)
.setAction(R.string.ok, listener)
.show();
}
#Override
public void onRequestPermissionsResult(int requestCode,
#NonNull String[] permissions,
#NonNull int[] grantResults) {
if (requestCode != RC_HANDLE_EXTERNAL_PERM) {
Log.d(TAG, "Got unexpected permission result: " + requestCode);
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
return;
}
if (grantResults.length != 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED
&& grantResults[1] == PackageManager.PERMISSION_GRANTED) {
Log.d(TAG, "Storage permission granted");
// We have permission
ruleList = createRules();
return;
}
Log.e(TAG, "Permission not granted: results len = " + grantResults.length +
" Result code = " + (grantResults.length > 1 ? grantResults[0] + " " + grantResults[1] : grantResults.length > 0 ? grantResults[0] : "(empty)"));
DialogInterface.OnClickListener listener = (dialog, id) -> finish();
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Assisting Tool")
.setMessage(R.string.no_storage_permission)
.setPositiveButton(R.string.ok, listener)
.show();
}
private ArrayList<Rule> createRules() {
Log.d(TAG, "=========================READING FILE======================");
ArrayList<Rule> ruleList = new ArrayList<>();
String folderName = "Innovation";
String confFileName = "conf.txt";
String commentSymbol = "#";
String commentLine = commentSymbol + " activation, level, type , regex or array";
File storage = Environment.getExternalStorageDirectory();
File folder = new File(storage, folderName);
File conf = new File(folder, confFileName);
Log.d(TAG, "Folder action!");
if (!folder.exists()) {
if (folder.mkdirs()) {
Log.d(TAG, "Created : " + folder.getAbsolutePath());
} else {
Log.e(TAG, "folder not created!");
}
}
Log.d(TAG, "File action!");
if (!conf.exists()) {
try {
Log.d(TAG, "opening...");
FileOutputStream fos = new FileOutputStream(conf);
fos.write(commentLine.getBytes());
fos.close();
Log.d(TAG, "Created : " + conf.getAbsolutePath());
} catch (IOException e) {
e.printStackTrace();
}
}
if (conf.exists()) {
Log.d(TAG, "File exist at : " + conf.getAbsolutePath());
} else {
Log.e(TAG, "The file doesn't exist...");
}
}
Now it create a app-specific file
/storage/emulated/0/Innovation/conf.txt
that is accessible by user !
So external storage.
No internal as file explorers have no access to your apps private internal memory.
You could use getExternalFilesDir(null) as then you dont need read and write permissions
Related
TLDR: Why isn't my app asking for runtime permissions despite I have written code for it?
I am saving an image into the storage, on button click by doing something like this:
// Function triggered when save button is pressed
private void saveImage(){
// Asking Runtime permissions for API level 23 and higher
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
if(checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED){
finallySave();
} else {
requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},STORAGE_WRITE_REQUEST_CODE);
}
} else {
// Do not ask permissions in the runtime for API level 22 and lower
finallySave();
}
}
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == STORAGE_WRITE_REQUEST_CODE) {
if (grantResults.size == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
finallySave()
} else {
Toast.makeText(this, "Permission denied", Toast.LENGTH_SHORT).show()
}
}
}
private void finallySave(){
try {
File outFile = new File(imagesDir, "Abc " + System.currentTimeMillis() + ".png");
FileOutputStream outputStream = new FileOutputStream(outFile);
bitmaps[0].compress(Bitmap.CompressFormat.PNG, 100, outputStream);
outputStream.flush();
outputStream.close();
scanFile(outFile, "image/png");
} catch (Exception unused) {
}
}
The problem is, sometimes the image is not saved and the following error is shown:
java.io.FileNotFoundException: /storage/emulated/0/Abc/Abc 1601370822977.png (Permission denied)
I found out that this error is thrown when the application is not asking "storage write" permissions on the runtime. Which is the very origin of the problem that I am facing. My app is not asking for runtime permissions in some cases despite I have written the code for it. Replicating this bug is also very difficult because many times the saving operation is carried out successfully.
I am sure that my code for saving images works fine because sometimes it works (this happens when my app asks for runtime permissions) and sometimes it doesn't.
P.S: I encountered this problem when I had reinstalled my app (after uninstalling).
EDIT: imagesDir is a global variable I am assigning is as follows:
imagesDir = getImageDir(); // in onCreate
private File getImageDir() {
File directory = new File(
Environment.getExternalStorageDirectory() + "/Abc");
if (!directory.exists()) {
if (!directory.mkdirs()) {
Toast.makeText(this, "Could not create directory", Toast.LENGTH_SHORT).show();
return null;
}
}
scanFile(directory, "*/*");
return directory;
}
I want to delete all files from Download folder
I am trying this approach -
File mydir = new File(String.valueOf(Environment.getExternalStoragePublicDirectory("Download")));
File lister = mydir.getAbsoluteFile();
System.out.println("Total files: " + lister.list().length);
for (String list : lister.list()) {
File f = new File(lister, list);
if (f.delete())
System.out.println(list + " is Deleted!");
else
System.out.println(list + " not deleted!");
}
It doesn't work, f.delete is returning false.
I have already looked at many such questions on SO, most of them suggest the use of delete() or getCanonicalFile().delete(). This is just not working.
manifest-
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
try this, this will delete all files under filePath directory
File file = new File(filePath);
if (file.isDirectory()) {
String[] children = file.list();
if (children != null) {
for (String aChildren : children) {
boolean isDelete = new File(file, aChildren).delete();
if (isDelete)
System.out.println(aChildren + " is Deleted!");
else
System.out.println(aChildren + " not deleted!");
}
}
}
Fixed it.
My app was requesting the user for READ permission only, not WRITE.
Deleting any file requires WRITE permission. So i changed READ to WRITE, it worked!
if (ContextCompat.checkSelfPermission(MainActivity.this
, Manifest.permission.**WRITE**_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED)
{
// ...
}
Not an answer but more robust code:
File dir = Environment.getExternalStoragePublicDirectory("Download");
if ( !dir.canWrite() )
{
Toast.makeText(context, "Can not write in directory\n\n" + dir.getAbsolutePath(), Toast.LENGTH_LONG).show();
return;
}
File files [] = dir.listFiles();
if ( files==null )
{
Toast.makeText(context, "Could not listFiles() for\n\n" + dir.getAbsolutePath(), Toast.LENGTH_LONG).show();
return;
}
for ( File file : files)
{
if ( file.isDirectory())
continue;
if ( ! file.delete())
{
Toast.makeText(context, "Could not delete\n\n" + file.getAbsolutePath(), Toast.LENGTH_LONG).show();
break;
}
}
Please try.
I am trying to provide a functionality in my app that the media or storage files used in my application can be moved to SD card by the user.
I am trying to use the code as described in below link
http://www.java-samples.com/showtutorial.php?tutorialid=1523
But I get a permission exception. When I searched for getting that permission, I see that I have to root the device. I don't want to root my device, as it is illegal, no? Is there any android device model that comes rooted from the beginning itself from the manufacturer?
Earlier also I used to see a "Move To SD Card" option in the app settings, but I don't see that option any more. I also saw that most of the file browser applications installed in my device are unable to create a folder on the SD card,
Please share some light on what's the best recommended way to implement this feature. We are supporting android 4.4 to 8.0
Yes writing to the sd card is blocked in modern Android versions.
Mostly you have read acces to the whole sd card.
Writing only to one app specific directory which if you are lucky is available in the second item returned by getExternalFilesDirs().
If you want to write to the whole sd card then use the Storage Access Framework.
For instance Intent.ACTION_OPEN_DOCUMENT_TREE.
If you haven't done so already, you will need to give your app the correct permission to write to the SD Card by adding the line below to your Manifest:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
Getting runtime permissions
You should be checking if the user has granted permission of external storage by using:
if (checkSelfPermission(android.Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
Log.v(TAG,"Permission is granted");
//File write logic here
return true;
}
If not, you need to ask the user to grant your app a permission:
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_CODE);
Of course these are for marshmallow devices only so you need to check if your app is running on Marshmallow:
if (Build.VERSION.SDK_INT >= 23) {
//do your check here
}
Be also sure that your activity implements OnRequestPermissionResult
The entire permission looks like this:
public boolean isStoragePermissionGranted() {
if (Build.VERSION.SDK_INT >= 23) {
if (checkSelfPermission(android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
== PackageManager.PERMISSION_GRANTED) {
Log.v(TAG,"Permission is granted");
return true;
} else {
Log.v(TAG,"Permission is revoked");
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
return false;
}
}
else { //permission is automatically granted on sdk<23 upon installation
Log.v(TAG,"Permission is granted");
return true;
}
}
Permission result callback:
#Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if(grantResults[0]== PackageManager.PERMISSION_GRANTED){
Log.v(TAG,"Permission: "+permissions[0]+ "was "+grantResults[0]);
//resume tasks needing this permission
}
}
Also
SD Card directory is /sdcard but you shouldn't be hard coding it. Instead, make a call to Environment.getExternalStorageDirectory() to get the directory:
File sdDir = Environment.getExternalStorageDirectory();
Code to write into external storage
Source
/** Method to check whether external media available and writable. This is adapted from
http://developer.android.com/guide/topics/data/data-storage.html#filesExternal */
private void checkExternalMedia(){
boolean mExternalStorageAvailable = false;
boolean mExternalStorageWriteable = false;
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
// Can read and write the media
mExternalStorageAvailable = mExternalStorageWriteable = true;
} else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
// Can only read the media
mExternalStorageAvailable = true;
mExternalStorageWriteable = false;
} else {
// Can't read or write
mExternalStorageAvailable = mExternalStorageWriteable = false;
}
}
/** Method to write ascii text characters to file on SD card. Note that you must add a
WRITE_EXTERNAL_STORAGE permission to the manifest file or this method will throw
a FileNotFound Exception because you won't have write permission. */
private void writeToSDFile(){
// Find the root of the external storage.
// See http://developer.android.com/guide/topics/data/data- storage.html#filesExternal
File root = android.os.Environment.getExternalStorageDirectory();
// See http://stackoverflow.com/questions/3551821/android-write-to-sd-card-folder
File dir = new File (root.getAbsolutePath() + "/download");
dir.mkdirs();
File file = new File(dir, "myData.txt");
try {
FileOutputStream f = new FileOutputStream(file);
PrintWriter pw = new PrintWriter(f);
pw.println("Hi , How are you");
pw.println("Hello");
pw.flush();
pw.close();
f.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
Log.i(TAG, "******* File not found. Did you" +
" add a WRITE_EXTERNAL_STORAGE permission to the manifest?");
} catch (IOException e) {
e.printStackTrace();
}
}
/** Method to read in a text file placed in the res/raw directory of the application. The
method reads in all lines of the file sequentially. */
private void readRaw(){
InputStream is = this.getResources().openRawResource(R.raw.textfile);
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr, 8192); // 2nd arg is buffer size
// More efficient (less readable) implementation of above is the composite expression
/*BufferedReader br = new BufferedReader(new InputStreamReader(
this.getResources().openRawResource(R.raw.textfile)), 8192);*/
try {
String test;
while (true){
test = br.readLine();
// readLine() returns null if no more lines in the file
if(test == null) break;
}
isr.close();
is.close();
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
In addition to that
Shared storage may not always be available, since removable media can be ejected by the user.
Media state can be checked using getExternalStorageState(File).
There is no security enforced with these files.
I am trying to copy created database sqlite file from android memory to an sdcard but I am getting "failed to copy" error. Is there a way to create the database sqlite file so it can be easily copied? Do I need to set permissions anywhere on the device?
Yes you do need permissions and how depends upon the API if less than 23 then you need to have
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
in your manifest (AndroidManifest.xml).
If 23 or greater then you need to specifically request Accsess. e.g. In an activity (I have this in MainActivity so it is always checked) have :-
if(Build.VERSION.SDK_INT >= 23) {
ExternalStoragePermissions.verifyStoragePermissions(this);
}
ExternalStoragePermissions Class is :-
class ExternalStoragePermissions {
public int API_VERSION = Build.VERSION.SDK_INT;
private static final int REQUEST_EXTERNAL_STORAGE = 1;
private static String[] PERMISSIONS_STORAGE = {
//Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
};
public static final String THISCLASS = ExternalStoragePermissions.class.getSimpleName();
public ExternalStoragePermissions() {}
// Note call this method
public static void verifyStoragePermissions(Activity activity) {
int permission = ActivityCompat.checkSelfPermission(
activity,
Manifest.permission.WRITE_EXTERNAL_STORAGE);
if(permission != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(
activity,
PERMISSIONS_STORAGE,
REQUEST_EXTERNAL_STORAGE
);
}
}
}
This prompt the user to allow access.
PS I have both encoded so that the app copes with both situations.
I do actually have a working backup/restore where a backup is a copy. However, it's quite long winded and perhaps a little convoluted. As I've tried to handle many situations. I actually backup to a folder created in the downloads folder.
PS if you have something like SQLite Manager, you can even copy the file to a PC and open it (connect). I use this for testing queries etc. You can even modify the database and copy it back (well I can, very easily, because of the restore side). I've even gone to the lengths of seeing what happens if I copy a non-SQLite file and restore from that (basically it copes but does require the App to be closed and then re-opened to circumvent unpredictable results).
As an example of the length/convolution this is one of the first checks I do :-
// External Storage must be mounted.
String chkmnt = Environment.getExternalStorageState();
if(!(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED))) {
switch (Environment.getExternalStorageState()) {
case Environment.MEDIA_SHARED : {
errorlist.add(
"Although External Storage is present." +
" It cannot be used as it's in use via USB." +
"\nDisconnect the USB cable and then try again."
);
break;
}
case Environment.MEDIA_REMOVED : {
errorlist.add(
"External Storage is not present." +
"\nInsert an SC Card."
);
break;
}
case Environment.MEDIA_EJECTING : {
errorlist.add(
"External Storage is being ejected." +
"\nRe-insert the SD Card."
);
break;
}
case Environment.MEDIA_NOFS : {
errorlist.add(
"External Storage is blank or does not have the correct" +
" filesystem present." +
"\nUse a valid SDCard."
);
break;
}
case Environment.MEDIA_BAD_REMOVAL : {
errorlist.add(
"External Storage was removed incorrectly." +
"\nRe-insert the SD Card, if this fails then" +
" try restarting the device."
);
break;
}
case Environment.MEDIA_CHECKING : {
errorlist.add(
"External Storage is unavailable as it is being checked." +
"\nTry again."
);
}
case Environment.MEDIA_MOUNTED_READ_ONLY : {
errorlist.add(
"External Storage is READ ONLY." +
"\nInsert an SD card that is not protected."
);
}
case Environment.MEDIA_UNKNOWN : {
errorlist.add(
"External Storage state is UNKNOWN." +
"\ntry a different SD Card."
);
}
case Environment.MEDIA_UNMOUNTABLE : {
errorlist.add(
"External Storage cannot be mounted." +
"\nTry re-inserting the SD Card or using a different SD Card."
);
}
case Environment.MEDIA_UNMOUNTED : {
errorlist.add(
"External Storage is not mounted." +
"\nTry re-inserting the SD Card or using a different SD Card."
);
}
default: {
errorlist.add(
"Undefined Error"
);
}
}
this.errorcode = UNMOUNTED;
return;
} else {
this.mounted = true;
}
// Get the require directory and specified sub directory
File dir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),subdirectory);
this.directory = dir.getPath();
For the backup itself I use :-
/**************************************************************************
* method saveDB save a file copy of the Database
*/
private void saveDB() {
busy.show();
errlist.clear();
confirmaction = true;
String dbfilename = this.getDatabasePath(
DBConstants.DATABASE_NAME).getPath();
dbfile = new File(dbfilename);
backupfilename = directory.getText().toString() +
"/" +
backupfullfilename.getText().toString();
new Thread(new Runnable() {
#Override
public void run() {
try {
FileInputStream fis = new FileInputStream(dbfile);
OutputStream backup = new FileOutputStream(backupfilename);
//byte[] buffer = new byte[32768];
int length;
while((length = fis.read(buffer)) > 0) {
backup.write(buffer, 0, length);
}
backup.flush();
backup.close();
fis.close();
}
catch (IOException e) {
e.printStackTrace();
errlist.add("Database backup failed with an IO Error. Error Message was " +
e.getMessage() +
"/n/tFile Name was " +
backupfilename);
confirmaction = false;
}
runOnUiThread(new Runnable() {
#Override
public void run() {
busy.dismiss();
AlertDialog.Builder dbbackupresult = new AlertDialog.Builder(context);
dbbackupresult.setCancelable(true);
if(confirmaction) {
dbbackupresult.setTitle("DB Data Backed up OK.");
dbbackupresult.setMessage("DB Data successfully saved in file \n\t" +
backupfilename );
} else {
dbbackupresult.setTitle("DB Backup Failed.");
String emsg = "";
for(int i = 0; i < errlist.size(); i++) {
emsg = emsg + errlist.get(i);
}
}
dbbackupresult.setPositiveButton("OK", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
}
}).show();
}
});
}
}).start();
Note these are extracts and may make references to code that is not included. e.g. busy is a progressdialog, directory is an EditText that is populated when the check routine is called
I tried everything to write on external SD card on Android 6.0, but I am not able to write on it.
I did research on stackoverflow and found lot of things but none works. Here is my code
String extPath = System.getenv("SECONDARY_STORAGE") + "/Android/data/com.gvm.externalstorage.externalstoragetest/";
File file = new File(extPath,"myFiles");
if (!file.exists()) {
boolean dirResult = file.mkdirs();
Log.e("Directory Exist", dirResult + " Directory created");
} else {
Log.e("Directory Exist", "Exist");
Log.e("Direcotry Path",file.getAbsolutePath());
}
//String displayname = fileName.replace("%20", " ");
File outputFile = new File(file, "mytest5.txt");
outputFile.createNewFile();
This code works on Android 5.0 but not on Android 6.0.
Then I tried this path as well, and that gives me permission error, I have set all permission and managed code for runtime permission as well.
/mnt/media_rw/6AC9-083B
File write failed: java.io.IOException: open failed: EACCES (Permission denied)
If anyone can help me it would be great as I am trying this since last 3 days.
Thanks,
Anvesh
After long hard work I figured out a solution. In Android 6.0 it's not going to give you SD Card path always using this:
System.getenv("SECONDARY_STORAGE")
or this
Environment.getExternalStorageDirectory()
So I retrieved external SD Card path using this
File[] fs = context.getExternalFilesDirs(null);
String extPath = "";
// at index 0 you have the internal storage and at index 1 the real external...
if (fs != null && fs.length >= 2)
{
extPath = fs[1].getAbsolutePath();
Log.e("SD Path",fs[1].getAbsolutePath());
}
Rest everything will remain same for permission and all.
Thanks to those who helped me.
From API 23+(6.0) you need to request the read/write permissions even if they are already in your manifest known as Requesting Permissions at Run Time.
from docs
Beginning in Android 6.0 (API level 23), users grant permissions to
apps while the app is running, not when they install the app. This
approach streamlines the app install process, since the user does not
need to grant permissions when they install or update the app. It also
gives the user more control over the app's functionality; for example,
a user could choose to give a camera app access to the camera but not
to the device location. The user can revoke the permissions at any
time, by going to the app's Settings screen.
java
// Storage Permissions
private static final int REQUEST_EXTERNAL_STORAGE = 1;
private static String[] PERMISSIONS_STORAGE = {
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
};
/**
* Checks if the app has permission to write to device storage
*
* If the app does not has permission then the user will be prompted to grant permissions
*
* #param activity
*/
public static void verifyStoragePermissions(Activity activity) {
// Check if we have write permission
int permission = ActivityCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE);
if (permission != PackageManager.PERMISSION_GRANTED) {
// We don't have permission so prompt the user
ActivityCompat.requestPermissions(
activity,
PERMISSIONS_STORAGE,
REQUEST_EXTERNAL_STORAGE
);
}
}
AndroidManifest.xml
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
I think u should check your app permission first, to make sure your storage permission has been turned on.
If there's no storage permission:
Please check if u use this permission in your AndroidManifest
<uses-permission android:name="android.permission.STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
If the storage permission has been turned off:
Please check your runtime permission, maybe u can refer to this code
private void checkPermissions() {
if ( Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
return;
}
final List<String> permissionsList = new ArrayList<String>();
permissionsList.add(Manifest.permission.ACCESS_COARSE_LOCATION);
permissionsList.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
permissionsList.add(Manifest.permission.WRITE_CALENDAR);
permissionsList.add(Manifest.permission.READ_PHONE_STATE);
int permissionCheckLocation = ContextCompat.checkSelfPermission(IntroductionActivity.this, Manifest.permission.ACCESS_COARSE_LOCATION);
int permissionCheckStorage = ContextCompat.checkSelfPermission(IntroductionActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE);
int permissionCheckCalendar = ContextCompat.checkSelfPermission(IntroductionActivity.this, Manifest.permission.WRITE_CALENDAR);
int permissionCheckPhoneState = ContextCompat.checkSelfPermission(IntroductionActivity.this, Manifest.permission.READ_PHONE_STATE);
boolean locationPermission=permissionCheckLocation == PackageManager.PERMISSION_GRANTED?true:false;
boolean storagePermission=permissionCheckStorage == PackageManager.PERMISSION_GRANTED?true:false;
boolean calendarPermission=permissionCheckCalendar == PackageManager.PERMISSION_GRANTED?true:false;
boolean phoneStatePermission=permissionCheckPhoneState == PackageManager.PERMISSION_GRANTED?true:false;
boolean shouldShowLocationPermission=ActivityCompat.shouldShowRequestPermissionRationale(IntroductionActivity.this, Manifest.permission.ACCESS_COARSE_LOCATION);
boolean shouldShowStoragePermission=ActivityCompat.shouldShowRequestPermissionRationale(IntroductionActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE);
boolean shouldShowCalendarPermission=ActivityCompat.shouldShowRequestPermissionRationale(IntroductionActivity.this, Manifest.permission.WRITE_CALENDAR);
boolean shouldShowPhoneStatePermission=ActivityCompat.shouldShowRequestPermissionRationale(IntroductionActivity.this, Manifest.permission.READ_PHONE_STATE);
if (permissionCheckLocation == PackageManager.PERMISSION_GRANTED && permissionCheckStorage == PackageManager.PERMISSION_GRANTED
&& permissionCheckCalendar == PackageManager.PERMISSION_GRANTED && permissionCheckPhoneState == PackageManager.PERMISSION_GRANTED){
return;
}else if(((!locationPermission&&!shouldShowLocationPermission)||(!storagePermission&&!shouldShowStoragePermission)
||(!calendarPermission&&!shouldShowCalendarPermission)||(!phoneStatePermission&&!shouldShowPhoneStatePermission))&&appContext.localCheckPermission){
showMessageOKCancel("You need to allow access these permissions",
new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, Uri.parse("package:" + getPackageName()));
startActivityForResult(intent, 1);
}
});
}else{
ActivityCompat.requestPermissions(IntroductionActivity.this, permissionsList.toArray(new String[permissionsList.size()]), 0);
}
}
If still have problem, please try to change your file path :
String fileName="mytest5.txt";
File folder = new File(Environment.getExternalStorageDirectory().getPath() + "/com.gvm.externalstorage.externalstoragetest/");
if (!folder.exists()) {
try {
folder.mkdirs();
} catch (Exception e) {
e.printStackTrace();
System.out.println("Default Save Path Creation Error:" + folder);
}
}
File logFile = new File(folder, fileName);
if (!logFile.exists()) {
try {
logFile.createNewFile();
} catch (IOException e) {
e.printStackTrace();
System.out.println("Default Save Path Creation Error:" + logFile);
}
}
Best regards. I hope this can help u
#Anvesh Another reliable method i'm using:
/**
* Get external storage path use reflect on android 6.0 device.
* Source code:
* https://github.com/android/platform_frameworks_base/blob/master/core/java/android/os/storage/StorageVolume.java
*
* #param removable the sdcard can remove or not, true means external sdcard, false means
* internal sdcard.
* #return path of sdcard we want
*/
public static String getStoragePath(boolean removable) {
WinZipApplication application = WinZipApplication.getInstance();
Context mContext = application.getApplicationContext();
StorageManager mStorageManager = (StorageManager) mContext.getSystemService(Context.STORAGE_SERVICE);
Class<?> storageVolumeClazz = null;
try {
storageVolumeClazz = Class.forName("android.os.storage.StorageVolume");
Method getVolumeList = mStorageManager.getClass().getMethod("getVolumeList");
Method getPath = storageVolumeClazz.getMethod("getPath");
Method isRemovable = storageVolumeClazz.getMethod("isRemovable");
Object result = getVolumeList.invoke(mStorageManager);
final int length = Array.getLength(result);
for (int i = 0; i < length; i++) {
Object storageVolumeElement = Array.get(result, i);
String path = (String) getPath.invoke(storageVolumeElement);
boolean mRemovable = (Boolean) isRemovable.invoke(storageVolumeElement);
if (removable == mRemovable) {
return path;
}
}
} catch (Exception e) {
return null;
}
return null;
}
After a lot of research I found ABSOLUTE SOLUTION. IT WORKS.
public boolean checkStorage() {
File[] fs = con.getExternalFilesDirs(null);
if (fs.length == 2)
return true;
else
return false;
}