I am working on a big project with a reasonably big code base. What I want to ask you is hints on how to reliably check where I have to provide a solution to adapt for android 6.0 . What i have used is Analyze > Inspect Code , it does a static analysis of the code and than shows missing checks in the section :
Android > Constant and Resource Type Mismatches. It looks somewhat not the right place to find those problems and that is why I am asking to make sure I am using the right thing and am looking at the right thing, plus I am a bit confused because I have parts of code which write files and i am not getting notified about permissions checks there(is it a normal behaviour?!)
public static boolean write(String folderName, String filename, Object objToWrite) {
// serialize cardModel
FileOutputStream file = null;
ObjectOutputStream o = null;
File dirFile = AppController.getInstance().getApplicationContext().getDir(folderName, Context.MODE_PRIVATE);
try {
// Create archiveDir
File mypath = new File(dirFile, filename);
file = new FileOutputStream(mypath, false);
o = new ObjectOutputStream(file);
o.writeObject(objToWrite);
} catch (Exception e) {
e.printStackTrace();
return false;
} finally {
try {
o.close();
file.close();
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
return true;
}
Should I get a warning for Write permission here ?
Another thing that makes me ask is this issue that i have posted on google regarding an Android Studio bug:
Android Studio Bug
Not every write to a file needs a permission - only the one to external storage - this function might also write to the app file space - this needs no permission. Depends on the parameters
Interestingly enough, the definition of what "external storage" vs "internal storage" has changed quite a bit since Android Lollipop. What does this mean?
This call will require you to have storage permissions in the external public directory:
public File getAlbumStorageDir(String albumName) {
// Get the directory for the user's public pictures directory.
File file = new File(Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES), albumName);
if (!file.mkdirs()) {
Log.e(LOG_TAG, "Directory not created");
}
return file;
}
However, if you do the following, you won't need STORAGE Permissions.
public File getAlbumStorageDir(Context context, String albumName) {
// Get the directory for the app's private pictures directory.
File file = new File(context.getExternalFilesDir(
Environment.DIRECTORY_PICTURES), albumName);
if (!file.mkdirs()) {
Log.e(LOG_TAG, "Directory not created");
}
return file;
}
According to Google:
`If none of the pre-defined sub-directory names suit your files, you can instead call getExternalFilesDir() and pass null. This returns the root directory for your app's private directory on the external storage.
Remember that getExternalFilesDir() creates a directory inside a directory that is deleted when the user uninstalls your app. If the files you're saving should remain available after the user uninstalls your app—such as when your app is a camera and the user will want to keep the photos—you should instead use getExternalStoragePublicDirectory().
Regardless of whether you use getExternalStoragePublicDirectory() for files that are shared or getExternalFilesDir() for files that are private to your app, it's important that you use directory names provided by API constants like DIRECTORY_PICTURES. These directory names ensure that the files are treated properly by the system. For instance, files saved in DIRECTORY_RINGTONES are categorized by the system media scanner as ringtones instead of music.
`
Related
Currently, we have an app that we are targeting Android 10 and right now are using the legacy storage API. Our app communicates via Bluetooth sensors and reads and writes raw data in CSV files in a subfolder in the main directory, with that subfolder having subfolders for each user.
I know Android 11 will enforce Scoped Storage. I would like to know, is our use case outside of the Scoped Storage requirement? It appears our use case isn't supported by MediaStore. If not, how would we go about this?
MediaStore APIs are just for media files - images, videos, and audio.
You can store all files in the app's private folder and add an export option to your app (maybe compress the whole structure to an archive). So a user will be able to store or send it wherever they want.
In this case, you need to use FileProvider to expose the file from the private directory.
reads and writes raw data in CSV files in a subfolder in the main directory,
For an Android 11 device you can create your own folders an subfolders in the Documents directory of what you call the 'main folder'.
And for using the MediaStore: you can also write any file to that Documents directory. Well in a subfolder if not directly.
I'm in a similar boat. This may help you get started.
public class FirstFragment extends Fragment {
...
public void fauxMakeCsvSurveyFile() {
File appDir = new File(getContext().getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "Field_data");
appDir.mkdirs();
try {
String storageState = Environment.getExternalStorageState();
if (storageState.equals(Environment.MEDIA_MOUNTED)) {
File file = new File(getContext().getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS) + "/Field_data/" + "OutputFile.csv");
FileOutputStream fos = new FileOutputStream(file);
String text = "Hello, world!";
fos.write(text.getBytes());
fos.close();
}
} catch (IOException e) {
Log.e("IOException", "exception in createNewFile() method");
}
}
...
}
I am trying to use Android's internal helpers to get a path from the system for my file first and then put my files where the system wants. Because tomorrow they might change their minds.
I made a simple program to explore this subject. Here is my code;
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
String path = letsMakeAfile(this, "myFile.txt");
}
private static String letsMakeAfile(Context context, String nameOfFile) {
String strOfFinalPath ="";
//ask the system what path to use...
String strOfContextPath = context.getFilesDir() + "/";
//line above doesnt work without ' + "/" ' or something on the end
//line above records this path: /data/user/0/com.example.filesexperimenting/files/
//this appears to be an invalid path unless "user" is a hidden directory
Log.d("IDIOT", "strOfContextPath: "+ strOfContextPath);
try
{
File file = new File(strOfContextPath, nameOfFile);
if (file.exists() == false) {
file.mkdirs();
//after this line "makes dirs" is file automatically still made and dropped in?
letsMakeAfile(context, nameOfFile);
//I assume not so Ive made a recursive call
}
else
;
//escape recursion....
strOfFinalPath = file.getAbsolutePath();
//Here I record the path where I hope the file is located
Log.d("IDIOT", "strOfFinalPath: "+ strOfFinalPath);
}
catch (Exception e) {
e.printStackTrace();
Log.d("IDIOT", "CATCH ERROR: "+ e.getLocalizedMessage());
}
//runs without a catch
return strOfFinalPath;
}
}
Logcat:
2019-04-09 09:59:22.901 16819-16819/? D/IDIOT: strOfContextPath: /data/user/0/com.example.filesexperimenting/files/
2019-04-09 09:59:22.901 16819-16819/? D/IDIOT: strOfFinalPath: /data/user/0/com.example.filesexperimenting/files
Ultimately I am getting a path of /data/user/0/com.example.filesexperimenting/files/ from context.getFilesDir() which appears to be an invalid path unless "user" is a hidden directory (then why can I see root?). In Device File Explorer under data the only other directories are app, data and local
What am I missing? I'll assume its something with file.makedirs()
Full disclosure, I am a student and there is not a lot out there on this so your replies, while obvious to you at your experience level, should help others. I have some experience with Java and more with C++ but Android is new to me. Thanks in advance!
So, in talking outside of StackExchange it appears that using java.io like I am trying to in the example can cause some problems because of the preset file directories that may be locked or restricted that Java io might not know about.
Android has it's own method openFileOutput(String name, int mode) that has the ability to create the app resource file and directory it belongs in.
Description copied from class: android.content.Context
Actions:
~Open a private file associated with this Context's application package for writing.
~Creates the file if it doesn't already exist.
~No additional permissions are required for the calling app to read or write the returned file.
Params:
~name – The name of the file to open; can not contain path separators.
~mode – Operating mode.
Returns: The resulting FileOutputStream.
Throws: java.io.FileNotFoundException
If you want to be able to navigate to the location of your saved files through the file explorer (either in Android Studio or the Files app on the phone) you should use Context.getExternalFilesDir().
Context.getFilesDir() returns a directory not accessible by anyone BUT the creating application. So if you would like to see what is in this file you would need to open it with the same application that wrote it. IE: Print the contents to the screen after you save it in your app.
Context.getExternalFilesDir() returns a directory completely accessible by anyone and any application. So files created and saved in this external directory can be seen by Android Studio's file explorer as the OP has screenshot or by any application installed on the phone.
What is nice about both of these methods is that as long as you are only accessing files you have created you never need to ask the user for storage permissions Read or Write. If you would like to write to someone else's external files dir then you do.
Source
Check if sdcard is mounted or not.
if (android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED)){
///mounted
}
Get the path of sd card
File dir= new File(android.os.Environment.getExternalStorageDirectory());
walkdir(dir);
ArrayList<String> filepath= new ArrayList<String>();
//list for storing all file paths
public void walkdir(File dir) {
File listFile[] = dir.listFiles();
if (listFile != null) {
for (int i = 0; i < listFile.length; i++) {
if (listFile[i].isDirectory()) {
// if its a directory need to get the files under that directory
walkdir(listFile[i]);
} else {
// add path of files to your arraylist for later use
//Do what ever u want
filepath.add( listFile[i].getAbsolutePath());
}
}
}
}
Try using this:
context.getFilesDir().getParentFile().getPath()
I want to create a private folder for my app data. This folder has to be deleted automatically when the app is removed from the device. According to http://developer.android.com/training/basics/data-storage/files.html, I have to create a private folder. How can I achieve this. My code below. Folder is not deleted when the app is removed.
public static String getAppFilePath(Context context) {
String path = "";
if (Environment.getExternalStorageState().equals(
Environment.MEDIA_MOUNTED)) {
path = Environment.getExternalStorageDirectory()
.getAbsolutePath();
path += "/myFolder";
} else {
ContextWrapper c = new ContextWrapper(context);
path = c.getFilesDir().getPath();
}
File ret = new File(path);
if (!ret.exists()) {
ret.mkdirs();
}
return path;
}
you are doing it wrong.
from the documentation you pointed:
If you want to save files that are private to your app, you can
acquire the appropriate directory by calling getExternalFilesDir()
and passing it a name indicating the type of directory you'd like.
Each directory created this way is added to a parent directory that
encapsulates all your app's external storage files, which the system
deletes when the user uninstalls your app.
For example, here's a method you can use to create a directory for an
individual photo album:
public File getAlbumStorageDir(Context context, String albumName) {
// Get the directory for the app's private pictures directory.
File file = new File(context.getExternalFilesDir(
Environment.DIRECTORY_PICTURES), albumName);
if (!file.mkdirs()) {
Log.e(LOG_TAG, "Directory not created");
}
return file;
}
If none of the pre-defined sub-directory names suit your files, you
can instead call getExternalFilesDir() and pass null. This returns the
root directory for your app's private directory on the external
storage.
Remember that getExternalFilesDir() creates a directory inside a directory that is deleted when the user uninstalls your app. If the
files you're saving should remain available after the user uninstalls
your app—such as when your app is a camera and the user will want to
keep the photos—you should instead use
getExternalStoragePublicDirectory().
so basically you need to use getExternalFilesDirfunction in the example above to get it deleted when app in uninstalled.
according to documentation for getExternalFilesDir()
Parameters
type The type of files directory to return. May be null for
the root of the files directory or one of the following constants for
a subdirectory: DIRECTORY_MUSIC, DIRECTORY_PODCASTS,
DIRECTORY_RINGTONES, DIRECTORY_ALARMS, DIRECTORY_NOTIFICATIONS,
DIRECTORY_PICTURES, or DIRECTORY_MOVIES.
so apart from predefined types you can use null for root of the directory.
In simple words you cannot.
It is not possible as you cannot handle event after uninstalling the App.
I'm having a little problem with my android app.
My app generates a .html file when a "export button" is pressed.
But I can't see the file in my pc or in the Android's Download app. I can only see it in Astro file manager.
That's how I generate and saved my file .
String string = "Hello World"
String filename = "/sdcard/Download/teste.html";
FileOutputStream outputStream;
try {
File file = new File(filename);
boolean newFile = file.createNewFile();
if(!newFile){ //if the file exists I delete it and generate a new file
file.delete();
newFile=file.createNewFile();
}
Context context=getActivity();
FileOutputStream fOut = new FileOutputStream(file,true);
// Write the string to the file
fOut.write(string.getBytes());
/* ensure that everything is
* really written out and close */
fOut.flush();
fOut.close();
}
catch (Exception e) {
e.printStackTrace();
}
I suppose there is a way to visualize this file without the Astro app but I can't find how do this, if someone can help I'll be grateful.
Thanks
First, never hardcode paths. Your path will be wrong on some Android devices. Please use the proper methods on Environment (e.g., getExternalStoragePublicDirectory()) or Context (e.g., getExternalFilesDir()) to get the roots under which you can safely place files.
Beyond that, files that you write to external storage will not be visible to PCs until that file is indexed by MediaScannerConnection, and even then it might require the user to perform some sort of "reload" or "refresh" operation in their file browser to see it.
I have another blog post with more about external storage which may be of use to you.
I'm not trying to write into the external sd at /mnt/sdcard. I'm trying to create a folder for may app files and have them accesible by others.
I have an app called Libra that generates .csv files when exporting data and all af it goes to /Libra/ folder. I want my app to do the same.
As far as I've seen there's external storage which is the sd and internal which is a non public place for the app.
How can I make a dir at the root of the android file system as Libra does ?
If I ask for the external storage I get the following non desired locations :
Environment.getExternalStorageDirectory()
/storage/emulated/0/ (in my Nexus)
/mnt/sdcard (in the emulator)
If I try to make the folder in an absolute path /MyDiary It returns Error creating folder
File folder = new File("/MyDiary");
boolean success = true;
if (!folder.exists()) {
success = folder.mkdir();
}
if (success) {
Toast.makeText(getBaseContext(), "Already exists", Toast.LENGTH_LONG).show();
} else {
Toast.makeText(getBaseContext(), "Error creating directory", Toast.LENGTH_LONG).show();
return;
}
If I try to check if the Libra folder does exists, it says it doesn't exist:
File folder = new File("/Libra");
boolean success = true;
if (!folder.exists()) {
success = folder.mkdir();
}
if (success) {
Toast.makeText(getBaseContext(), "Libra exists", Toast.LENGTH_LONG).show();
}
Note: I've got the manifest permissions to write in external storage.
The root is a ramdisk. And the folder in it some are created by init.rc. Only init.rc have permission to create the folder in the ramdisk.
If you create the folder by init.rc, you still need init.rc help to mount a true stroage device on the new folder.
Repack like The init.rc file regenerated on restart
First of all you need root access Permissions to write.
You can see whether it has created folder there or not using rootexplorer app.
Try this after getting root access.
I don't think that's the right way to create a File in the Android OS.
From http://developer.android.com/training/basics/data-storage/files.html:
If you want to save public files on the external storage [I've read your advice, but I think this way you can access files from multiple apps, if that's what you want], use the getExternalStoragePublicDirectory() method to get a File representing the appropriate directory on the external storage. The method takes an argument specifying the type of file you want to save so that they can be logically organized with other public files, such as DIRECTORY_MUSIC or DIRECTORY_PICTURES. For example:
public File getAlbumStorageDir(String albumName) {
// Get the directory for the user's public pictures directory.
File file = new File(Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES), albumName);
if (!file.mkdirs()) {
Log.e(LOG_TAG, "Directory not created");
}
return file;
}
try first getting superuser permission to your app.Run linux commands for making folder and files into which you want to write data(You can do this with only linux commands)just at 1st run of your app.Then you can write data to files by getting FileOutputStream("filename") object.Leave ur remarks if it worked or not.
partial code for above procedures is
Process p=Runtime.getRuntime().exec("su");
OutputStream o=p.getOutputStream();
o.write("mkdir foldername\n".getBytes());
o.write("cd foldername\n".getBytes());
o.write("cat > filename\n".getBytes());
o.write("exit\n".getBytes());
o.flush();
p.waitFor();