Android multiple content-providers, (DLC) - android

I am looking to add additional content to my app as extra databases.
Should I put them in apks and content providers, so they can be updated from google play directly, or just as new db files downloaded straight into the app.
My problem with the content-provider/apk method, is they all have to be declared in the android manifest, and I might have multiple databases even hundreds, so would need 100s of content-provider declarations in my manifest, even when the user may have only a couple or even none of them.
Unless there is a way I can generate the manifest dynamically? Or load the content-providers outside of the manifest?
Thanks

I think the cleanest way to do this is to have one unique ContentProvider for all your db files.
You should build your URI's around database selection.
For example : content://com.your.package/a_db_file/something/things/5
Then when implementing your ContentProvider, parse the Uri to get the a_db_file segment, open the corresponding db file, then do the needed work according to the rest of the segments.
Maybe you will need a method like getCorrectDb(String a_db_file).
Inside this method you should make the correct call to a sqlLiteOpenHelper that properly match the needed db file.
Also take a look at UriMatcher, it might be useful for you. :)
In the end you should have something like:
#Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
// TODO : Parse uri to get a_db_file string.
SQLiteDatabase database = getCorrectDb(a_db_file);
// TODO : get cursor from db according to other segments of uri.
return cursor.
}

I'd use one content provider to keep things simple and utilize the "attach database" sql command.

Related

Android Content Provider - Using Query Method To Insert, Delete, and Update

I have been studying Content Providers by reading Android Developer Fundamentals (Version 1).
Under section "11.1 Share Data Through Content Providers" / "The query() method" there is a note that states
Note: The insert, delete, and update methods are provided for
convenience and clarity. Technically, the query method could handle
all requests, including those to insert, delete, and update data.
How can query method be used to insert / delete / and update data? The query method has the following signature and does not take in custom SQL strings.
public Cursor query(Uri uri, String[] projection, String selection,String[] selectionArgs, String sortOrder){}
I have not been able to find any resources that shows how this can be done. All seems to utilize the provided methods.
Frankly, that is a really poor job in that training guide. All it will do is confuse people.
I presume what they mean is that there is nothing magic about query() that forces it to only perform SQL SELECT statements. A ContentProvider is a facade — you can use it to store and retrieve from pretty much whatever you want, not just SQLite.
So, you could have your query() method:
Examine the Uri
Look for it to be of the form content://your.authority/something/insert/put/data/here
Parse the Uri to get the put, data, and here values
Insert those in a table under some pre-determined columns
Return an empty MatrixCursor
Or, it could:
Examine the Uri
Look for it to be of the form content://your.authority/something/insert
Insert a row using the projection for the columns and the selectionArgs as the values to put in those columns
Return an empty MatrixCursor
I do not know why anyone would do that, but it is certainly possible.

What is the right way to change the display name in an Android FileProvider?

Say if I have filename 1234567.png on the internal filesystem - say I want to send it to the user with a share intent as CoolPicture.png - ie subclass fileprovider and replace the query method.
Is using a matrix cursor based on the cursor from super a good plan?
It seems like DISPLAY_NAME and SIZE are the only columns but doesn't seem to confirm if SIZE is int or string.
http://developer.android.com/reference/android/provider/OpenableColumns.html
http://developer.android.com/reference/android/support/v4/content/FileProvider.html
I'll plan to post my code after I answer it, I just wanted to get an idea of best practice, and perhaps contribute.
Thanks
Is using a matrix cursor based on the cursor from super a good plan?
You could do that, and it's probably OK. After all, FileProvider itself uses a MatrixCursor.
To modify an existing Cursor, I use CursorWrapper myself, in my LegacyCompatCursorWrapper, then in query() wrap the Cursor I get from the base ContentProvider implementation (e.g., FileProvider) in my wrapper.
It seems like DISPLAY_NAME and SIZE re the only columns but doesn't seem to confirm if SIZE is int or string.
That's one of the reasons I went with the CursorWrapper, to avoid messing with any existing values.
That being said, the existing FileProvider implementation uses a Long (file.length(), auto-boxed).
This is an old question, but the new androidx.core.content.FileProvider has now a second getUriForFile method with an additional displayName param which sets the value for the OpenableColumns#DISPLAY_NAME key-column.

Content Provider, CONTENT_FILTER_URI... How to form the filtering URI correctly?

I have several questions related to the official LoaderManager example when implementing filtering in an application (i.e. SQLite database, using Content Provider, using CONTENT_URI when no filtering is used, or forming the URI for filtering via concatenation of the CONTENT_FILTER_URI and the content of the mCurFilter variable.
Fistly, I need confirmation, that I understand the filtering URI correctly. Earlier, I did use the CONTENT_URI and set the selection, selectionArgs, and sortOrder when creating new CursorLoader(...) in the onCreateLoader method of the activity that implements LoaderManager.LoaderCallbacks<Cursor>. I did not understand why others use a special CONTENT_FILTER_URI.
The official example passes null for the selectionArgs, and the filter value is probably extracted as filter = uri.getLastPathSegment(); and added used for the selectionArgs. In my case, I am passing null also for the selection and forming it in the content provider like:
selection = ProductTable.COLUMN_NAME + " like ?";
selectionArgs = new String[]{ "%" + filter +"%" };
My understanding now is that:
The filter value can be used also other ways than simply put to a simple and single SQL command, and this is the reason to use the CONTENT_FILTER_URI.
The CONTENT_FILTER_URI makes getting the information more abstract, i.e. less SQL specific, more web-service like access to the data.
With respect to that, I am still confused why also the sortOrder is not expressed in the URI somehow. Is it a kind of tradeoff? (I hope the official example simply does not take a slopy approach, and there is real reason to form the select argument outside the content provider.)
Should I always parse the user-entered filter value? The reason is that a user can enter whatever filter value -- also the forbidden characters can be inserted. This way the unknown URI path can be formed and the URI may not be recognized. Or even worse, there may be a chance that some kind of SQL injection can be done, or at least the application can be crashed. Am I too paranoic when thinking about URI and the injection errors? Are there some rules or tools mentioned at developer.android.com or elsewhere that are related to the problem?

Should my function to get an Android Contact by name return a Cursor or something else?

I'm working on a class that is supposed to handle the user's Android contacts and interact with an SQL database where you can move your phone's contacts to (the information in the database will be displayed as a ListView). I've made a function that is supposed to retrieve an Android contact by name and return all of that contact's information. My function is as follows:
public Cursor getContactByName(String name)
{
Uri uri = ContactsContract.Data.CONTENT_URI;
String[] projection = null;
String selection = Data.DISPLAY_NAME + "=?";
String[] selectionArgs = new String[]{name};
String sortOrder = null;
return managedQuery(uri, projection, selection, selectionArgs, sortOrder);
}
So the idea is that this would be called by another function, which would first check if the contact existed. This function would return a cursor containing only that contact's information. First I want to know if I'm right in returning a Cursor containing the contact's information or should I instead create a class that stores this information and return an object of the class? Or perhaps I should return a string? Keeping in mind that what I'm returning is the contact that I intend to move to my database, I feel like returning a Cursor isn't what I should be aiming for. And as a side-question, is there any reason to use ContentResolver.query instead of managedQuery for my purposes?
It depends on what you are going to be using the data for. If you're going to be adding it to another database (as your question suggests) you are probably best off leaving it as a cursor and simply entering that cursor in your second database. If you like mapping your database rows to objects, then do that. Keep in mind Object Relational Mapping takes more memory and cycles but usually results in clearer code. It's really up to you as the programmer (sorry I know that's not what you want to hear).
Personally i would prefer to create an object Contact. So you are more flexible and you can also implement functions related to your objects in this class (or the related DAO).
Passing the values as Cursor or even as String gives more trouble than it will help you. In an object you can see exactly the propertynames for everything, with a cursor you have to know the names or the positions of the corresponding fields, and in a string you even have to parse your results.
EDIT:
As #DavidCowden metioned, it depends strongly on what you are trying to achieve. So my guess was, that you want to load your data and display it somewhere.
And as already mentioned, in this case i would prefer a seperate class as it's much cleaner and maintainable.

Using Cursor respond and getExtras not working?

I'm designing a ContentProvider which needs to return a cursor with data and I need to know the status of that data.
I need a status for the cursor data since this data was downloaded and I need to know if it only downloaded partial data or if during the download some of the elements weren't downloaded. It's a bit more complex but you might get the idea why I need a status for my cursor data.
So I have reviewed the source code for all Cursor implementations in Android and it seems getExtras and respond functions are plain garbage since they don't do anything internally... Also, there seems to be missing a setExtras function. Any ideas?
I think that the method is not setExtras(), but respond().
I'm not sure what you mean by the "status of the data", but it sounds like you want to know if the columns in the cursor are clean or not. Most content provider implementations I've seen put this type of status into the rows themselves, or store it in a separate table. I don't think you should try attaching the status to the Cursor itself.
Just so you know, if you want to get notification when the data underlying the cursor changes, you can use setNotificationUri(), registerContentObserver, or registerDataSetObserver().
Notice that nothing requires you to make the columns you provide from a content provider be the same as the columns in any underlying database you use. You could add a column that the provider itself generates, and store status in that.

Categories

Resources