I have read the documentation on the Android ContentResolver
I have also searched for a suitable example to no avail.
According to the documentation, there is a method call that can be used as a way to get access to custom provider methods when the standard content provider methods are insufficient:
final Bundle call(Uri uri, String method, String arg, Bundle extras)
Call a provider-defined method.
so in my code I execute:
getContentResolver().call(uri, method, arg, extras);
but it always returns null bundle. In fact, the method in the provider never gets called.
Further research points to a (perceived) discrepancy of the contract where the RESOLVER has a uri argument with no equivalent PROVIDER parameter:
Bundle call(String method, String arg, Bundle extras)
Call a provider-defined method.
I am obviously not understanding something. Can anyone point me in the correct direction?
Further research points to a discrepancy of the contract where the RESOLVER has a uri argument with no equivalent PROVIDER parameter
That's the way they wrote it. The Uri is simply to identify the ContentProvider -- the ContentProvider knows who it is and therefore does not need the Uri.
the provider method does not allow #Override annotation
Yes, it does, as you can see in this sample ContentProvider:
#Override
public Bundle call(String method, String arg, Bundle extras) {
if (SET_KEY_METHOD.equals(method) && arg != null) {
key=arg;
}
return(null);
}
However, your build target (e.g., in Eclipse, Project > Properties > Android) must be set to API Level 11 or higher.
The corresponding call() from the client looks like:
getContentResolver().call(Provider.Constants.CONTENT_URI,
Provider.SET_KEY_METHOD, "sekrit", null);
Yes. I have a method in the provider that is declared 'public' that is passed into the contentresolver argument 'method.'
That's not how it works. call() on ContentResolver calls call() on your ContentProvider.
To answer your second question, my guess is that the ContentProvider version of call() does not need a Uri argument because, unlike a ContentResolver, it doesn't need to find a ContentProvider; it calls the method on itself.
Related
In my current application, I am implementing localization. I came across various ways to access the string (external string). Some methods are:
Accessing directly using R.string.hello
Using getResourse().getString(R.string.hello)
getString.
The Eclipse tool to externalize resources, which creates a message.property file and access the string from a snippet.
The main confusion is between 1 and 2. If I use "1" that is accessing directly, can I get resources based on locale?
TL;DR
It doesn't matter whether you use R.string.hello or getResources().getString(R.string.hello).
Both will point the same thing.
So, yes, for your localization, then it will point to the right stuff.
It's been taken care of.
Some trivial stuff:
R.string.hello is actually an integer.
And most methods, that are used by passing R.string.hello (or other resources) as its argument, basically have the function to translate to corresponding string of the number.
Let's take TextView as an example.
It has two methods for setText.
First:
public final void setText(CharSequence text)
Second:
public final void setText(int resid)
So, when you call using getResources().getString():
myTextView.setText(getResources().getString(R.string.hello);
Then the first method is called.
And when you call using R.string.hello directly, then the second method is called.
If you take a look closer to the source, the second method's content (calling using R.string.hello directly) is actually calling the first method.
https://github.com/android/platform_frameworks_base/blob/master/core/java/android/widget/TextView.java#L4133
Both are provided for our convenience, so that we can use either direct resource R.string.hello or using getResources().getString(R.string.hello).
Implementation of Context method getString():
/**
* Return a localized string from the application's package's
* default string table.
*
* #param resId Resource id for the string
*/
public final String getString(int resId) {
return getResources().getString(resId);
}
So it's the same as using getResourse().getString(R.string.hello) (2), and it returns the result in the proper locale.
As I understand from the docs, one SyncAdapter defined in a SyncService is limited to receive only one ContentProvider authority to work on.
But, at the same time, it has access to ContentResolver which it allows to run queries on other ContentProviders as well. I don't understand this particular design concept if a developer is needed to provide a single content authority to SyncAdapter and nonetheless she is able to do whatever she wants on whatever ContentProvider she has access to. My question is: What are the consequences of ignoring onPerformSync's parameters: String authority and ContentProviderClient provider and going with pure ContentResolver?
My application's (actually its SyncService) idea is simple: query a calendar server (OwnCloud in my case) to get not only events (synced with com.android.calendar) but also VTODOS, which are then distributed between various task management apps I can get source code and/or ContentProviderContract. I also thought of my own "Hub" ContentProvider, which has basic VTODO/Task structure, and is the only one compared to the server. It should be able to sync 2-way with different content providers of task management apps and then it syncs with the server.
I have read using ContentProviderClient vs ContentResolver to access content provider and I think I understand the difference. I'm now puzzled why there is so strong suggestion from android SDK to use a single ContentProvider in a single SyncAdapter and yet you are allowed to use ContentResolver to bypass that limitation.
I spent all day figuring this out and searched hundreds of SO/Google resources on the matter (some of them multiple times). I have also seen questions regarding using one SyncAdapter to sync multiple ContentProviders, but none of the answers were any close to suggesting using ContentResolver instead.
There is no special limitation on ContentResolver's API when used from the context of SyncAdapter. IMHO, the only reason why the framework passes ContentProviderClient and authority to onPerformSync() is convenience and kind of a hint to developers as to how SyncAdapter intended work.
This fact is easily seen in the source code for AbstractThreadedSyncAdapter.SyncThread - the ContentProviderClient passed to onPerformSync() is obtained in a standard fashion:
#Override
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
// Trace this sync instance. Note, conceptually this should be in
// SyncStorageEngine.insertStartSyncEvent(), but the trace functions require unique
// threads in order to track overlapping operations, so we'll do it here for now.
Trace.traceBegin(Trace.TRACE_TAG_SYNC_MANAGER, mAuthority);
SyncResult syncResult = new SyncResult();
ContentProviderClient provider = null;
try {
if (isCanceled()) {
return;
}
provider = mContext.getContentResolver().acquireContentProviderClient(mAuthority);
if (provider != null) {
AbstractThreadedSyncAdapter.this.onPerformSync(mAccount, mExtras,
mAuthority, provider, syncResult);
} else {
syncResult.databaseError = true;
}
} finally {
Trace.traceEnd(Trace.TRACE_TAG_SYNC_MANAGER);
if (provider != null) {
provider.release();
}
if (!isCanceled()) {
mSyncContext.onFinished(syncResult);
}
// synchronize so that the assignment will be seen by other threads
// that also synchronize accesses to mSyncThreads
synchronized (mSyncThreadLock) {
mSyncThreads.remove(mThreadsKey);
}
}
}
Therefore, the bootom line: you can use ContentResolver in your SyncAdapter as you wish - just call getContext().getContentResolver() and access any exported ContentProvider.
I have implemented a SyncAdapter, AccountManager and private ContentProvider along the lines of the SimpleSyncAdapter sample project in the SDK. It is all working well.
Now I want to show a message to the user when new rows have been downloaded from the remote server that have a specific flag set. I need a callback from the SyncAdapter when a Sync has finished so I can do the query and display the message from an activity. I have seen a few questions on StackOverflow discussing this but none with a good answer.
How does one listen for progress from Android SyncAdapter? says that the SyncStatusObserver is useless. User mobibob suggests using a ResultReceiver to respond back to the UI from the sync thread.
How to know when sync is finished? suggests using an Intent in your SyncService.
How to signal sync-complete to the Android SyncManager? suggests using the SyncResult. The example code linked to by maxpower47 uses the SyncResult class to report exceptions but not to actually report if a sync was successfully completed.
I just don't know which is the best option and I have not seen any example projects where any of these solutions are used.
I know this is an old question, but I was asking the same thing myself.
What I found out as a good solution, specially because I'm dealing with local data as you are, is to use the following method from ContentResolver:
registerContentObserver(Uri uri, boolean notifyForDescendents, ContentObserver observer)
This register an observer class that get callback when data identified by a given content URI changes. But that can only happens if your ContentProvider send the notification. So for example, if you want to get notified on the ContentObserver above for all updates done on your database through a ContentProvider, your ContentProvider should implement update similar to this:
#Override
public int update(Uri uri, ContentValues contentValues, String s, String[] strings) {
// code goes here
this.getContext().getContentResolver().notifyChange(uri, null);
return 0;
}
Using notifyForDescendents when you do a registerContentObserver can be very useful.
This is an old question but I did some research in the past days and there are not many exemples on syncAdapter handling network requests and notifying the UI.
First you should use Loaders with contentProvider to make your life easier. You don't need to register for content resolver anymore the Loader does it for you. So it means your UI gets notified for anything that goes into your content provider.
What if nothing changed ? everything was up to date or you had a network error.
You can listen to the status of your syncAdapter as the Google I/O
app does, search for mSyncStatusObserver in the BaseActivity
I had a look at the default android email app and they use a Singleton with callBacks.
You can BroadcastIntents or use an eventBus (square Otto for exemple) to notify your UI of any behaviour.
I like the last one better because it gives you more granularity on the events that happen in the syncAdapter.
We ran into a similar situation and wrote a static Listener interface to the SyncAdapter. The listener is the activity and performs necessary actions when the data is available (update UI). This also works when the sync-adapter is called by the system during autosync where this listener would be null and the sync process would mind its own business.
class SyncAdapter extends AbstractThreadedSyncAdapter {
protected static Listener uiListener = null;
public interface Listener {
public void onSync();
}
public static void setListener(Listener l) {
uiListener = l;
}
public static void clearListener() {
uiListener = null;
}
protected void broadcastSync() {
if (uiListener != null)
uiListener.onSync();
}
public void onPerformSync(Account account, Bundle extras, String authority,
ContentProviderClient provider, SyncResult syncResult) {
// call broadcastSync();
}
Then in the Activity, implement SyncAdapter.Listener interface.
I have a content observer that should be notified when one of the contacts added by my sync adapter is modified.
I register and unregister the observer doing this:
private static final Uri MYAPP_CONTENT_URI = ContactsContract.RawContacts.CONTENT_URI.buildUpon().appendQueryParameter(RawContacts.ACCOUNT_NAME, SyncAdapter.MYAPP_ACCOUNT_NAME).appendQueryParameter(RawContacts.ACCOUNT_TYPE, MY_APP_ACCOUNT_TYPE).build();
public static void registerContentObserver() {
ContentResolver resolver = MyApplication.getAppContext().getContentResolver();
cursorContacts = resolver.query(MYAPP_CONTENT_URI, null, RawContacts.DELETED + "=0", null, null);
cursorContacts.registerContentObserver(MYAPP_URI_OBSERVER);
}
public static void unregisterContentObserver() {
if (cursorContacts != null) {
cursorContacts.unregisterContentObserver(MYAPP_URI_OBSERVER);
cursorContacts.close();
}
}
The problem is that even when the cursor is empty (getCount returns 0) after I register the observer I get a call to onChange what ever I do in the native address book.
Shoudn't the observer be called only when one of the entries in the cursor was modified?
The documentation states:
Register an observer that is called when changes happen to the content backing this cursor
What's "the content that is backing this cursor"? I thought it was the list of lookupuri of the contacts in the cursor but it looks like it is enough to have a change in the ContactsContract.RawContacts.CONTENT_URI.
I have also tried to register one observer for each Uri. It does not help. Although the documentation for ContentResolver.registerContentObserver states:
Register an observer class that gets callbacks when data identified by a given content URI changes.
Parameters
uri The URI to watch for changes. This can be a specific row URI, or a base URI for a whole class of content.
notifyForDescendents If true changes to URIs beginning with uri will also cause notifications to be sent. If false only changes to the exact URI specified by uri will cause notifications to be sent. If true, than any URI values at or below the specified URI will also trigger a match.
(I set notifyForDescendents to false but it shouldn't have called the observers in any case).
What's wrong?
Thank-you
It is up to the content provider to decide when to report changes. For complicated content providers (like the contacts provider) it can be very difficult to determine all of the specific URIs that change due to an operation, so they just report a global change when something happens.
Query parameters in your Uri, the Fragment, nor even the Scheme are considered when Observer Uri matching occurs. The only thing that matters is the Uri Authority and the Path Segments. Strict left to right matching occurs. I have not tested "*" in a path segment to denote a wildcard, but I suspect that it will not work.
Your particular Observer is ContactsContract.RawContacts.CONTENT_URI, so any time any contact content changes for any reason, your Observer will fire.
So I've always been under the assumption that you can't pass arrays between activities with extras. But I was just going through the Bundle class docs and saw putStringArray(String key, String[] value) and public String[] getStringArray (String key). Are these new? They both say since API lvl 1, but I don't remember being able to pass arrays. Am I going crazy here?
I think you must be able to pass string[] as a bundle in Android. Any specific reason you have for getting into such a conclusion?
Refer to this post
http://www.anddev.org/view-layout-resource-problems-f27/how-can-i-pass-multidimensional-string-array-two-activities-t9259.html
You can pass any Serializable object as extra, so I don't see why you could not pass String array. There is a setExtra(String, Serializable) method in the Intent, that's what I use.