Opening Files from a String Path with Android NDK - android

I've been going over the Android file access documentation lately, but I seem to be unable to figure out how to actually open a file given as a string containing the path to the file I'd like to open.
What I (eventually) want to accomplish is something like this:
The user selects a specific kind of text file using Intents, receiving a URI to the file. From this I derive the path (getPath()) and pass this string to the native C++ code.
The native C++ opens the file from the string, parsing the content.
Perform some actual work with the above.
From what I've found so far, it seems like it is no longer possible to open files this way (as of SDK version 26 at least):
A hard-coded path to a file I know exists gives me permission denied.
The path itself received from getPath() triggers a No such file or directory error.
One workaround called for opening the file on the Java side using the ContentResolver, and then passing the file descriptor to the native side. This works, but it's problematic: the files can contain references to other files to be opened ("include files") making such a solution of limited use.
Just to make things clear, these files reside locally on the "USB" partition of the Android system, unrelated to the app itself. Not as resources/assets to the APK or anything similar which other questions of this kind seem to require.
In summary, I guess the question is this: Is it possible to open a file, and possibly any other files it refers to, given a path from the Java side of the application? Is there any requirements for doing this, such as requesting the correct permissions for folders or something similar?

As of Android 6.0 (API level 23) you need to request permissions every time your app starts for "dangerous actions" such as accessing the filesystem or reading contacts. The linked pages already have a snippet of code you can copy.
On older phones requesting permissions in the manifest was sufficient, but the target SDK version was recenly upped to 8.0 (=26). If you want to support devices pre-6.0, the Android compatibility library will allow you to call the same API.

Related

How to get full access to files in external storage Android 11?

In Android 11, the getExternalStorageDirectory method that returns the path to external memory is deprecated. Instead, new APIs appeared for working with specific public folders or application folder.
But for a work task, I need to work with folders and files located at the root of external memory. I haven't found an example of a new API that allows this. In the Google documentation, in fact, only how to get permission (via ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION) is not a problem. In addition to working with files from Kotlin, I also need to interact with files from C++. There are no problems with the deprecated API, there you can get full paths to files and do whatever you want with them. But what about the new APIs? Or just use the old API, hoping that it won't be released in the next versions?
PS The application will not go to the market, so I can get any permissions and do whatever I want :)

System.IO write operations not working in Android 11 outside of app sandbox

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.

Using legacy storage in Android API29

I am trying to use "Legacy Storage" for my app so that it can run on API29, as a temporary measure until I can understand the new storage model. I have set
requestLegacyExternalStorage="true"
in the manifest, but it still doesn't work. The Android docs say:
Test scoped storage
To enable scoped storage in your app, regardless
of your app's target SDK version and manifest flag values, enable the
following app compatibility flags:
DEFAULT_SCOPED_STORAGE (enabled for all apps by default)
FORCE_ENABLE_SCOPED_STORAGE (disabled for all apps by default)
To disable scoped storage and use the legacy storage model instead, unset both flags.
How do I unset the flags? Any other advice?
Back to the main question:
How do I unset the flags? Any other advice?
The flags you mention (DEFAULT_SCOPED_STORAGE and FORCE_ENABLE_SCOED_STORAGE) are for development purpouse only. You can set them in App Compatibility Changes panel on settings. To find this pannel, you have to unlock Developer options in settings and you can find there App Compatibility Changes.
For more information, see the documentation: How to identify which changes are enabled
At last (with more research on stackoverflow!) I have found the way around my problem, so I'll describe it here in case it is of help to anyone else.
THE APP WORKS OK UP TO API28:
My app downloads a file called myfile.txt and saves it in external memory, using DownloadManager. The directory where DownloadManager saves it is determined by
request.setDestinationInExternalPublicDir("","myfile.txt");
which returns the directory path/storage/emulated/0
When I read the file, I get the directory to read from using
String myPath = Environment.getExternalStorageDirectory();
which returns the same directory path. This works OK up to API28.
THE PROBLEM:
For API29, I put
android:requestLegacyExternalStorage="true"
in the <application....../> section of the manifest, but even then request.setDestinationInExternalPublicDir doesn't work - it crashes with a runtime error.
THE ANSWER THAT WORKED FOR ME:
Instead of request.setDestinationInExternalPublicDir I used
request.setDestinationInExternalFilesDir(this,"","myfile,txt");
which works OK in API29 and also the earlier versions. However, it returns a different directory path: /storage/emulated/0/Android/data/com.barney.aboutmyjourney/files (com.barney/aboutmyjourney is my app).
To get the directory path when reading the file, I therefore used
File myFile=this.getExternalFilesDir("myfile.txt");
String myPath=myFile.getParent();
which returns the path I want (/storage/emulated/0/Android/data/com.barney.aboutmyjourney/files).
This now works for me. Now just lots of slog modifying all my file IO. Perhaps I should just understand and used Scoped Storage instead!

How to edit already present file in SD CARD using SAF(storage access framework)?

I have a doubt I read these tutorials just to get the clear understanding of the SAF introduced in kitkat in higher version of android How to use the new SD card access API presented for Android 5.0 (Lollipop)?
How to persist permission in android API 19 (KitKat)?
Android API below 19 equivalent for ContentResolver takePersistableUriPermission
Android Gallery on KitKat returns different Uri for Intent.ACTION_GET_CONTENT
Now i have a question when the intent is fired and i get select the sd card to get the uritree from the intent which i am getting but is the root tree I will have to iterate through the uri to get the specific file uri.
Now the question is I have mp3 files in my phone and I want to edit that specific file which is selected so how can i get that selected files uri from the tree and edit it?I tried editing directly using file but it makes my mp3 file disappear and i dont want to fire SAF intent again n again so how to check is user has given permission to sd card or not?
P.S. I am using jaudio tagger for editing the tags of mp3 files.
THANK YOU!!
I have got answers of almost everything in this question Now the issue if I am using jaudio tagger library for tagging mp3 files which takes File as a objects but android 4.4 and above wont let u modify File objects so I just want to knw the alternative how can i edit File objects in 4.4 above?
This is how I did it,
first copy the file from the sdcard to the phone memory and then do the desired changes and cut and paste the file again at its original place.
Example is here where I am editing the tag of mp3 file which is in the sdcard:-
https://github.com/reyanshmishra/Rey-MusicPlayer/tree/master/app/src/main/java/com/boom/music/player/TagEditor
The library you refer to is called JaudioTagger, and it does not and most probably will never accept DocumentFile. The Storage Access Framework is Google's invention and far from any standard. And JaudioTagger is written for portability.
However, I finally managed to get JaudioTagger mostly running with SAF, but had to substantially modify it, even to rewrite some functions. Basically I replaced all File and RandomAccessFile with my own classes, and additionally the handling of temporary files and renaming must be rewritten (e.g. the scheme: copy original file, change copy, remove original file, rename copy to original name).
In fact it might be easier to use the native TagLib instead, because that already accepts a special C++ class as input. All I did was to create such C++ class whose basis is a file descriptor derived from ParcelFileDescriptor derived from Uri derived from DocumentFile.
Finally one might come to the conclusion that the Storage Access Framework is somewhat suboptimal.

How to write an Intent to display file with only desired extensions in Android

I am developing an android app that encrypts/decrypts file.
Once a file is encrypted, its extension changes to filename.encrypted ( here filename is itself a fully qualified filename like hello.pdf, so the encrypted file becomes hello.pdf.encypted).
I have intregrated a file chooser intent in my app, so that user don't need to open a third party intent to choose the files.
My problem is, while user selects to Encrypt a file, he should not be allowed to choose files that are already encrypted i.e. having the extension ".encrypted" and when they are trying to Decrypt a file they should only see the files with the extension ".encrypted".
I have seen a lot of file chooser intents like (https://github.com/iPaulPro/aFileChooser) and (http://code.google.com/p/android-filechooser/) but none have the ability to customize the viewable/selectable files. I read a post (Android file chooser) that says, it cannot be done, but I have seen apps that have implemented this feature.
I am need of help in some resources or links that shows me how is that possible to achieve. I strongly believe the functionality is possible.
Besides, I would also like my app to recognize the extension ".encrypted" when viewed from a third party file manager. I believe that is possible by indicating it in the Android Manifest, but I failed to make it possible. I read this post: Register new file type in Android but could not do it.
Looking for some help here.
I found a library https://github.com/Kaloer/Android-File-Picker-Activity for the thing I wanted to achieve.
They have given us provision to specify allowed extensions using putExtra in the intent.
May be it would be useful to others with similar problems.

Categories

Resources