I want to be able to create a folder for my app within "Internal Storage/" (on startup, if required) and later, within this folder, create any files (.txt) the user creates in the app.
These files must remain if the app is uninstalled, so use of getExternalFilesDir() - while it works - is not possible. I should note I don't want the user to have to use the file picker to create the folder or any files - ideally the app will take care of saving it to the app's public folder for them. So far I've tried using MediaStore and Storage Access Framework but have had little success. Just need a push in the right direction.
Note: I'm aware of getExternalStoragePublicDirectory(). It worked for what I need quite well, but I'd prefer not to use deprecated functions if possible. I also have the required permission in my manifest file, just having a hard time creating these files outside of my app's own folder within /data/
Thanks in advance anyone who can help :)
Try this,
private fun getInternalLogFile(filename: String): File? =
File(
context.getDir(INTERNAL_DIRECTORY_NAME, Context.MODE_PRIVATE),
filename
)
INTERNAL_DIRECTORY_NAME is your folder name
Related
first question on this site, if improper just tell me.
I am creating an app on an adroid platform. With this app I create some files and folders in the shared document folder.
What happens is this: with every build, at least with the -cleaninstall parameter set, it is impossible to overwrite existing files and/or folders. Even after deleting them on the phone.
Probably this is due to the fact that the filesystem thinks that the new build is not the original owner of the file/folder and is therefore not authorized to delete or overwrite.
As a bypass solution I am using an "appname" variable to create a folder to store data in, if necessary I update the "appname" variable so a fresh set of folders is created, based on the "appname" but this a pretty crooked way to work.
DocumentFolder := System.IOUtils.TPath.GetSharedDocumentsPath;
AppName := 'ExpensesV2';
AppFolder := DocumentFolder+PathDelim+AppName;
if NOT DirectoryExists(AppFolder) then ForceDirectories(AppFolder);
Is there a proper way to really remove/clean up that specific folder OR get the proper autorisation.
Thank you for your valued responses!!
You don't specify how you're going about creating the folders and writing the files. There are different mechanisms for doing so depending upon the type of file you want to write (media files, documents, generic files) and who you want to have access to it (public vs app private storage). There are also additional complications with permissions and things depending upon what OS version you're targetting.
Given the information available, the best I can suggest is having a look at these links:
https://developer.android.com/training/data-storage
https://developer.android.com/training/data-storage/use-cases
The latter gives some sample cases of types of file and purpose and suggests the mechanism to use to write/read it.
In the most general of senses, if you want to write to public external storage then use getExternalStoragePublicDirectory(); but the warning note here is that the user can do as they wish with the files in the storage area; putting their own files there, renaming files, deleting files etc etc It is beyond the control of your app to manage.
If you want your app to manage the storage space properly then you need to use something like Context#getExternalFilesDir(), but then if you want those files to be externally visible you will have to share them with the system and look into things like file sharing or content providers.
I have read about and think I understand the essentials of changes in Android 10 and 11. Gone are the days of accessing folders and files outside of the Android app sandbox willy nilly. That's fine. Just need a way forward and that's become difficult.
I have 2+ apps that share a local Sqlite database and related files in a folder. One or more of the apps in the group might be installed - no guarantee on which of the apps are present. On iOS and Windows (UWP) there is a nice "app group" (iOS name for it) style concept that supports this kind of arrangement formally in the platform. First one installed/run will create the local storage files. Last app in the group uninstalled and the OS cleans up the shared storage location. Android never had this concept so a common location was created outside of the app specific sandbox.
After studying the options available going forward, seems like the "Best" option was to use the Storage Access Framework (SAF) to get permission from the user for some common folder to use. Note that although there are many different "sharing" options in Android, none of them are great for this use case, and most are not friendly to cross platform Xamarin C# without wrapping them somehow. This "Best" option using SAF still requires the user to independently pick the SAME folder from each app that wants to share the local db/files. You know users are going to mess that up, but that's beside the point at the moment.
In testing this approach, I have been able to use the SAF picker to get the user to choose a folder. The Documents folder is what I've been choosing to test with as a folder. From there the app attempts to create a subfolder where all this shared "app group" content would go. Unfortunately simply doing a Directory.CreateDirectory(path) gives a System.IO.IOException: 'Read-only file system'. I checked am I am still able to do Directory.CreateDirectory(path) in the app sandbox (GetExternalFilesDir), just not the SAF chosen location.
I am also able to create a directory in the SAF location if I stick to the SAF API, such as illustrated in the Xamarin Android sample here: https://github.com/xamarin/monodroid-samples/blob/master/android5.0/DirectorySelection/DirectorySelectionFragment.cs#L169-L188.
Is there any way to treat the SAF location chosen by the user just like a normal file system and use System.IO operation to manipulate it? The app has been given permission but those ops don't seem to work in that location. Or is there a better overall approach to this problem that I've totally missed?
Normal Java File I/O does not work with Scoped Storage. File paths and File or Directory objects do not worked in Storage Access Framework, you have to do everything through the DocumentFile API. DocumentFile has the ability to create files and directories in locations that the user has granted your app access to through the File-picker dialog.
There IS a way for normal/traditional System.IO file I/O to work after converting the SAF content to a classic file system path. Using the FileUtil logic in this answer https://stackoverflow.com/a/36162691/1735721 I was first able to get permission to a folder from the user:
var intent = new Intent(Intent.ActionOpenDocumentTree);
StartActivityForResult(intent, 1);
The in OnActivityResult(_, _, Intent resultData) use the file util logic:
var folderPath = FileUtil.GetFullPathFromTreeUri(resultData.Data, this);
var filePath = Path.Combine(folderPath, "test.txt");
At that point filePath represents the path and filename in the chosen directory tree, and normal C# System.IO operations are available to the app for that file e.g. StreamWriter and StreamReader.
NOTE: I was creating "test.txt" directly in the chosen folder. This worked to create the file in "A" but then "B" couldn't read that same file (Unauthorized exception). At some point I created a subfolder and "test.txt" was created there instead...then both "A" and "B" could read and write the same file. Unfortunately, a couple days later, I couldn't repeat that. So as it stands this is only a partial solution.
I need to access the private folder of my app for debugging purposes.
I use myActivity.getFilesDir()
to handle the private folder but I cannot see it in the filesystem when using the file manager.
Before, I used Environment.getExternalStorageDirectory() and I could see the com.myDomain.app folder in the Android/data directory and I was able to open it and check the private files for debug.
Now, how can I access the getFilesDir()
folder from the file manager?
The directory I used to see being created in /Android/data path is found by means of the getExternalFilesDir(null) method call.
I saw an example with the null parameter, and it works for me.
I replaced all "getFilesDir()" instances in my code files with "getExternalFilesDir(null)", for debugging purposes.
That folder is correctly created and accessible: /Android/data/com.mydomain.app
Every other app is using that folder, even if it is accessible to the user.
I do not know whether I will end up using that folder or the hidden (as far it seems) one.
You are running into the Android security system. The private files of an application can only be accessed by the application itself. You only have a few options:
Add some code to your application that lets you view, dump or copy the private files. I typically make such code dependant on DEBUG builds so that I don't accidentally release it into production.
Move the storage to a public location. I don't typically like this because you have to change code between testing and production, which risks mistakes.
Save files on to an SD card and then access it outside of the device so you are not subject to Android restrictions.
Root the phone so you can bypass the normal restrictions.
If you make some simple code to copy all private files to a public location, then you can write your production code, but with debug builds, you can get it to snapshot the files for debugging.
Hope that helps.
After looking all over google I haven't found a way of accessing Android internal storage from python.
I need to store an image generated by kivy app. I would like my python code to be able to navigate to a root user dir, create an app specific dir (if not found) and save the image there.
Simply creating a file with my device's path doesn't work. Should I be setting permissions for internal storage access? I'm lost with it.
You have something wrong in your code, you didn't paste the code nor logs, so... let's get it another way.
You can read/write with python just fine in the app folder (/data/data/org.something) with using:
app_folder = os.path.dirname(os.path.abspath(__file__))
To write to SD card you'll need to use App.user_data_dir, which as you can see in the docs on android gets you /sdcard/<app_name>. Not sure if it automatically creates it if it's missing, so it needs checking out probably. However, if it doesn't exist, just create it with python:
os.mkdir(App.user_data_dir)
Is there any sort of consensus on creating/using directories for storing/accessing data for android apps.
For example on windows a new application (say MyApp) would go in the "Program files" directory in a new "MyApp" directory.
I'm writing an app that allows the user to analyse photo and xml files. Is it usual to expect MyApp to just look for those files in Environment.getExternalStorageDirectory(), require the user to move the photos/xmls to Environment.getExternalStorageDirectory()/MyApp or something else? Should you always just provide a file explorer to look anywhere on the device?
I can do any of the above means of accessing but it's better to stick with the user's expectations.
Any pointer to a UI preferred practices would be useful (assuming they're widely followed).
To store your pictures (assuming you are using API >= 8), you should use:
getExternalFilesDir(Environment.DIRECTORY_PICTURES);
Check the documentation which contains an example.
If you want to keep your images in a separate folder you can create one named after your package name inside this one.
Otherwise, if you are using API <= 7, use getExternalFilesDir() to obtain the root of the tree from where you should start looking for images.