I want the TXT files (and the folder containing them) generated by my App to be accessible by my phone's file browser, or even from a PC when I plug the phone via USB. Android's official documentation describes the different types of storing depending if it is "internal" or "external" (Google's terminology). And of course, I don't want those TXT files to be deleted when I uninstall the App.
To avoid any confusions:
The fact that I want to hardcode the file location in the App, doesn't imply that I would use neccessarily the complete absolute filepath as a parameter. What i mean is the relative filepath will be hardcoded (same task, same file, same place).
...that said...
The app is for personal use (I mean to use it myself), so fulfilling Google Play Store's requirements is not a priority for now.
Code example (what I tried)
try {
// Dedicated folder would be better...
// ...but not even in the root folder works...
// FIXME T_T
FileWriter writer = new FileWriter("/storage/emulated/0/myfile.txt");
writer.write(my_string_for_file);
writer.flush();
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
// ...always throws FileNotFoundException with Permission DENIED
It would be Python on PC, stuff's done in 5 minutes!! 3 hours and I still can't figure it out?? I wanna cry...
So, I want to save some output always in the same file, in the same path. And I want that TXT file to be accessible from my PC when I plug it in via USB, as well as from my phone's file browser. In Python, for example, for a PC app, I would achieve this with 3 single lines of code! It can't be much harder in Android, right? (or so I hope)
I am aware that, for what I describe here, it is technically "documents and other kind of files", and what Google or Android Developers consider "external storage" (but what Android Users consider Internal Storage!). Well, Android Developers Documentation has a section for this in this link.
But the example's Code Snippet is overkill for my needs: I don't want the User to choose filepath: I want to hardcode the TXT files' paths into /storage/emulated/0/myapp/
directory. Instead, the example seems to provide some way to open some kind of "Save As" file dialog, which I don't want.
According to this article, it suggests me to use Environment.getExternalStoragePublicDirectory
, and if I look to this method's documentation it says this method is deprecated from API 28. I have been looking other docs about the subject, but I am still confused about how to proceed. Supposing that I already have the text I want to write into a String
variable:
How would I make sure that files would be written to some dedicated folder like
/storage/emulated/0/myapp
or/storage/emulated/0/myfolder
instead of the root folder/storage/emulated/0
? WillgetExternalFilesDir()
return/storage/emulated/0
or is there a better method?Which is the simplest code to just write the file containing the
String
variable's text with a hardcoded filename into the dedicated directory, but it will still persist after desinstalling the app?
If it is either /storage/emulated/0
or /storage/emulated/0/Documents
, any of both is fine by me. The most important thing is that I can access those files from outside my App that is gonna make them and, if possible, that uninstalling the mentioned App won't delete them.
CodePudding user response:
Context.getExternalFilesDir(null)
is the new alternative to Environment.getExternalStoragePublicDirectory()
but as you mentioned this will return a directory that will get deleted once you uninstall the app. For your use case you need to use MediaStore
and I completely agree with you: this has become terribly complicated (i think android has done this in order to strengten security and so that other apps can more easyly browse the files you place in shared directories).Here are some methods to do what you want using Media Store:
private void saveTxtFile(String fileName, String fileContent, Context context) throws IOException {
OutputStream fos;
try{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
ContentValues values = new ContentValues();
values.put(MediaStore.MediaColumns.DISPLAY_NAME, fileName); //file name
values.put(MediaStore.MediaColumns.MIME_TYPE, "text/plain"); //file extension, will automatically add to file
values.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DOCUMENTS "/yourFolder/"); //end "/" is not mandatory
Uri uri = context.getContentResolver().insert(MediaStore.Files.getContentUri("external"), values); //important!
fos = context.getContentResolver().openOutputStream(uri);
} else {
String docsDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS).toString();
File file = new File(docsDir, fileName);
fos=new FileOutputStream(file);
}
try {
fos.write(fileContent.getBytes());
} finally {
fos.close();
}
}catch (IOException e){
e.printStackTrace();
}
}
you will also need the following method to be able to acertain if the file already exists. I made it it so it returns an Uri but you should change that to suit your needs
private Uri isFilePresent(String fileName, Context context) {
Uri contentUri = MediaStore.Files.getContentUri("external");
String selection = MediaStore.MediaColumns.RELATIVE_PATH "=?";
String[] selectionArgs = new String[]{Environment.DIRECTORY_DOCUMENTS "/yourFolder/"};
Cursor cursor = context.getContentResolver().query(contentUri, null, selection, selectionArgs, null);
Uri uri = null;
if (cursor.getCount() == 0) {
Toast.makeText(context, "No file found in " Environment.DIRECTORY_DOCUMENTS "yourFolder", Toast.LENGTH_LONG).show();
} else {
while (cursor.moveToNext()) {
String file = cursor.getString(cursor.getColumnIndex(MediaStore.MediaColumns.DISPLAY_NAME));
if (fileName.equals(fileName)) {
long id = cursor.getLong(cursor.getColumnIndex(MediaStore.MediaColumns._ID));
uri = ContentUris.withAppendedId(contentUri, id);
break;
}
}
}
return uri;
}
please do not forget to add the corresponding permissions to the manifest fro this to work:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
btw this answer does something similar
CodePudding user response:
Try this code, it saves data to file, but on internal storage. It's more convenient, because a lot of devices now don't have external card. You can extract that file at least using android emulator tools and save it to your PC:
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
public class FileManager {
public static void writeToFile(String fileName, String data, Context context) {
try {
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(context.openFileOutput(fileName, Context.MODE_PRIVATE));
outputStreamWriter.write(data);
outputStreamWriter.close();
} catch (FileNotFoundException e) {
LLog.e(e, "File not found");
} catch (IOException e) {
LLog.e(e, "File write failed");
}
}
public static void addToFile(String fileName, String data, Context context) {
try {
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(context.openFileOutput(fileName, Context.MODE_APPEND));
outputStreamWriter.append(data);
outputStreamWriter.close();
} catch (FileNotFoundException e) {
LLog.e(e, "File not found");
} catch (IOException e) {
LLog.e(e, "File write failed");
}
}
public static String readFromFile(String fileName, Context context) {
String resultStr = "";
try {
InputStream inputStream = context.openFileInput(fileName);
if (inputStream != null) {
InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String receiveString = "";
StringBuilder stringBuilder = new StringBuilder();
while ((receiveString = bufferedReader.readLine()) != null) {
stringBuilder.append(receiveString);
}
inputStream.close();
bufferedReader.close();
resultStr = stringBuilder.toString();
}
} catch (FileNotFoundException e) {
LLog.e(e, "File not found");
} catch (IOException e) {
LLog.e(e, "File write failed");
}
return resultStr;
}
public static String readFromFileInRawFolder(String fileName, Context context) {
String resultStr = "";
try {
InputStream inputStream = context.getResources().openRawResource(
context.getResources().getIdentifier(fileName, "raw", context.getPackageName()));
InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String receiveString;
StringBuilder stringBuilder = new StringBuilder();
while ((receiveString = bufferedReader.readLine()) != null) {
stringBuilder.append(receiveString);
}
inputStream.close();
bufferedReader.close();
resultStr = stringBuilder.toString();
} catch (FileNotFoundException e) {
LLog.e(e, "File not found");
} catch (IOException e) {
LLog.e(e, "File write failed");
}
return resultStr;
}
public static boolean deleteFile(String fileName, Context context) {
File dir = context.getFilesDir();
File file = new File(dir, fileName);
return file.delete();
}
}