how to store files in content providers - android

I have a requirement to attach a Zip file to a message in the Android email composer.
The Zip file is being created by my application and stored in the app's private storage area (accessible via getFilesDirectory()).
I am obtaining the URI to this Zip file and appending it to an email intent object; when I launch the email activity I am able see the attachment but my email recipient is not receiving the files,
After doing some research on this, I found that my application data cannot be shared with other app's (in this case the Android Email app).
To solve this problem I would like to implement the recommend solution of using a content provider to share my application data.
First of all, I would like to confirm if this is possible, and if it is could anyone please give me some hints on how to go about doing this. I need to know how to copy my Zip file from the getFilesDirectory() of my application, to the content provider, and also how to attach the content provider URI to the email intent object.
I can't place my zip files into an SD card...
I just want only to store my files in to my device internal storage and attach to the email composer.

By default, a ContentProvider can be accessed by any application on the device. If you are willing for these files to be accessed by any application on the device, create a ContentProvider, with real implementations for getType() and openFile(). Then, the content:// URL should work with the Email app, AFAIK.
Here is a sample project demonstrating a ContentProvider serving up files from local storage, in this case to a WebView.

If all you want is to avoid storage on the external SD card, then you don't need a ContentProvider. You can live with
openFileOutput("yourzipfile.zip", MODE_WORLD_READABLE)
and pass
putExtra(Intent.EXTRA_STREAM, Uri.fromFile(getFileStreamPath("yourzipfile.zip))
along with your ACTION_SEND Intent.
However, the email program will probably set no Content-type.
Also, there's no reliable way to tell when the email app doesn't need your file anymore. This means that you either risk ending up with many files or sending a newer content as the attachment of an older email.
Another issue with this solution is that everyone can read your zip file. This problem would not occur with a ContentProvider solution, where you can grant access permission on a per-Intent basis, i.e. allowing access to one file only for one email Intent.
Matching of the Uri is then done via the URI, which may start with your package name, such as
content://com.yourdomain.yourapp.yourproviderclass/some/path
You may want to look at http://developer.android.com/guide/topics/manifest/provider-element.html

Related

The only way to get a File object by letting the user choose a file is MANAGE_EXTERNAL_STORAGE?

I want to show a file browser to the user, let him choose an arbitrary file he wants, and read it using the ZipFile class. The class requires a File object, not an InputStream.
If my research is correct, the in-built file choosers (ACTION_OPEN_DOCUMENT, etc) only provide a stream, not a File, right? Even if I get the file name from the chooser and have acquired READ_EXTERNAL_STORAGE from the user, it seems that in recent Android versions, the API only allows "only media" and reading files of other types seems to fail.
If I have also acquired MANAGE_EXTERNAL_STORAGE, then it works, but that shows a scary warning like following to the user.
Allow this app to access, modify, and delete files on the device or any connected storage devices? This app may access files without asking you.
That seems to be an unnecessarily broad permission when all I want to is a read-only access to File for a file that the user has explicitly chosen in the file browser. There is no other way than acquiring MANAGE_EXTERNAL_STORAGE, is that right? I want to make it sure, because it sounds weird. Even a very restricted environment like a web browser, the browser does not require any special permission or show any warning for reading a file that the user has explicitly selected, and the same was true for UWP app.
If my research is correct, the in-built file choosers (ACTION_OPEN_DOCUMENT, etc) only provide a stream, not a File, right?
Correct. After all, the user can choose something that you cannot access via the filesystem, either due to permissions or due to the fact that it simply isn't on the filesystem (e.g., cloud storage, SMB/CIFS file server).
IOW, what you refer to as "file choosers" are not limited to what you seem to be thinking of as files.
If I have also acquired MANAGE_EXTERNAL_STORAGE, then it works
I do not know what "it" is. You cannot hack into Google Drive to get a File object using MANAGE_EXTERNAL_STORAGE, for example. Nor can you get a File object for a document contained as a BLOB in an encrypted database, etc.
There is no other way than acquiring MANAGE_EXTERNAL_STORAGE, is that right?
You cannot do what you want even with MANAGE_EXTERNAL_STORAGE. A Uri is not a file.
Your options are:
Copy the content identified by the Uri to some file that you control (e.g., in getCacheDir()), so you can use ZipFile.
Relax the ZipFile constraint. For example, perhaps you could use ZipInputStream, or find a third-party ZIP reader that supports InputStream (such as this or this). You can get an InputStream on the content identified by the Uri via openInputStream() on ContentResolver.
Relax the "in-built file choosers" constraint, and implement your own "file chooser" with files that your app can read on the filesystem. That way, you are guaranteed that you can use File, since you used File to get to them in the first place.

When to delete file shared with external apps by content provider

In my application I allow users to share files with external apps by using content provider. My files are encrypted so before I can share one of those files, I need to decrypt it and store it in application internal storage. After that I implemented custom content provider that can point to the file by using a given URL and return ParcelFileDescriptor instance.
For security issues I would like to remove the file from internal storage after external application has used it, or user canceled operation.
EDIT: The external application that I am sharing file with is a 3rd party application.
So my question is:
When should I remove the file, or do you know of any event that I can listen to or a handler that I can attach to the file?
Thank you for all you responses.
Side question would be:
Do you know of a way where I wouldn't have to store the file on internal storage, but rather send it directly as a stream to the external app?
The answer to my question is to use pipes ParcelFileDescriptor.
Code is described in The Busy Coder's Guide to Android Development from CommonsWare.
The solution is based on:
https://github.com/nandeeshwar/Pfd-Create-Pipe
Actual solution that I managed to implement is:
https://github.com/commonsguy/cw-omnibus/tree/master/ContentProvider/Pipe
If it is your external application, you could leave an encrypted file and pass the key in an Intent. If it is a 3-rd party external application, you have to find out what that application can accept.
You might also read about services -- maybe they provide what you want.
As to "when", I'd suggest launching a clean-up task in onResume(). (Unless you decide to use a service.)

Android: How can I share SQLite table with another phone with the same APP

I am thinking regarding the future options of my app and I am thinking of the idea of backing-up the data from the application's Database and also sharing that data with another phone, say via e-mail, messaging, Bluetooth, you name it, but basically saving it as a file and opening it from the other phone and having the same values on both phones.
What would be the best approach for such an Android application?
Would Content Providers accomplish exactly this or are they concerned with sharing data only between different Apps? Thanks!
I believe it is possible ,
If you read the documentation about the internal storage here, It mentions
You can save files directly on the device's internal storage. By default, files saved to the internal storage are private to your application and other applications cannot access them
So i believe you can copy the whole sqlite DB file to some temp location then share that file via BT or email or any other sharing option .
But DO NOTE, that the same application package can only access the file if you want perhaps another application to use the db then u need to set the SharedUserId , as mentioned here
Content Providers are generally only for sharing your app's data to other apps.
Content providers are the standard interface that connects data in one process with code running in another process.

write to external storage and send e-mail?

i have to make an application in which first i have to create a text file from the data user has entered in the UI and then send the text file as an attachment in an email. For this i will have to write data to the external directory and then pass the URI to the Intent so that the email client in the phone can read it.
i am not sure how would i:
write data to the external storage.
2.Pass the URI of the file created to the Intent so that i can attach it to the email.
I have seen several examples of how to send email attachments.. but none fit my requirements hence as a last resort i am posting this question.
thank you in advance.
Both questions have been answered here many times.
External storage:
How can i Use External Storage in android
How should I refer to "external storage" in the UI on Android?
Attach a file to an email:
Trying to attach a file from SD Card to email
Android:Attach file with email from device memory
I honestly cannot figure out what is so specific about your requirements. It looks like a pretty standard task to me...

Access database of another app

On the dev site, it says if your applications are signed with the same certificate you can share data.
I would like to create a utility that I can pack with a csv file, sign and package the apk and when I run it on my device, I would like for that csv to be imported into another one of my apps database. Is it possible?
I know how to get the csv into a database but not how to get it into a different apps database...
You could put your database on the external sdcard. There is no security there.
For sharing protected files the applications must be signed with the same cert and have matching android:sharedUserId in their AndroidManifest.xml files.
Your utility app could try opening the other apps database with an absolute pathname. I'm not sure what would happen with transaction management if both apps are modifying the db at the same time.
A better design is to have one application only own and access the database. Implement an "IMPORT" intent handler on the app that owns the database. Your utility application can send an Intent, specifying the csv filename in the intent extraData. Or you could just put the data on the Intent's extraData (if it's large then send an Intent for each record).
The standard way is to implement a ContentProvider. Use permissions to control access.
http://developer.android.com/guide/topics/providers/content-provider-basics.html
http://developer.android.com/reference/android/content/ContentProvider.html

Categories

Resources