.update() ContentProvider throws Unknown Uri, even though .query() works? - android

App A with the following AndroidManifest.xml content (name and authority redacted)
<provider
android:name="test.provider"
android:authorities="test.content_authority"
android:exported="true" />
With the following ContentProvider class:
public class Provider extends ContentProvider {
#Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
// [...]
}
#Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
// [...]
}
}
From another App B I have no problem querying this provider:
String URL = "content://test.content_authority/data";
Uri data = Uri.parse(URL);
Cursor c = getContentResolver().query(data, null, null, null, null);
But when I try to update an entry:
String URL = "content://test.content_authority/data";
Uri favarticles = Uri.parse(URL);
ContentValues values = new ContentValues();
values.put("key", "value");
getContentResolver().update(favarticles, values, null, null);
I get the following exception:
java.lang.UnsupportedOperationException: Unknown Uri: content://test.content_authority/data
at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:169)
at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:137)
at android.content.ContentProviderProxy.update(ContentProviderNative.java:560)
at android.content.ContentResolver.update(ContentResolver.java:1316)
at com.example.test_contentprovider.MainActivity.updateEntryTest(MainActivity.java:119)
Is there anything obviously wrong? Do I not have the rights to access the Provider? Why do I get an Unknown Uri Exception when I can access this URI with a query?
thank you very much.

Related

Android Custom Content Provider

I'm developing Custom content provider in my app. And face issuel when get list from custom content provider. Detail, my custom content provider contain a Table. I just want get all object in this table. But it not working. This is my code, please show me what thing i wrong ?
public void onAdd(View v) {
String name = edtname.getText().toString();
Uri uri = Uri.parse("content://homework.iuh.hh.customcontentprovider.AccountProvider/accounts");
ContentValues values = new ContentValues();
values.put("NAME", name);
getContentResolver().insert(uri, values);
}
public void getList(View v) {
Uri uri = Uri.parse("content://homework.iuh.hh.customcontentprovider.AccountProvider/accounts");
Cursor c = getContentResolver().query(uri,null,null,null,null);
c.moveToFirst();
String res= "";
while(!c.isAfterLast())
{
res += c.getString(0);
c.moveToNext();
}
c.close();
Log.i(TAG,res);
And it is query method in content provider
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
Log.i(TAG,"query()");
Cursor c = getContext().getContentResolver().query(uri,projection,selection,selectionArgs,sortOrder);
c.setNotificationUri(getContext().getContentResolver(),uri);
return c;
}
In method onAdd(), it working fine. But with method getList, it show log, query function is called very time. and crash app with message
E/JavaBinder: !!! FAILED BINDER TRANSACTION !!!
05-21 11:07:43.661 5876-5876/? E/AndroidRuntime: Error reporting crash
android.os.TransactionTooLargeException
Try changing your code to be as follows:
Cursor c = getContentResolver().query(uri,null,null,null,null);
String res= "";
while(c.moveToNext()){
res += c.getString(0);
}
c.close();
Log.i(TAG,res);
The real problem is that your Content Provider is just endlessly looping in your query method. You need to actually execute the SQL here, not call the content resolver. Take a look at this class

How to update values in MediaStore.Audio.Albums table

I am queryng MediaStore.Audio.Media for a song ID, using the ID to find the album art URI, and then downloading the album art if the URI is null, and finally updating MediaStore.Audio.Albums with the new image URI. I am not able to figure out the last part.
Here I am getting the cursor with GetCursor() method
private Cursor GetCursor() {
Uri contentURI = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
String[] projection = {MediaStore.Audio.Media.ALBUM,
MediaStore.Audio.Media.ARTIST,
MediaStore.Audio.Media.TITLE,
MediaStore.Audio.Media.DATA,
MediaStore.Audio.Media.DURATION,
MediaStore.Audio.Media.ALBUM_ID};
String selection = MediaStore.Audio.Media.IS_MUSIC + " != 0";
String order = MediaStore.Audio.Media.TITLE + " ASC";
final Cursor mCursor = getContentResolver().query(contentURI, projection, selection, null, order);// Run getContentResolver query
return mCursor;
}
Here I am adding the album art URI to songObject with GetAlbumArtURI()
String[] albumID = {mCursor.getString(5)};
songObject.albumArtURI = GetAlbumArtURI(albumID);
Here is GetAlbumArtURI() method
private String GetAlbumArtURI(String[] albumID) {
final Cursor mCursor = getContentResolver().query(
MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI,
new String[]{MediaStore.Audio.Albums.ALBUM_ART},
MediaStore.Audio.Albums._ID + "=?",
albumID,
null
);
if (mCursor.moveToFirst()) {
String var = mCursor.getString(mCursor.getColumnIndex(MediaStore.Audio.Albums.ALBUM_ART));
//DatabaseUtils.dumpCursor(mCursor);
mCursor.close();
return var;
}
else {
mCursor.close();
return null;
}
}
And here I test if the songObject.albumArtURI is null, download the image, and record the URI in the meta data
if (songObject.albumArtURI == null){
String anotherURI = downloadImageReturnURI();
ContentValues values = new ContentValues();
values.put(MediaStore.Audio.Albums.ALBUM_ART, anotherURI);
// And now I don't know what to do
}
So now, how can I add the new URI back to ALBUM_ART column? Something along these lines?
Uri uri = MediaStore.Audio.Albums.getContentUriForPath(newSoundFile.getAbsolutePath());
Uri newUri = getContentResolver().insert(uri, values);
The problem is that I am not trying to update values in MediaStore.Audio.Media but in MediaStore.Audio.Albums
From the documentation - Content Provider Basics
// Defines a new Uri object that receives the result of the insertion
Uri mNewUri;
...
// Defines an object to contain the new values to insert
ContentValues mNewValues = new ContentValues();
/*
* Sets the values of each column and inserts the word. The arguments to the "put"
* method are "column name" and "value"
*/
mNewValues.put(UserDictionary.Words.APP_ID, "example.user");
mNewValues.put(UserDictionary.Words.LOCALE, "en_US");
mNewValues.put(UserDictionary.Words.WORD, "insert");
mNewValues.put(UserDictionary.Words.FREQUENCY, "100");
mNewUri = getContentResolver().insert(
UserDictionary.Word.CONTENT_URI, // the user dictionary content URI
mNewValues // the values to insert
);
And this is probably the most important take away
Many providers allow you to access a single row in a table by appending an ID value to the end of the URI. For example, to retrieve a row whose _ID is 4 from user dictionary, you can use this content URI:
Uri singleUri = ContentUris.withAppendedId(UserDictionary.Words.CONTENT_URI,4);

Contact starting with a specific alphabet

I want to get all the contact starting with the alphabet. Example contact starting with A Is there any way to find that...
Now I am using the code
String name = phones.getString(phones.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
Try following..
managedQuery (Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder);
Cursor cursor = managedQuery(ContactsContract.Data.CONTENT_URI, null,
ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME + " = ?",
new String[] { "A" }, null);
This method was deprecated in API level 11.If it works ok., or otherwise use the below.
CursorLoader (Context context, Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder);

Reversing a Uri variable

I am trying to reverse a Uri's order.
The Uri accesses the phones call logs
Uri allCalls = Uri.parse("content://call_log/calls");
but it stores them from oldest to newest while I want the opposite.
I tried using
Collections.reverse
but that only works for arrays.
Anyone know a solution?
You can input sorting option when you use ContentResover's query function.
query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)
Query the given URI, returning a Cursor over the result set.
public static Cursor getAllCallLogs(ContentResolver cr) {
// reading all data in descending order according to DATE
String strOrder = android.provider.CallLog.Calls.DATE + " DESC";
Uri callUri = Uri.parse("content://call_log/calls");
Cursor curCallLogs = cr.query(callUri, null, null, null, strOrder);
return curCallLogs;
}

How do I ask for information from another Android application?

I need third party applications ("Foo") to get information from my application ("Bar"), but my solution so far seems cumbersome:
Application Foo needs information from Bar and sends a broadcast ("bar.POLL").
Application Bar listens for this broadcast, and replies with another broadcast ("bar.PUSH");
Foo listens for bar.PUSH and reads the contents of the included Bundle.
Is there a more direct way to do this?
EDIT: I solved it with an extremely simplistic ContentProvider as Guido suggested:
public class MyProvider extends ContentProvider {
private String state = "";
#Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
MatrixCursor cursor = new MatrixCursor(new String[]{"state"});
cursor.addRow(new Object[]{state});
return cursor;
}
#Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
state = (String) values.get("state");
return 1;
}
#Override
public boolean onCreate() {
return true;
}
#Override
public String getType(Uri uri) {
return null;
}
#Override
public Uri insert(Uri uri, ContentValues values) {
return null;
}
#Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
return 0;
}
}
Remember to add the provider to the manifest:
<provider android:name=".MyProvider" android:authorities="com.example.hello" />
Update the state from an Activity like this:
ContentValues cv = new ContentValues();
cv.put("state", "myNewState");
getContext().getContentResolver().update(Uri.parse("content://com.example.hello"), cv, null, null);
Get content from the provider in the external app:
Cursor cur = managedQuery(Uri.parse("content://com.example.hello"), null, null, null, null);
if (cur.moveToFirst()) {
String myContent = cur.getString(0);
}
You should expose a ContentProvider.
"Content providers store and retrieve data and make it accessible to all applications. They're the only way to share data across applications; there's no common storage area that all Android packages can access."
Content providers implement a common interface for querying the provider and returning results. It is not hard to implement, but maybe the official documentation is not the best to get started with it. You can find other examples at:
http://thinkandroid.wordpress.com/2010/01/13/writing-your-own-contentprovider/
google

Categories

Resources