Is there a way to tell why a ContentObserver was triggered? For example, if I am monitoring SMS via the "content://sms" URI and an SMS is sent or received, is there a way to deduce, within the ContentObserver class, what the SMS type is (I know I can set N ContentObservers, specifying different URI's, but I am hoping there is a way to tell from the ContentObserver class)?
BONUS: There is also a fun subtlety:
The 2nd method is only available from API level 16 onwards, so the code should not rely on a URI to work properly.
ContentObserver:
ContentResolver contentResolver = getBaseContext().getContentResolver();
contentResolver.registerContentObserver(Uri.parse("content://sms"), true,
new MessageObserver(new Handler(), getBaseContext()));
ContentObserver Class:
class SMSObserver extends ContentObserver {
public MyObserver(Handler handler) {
super(handler);
}
#Override
public void onChange(boolean selfChange) {
this.onChange(selfChange, null);
// What SMS type caused this to trigger????????
}
#Override
public void onChange(boolean selfChange, Uri uri) {
// What SMS type caused this to trigger????????
}
}
See you will receive Onchange in below mentioned cases
If any syncadapter is active upon that database then it will trigger onchange each time it pefrom sync, in this case you might receive onchange even if nothing is actually changed.
If there is actaul change in dataset.
Now coming to figuring out data which was changed, setting a observer for each possible URI is not at all good idea, with this you can although detect update or delete scenarios but sensing new insert will be a problem. I will suggest you following
Keep a single observer, upon initialization you can process entire data which is avilable initially, keep remebering column_id,update_timestamp and total row count once you progress forward, when onchange come next time with Coulmn_Id,update_timestamp and count you can figure out kind of change that was occured
--> Look for any rows having column_id greater then what you remeber, if it return any rows then certainly some new rows are inserted you can query them specifically
--> if above criteria fails then you need to look for update_timespamp any thing greater then last you remeber will tell if about rows that were updated
--> If both of above cirteria fial then look for difference in count which might tell you about delete operation.
if none of above occurs, clearly it was nothing then just ignore onchange signal. Hope it helps.
Related
Let me to start explain my problem. There is repository with some explanations, but there are no methods how to get collection or json file from Meteor server(only insert). Also author did not explain properly methods onDataChanged, onDataAdded etc.
public class Login extends Activity implements MeteorCallback{
public static Meteor mMeteor;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
mMeteor = new Meteor(this, "some_socket_it_doesn't_matter");
mMeteor.setCallback(this);
}
}
public class ListOfElements extends ListFragment implements MeteorCallback{
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
String subscriptionId = Login.mMeteor.subscribe("notifications");
Log.d("Log", subscriptionId);
}
}
I didn't understand how i have to use subscription or how to get collection from server. Why there are only insert methods in github repository and no get? I really have no idea how make the code to get collection, use subscribe and so on. There are no any understandable explanations in the network. Please, can you help me with this by explaining how to realize getting, subscribing in this code.
There are two special things about Meteor: It works asynchronously and it has been designed specifically for real-time applications. Thus it has a few different concepts for retrieving data and for some other tasks.
In a synchronous application, you would just call insert(...) and immediately get the method's return value, e.g. a boolean value for success/error or a numeric value for the number of rows that have been inserted.
You would call get(...) and immediately receive a collection of rows as the method's return value.
But in Meteor, everything is asynchronous. This means that you get the results not immediately, but a few (milli)seconds later, in a callback method.
When you call insert(...), this is not so important, as you have noticed. You just call this method and often forget about the result, i.e. you don't wait and check for the result because insertions are usually successful. But this method is still asynchronous and you could (and sometimes should) listen for the result which will arrive a few (milli)seconds later, again.
When you want to call get(...), this would be possible in theory, with the important point again being that it's asynchronous. So you would say "get me all chat messages from the last 5 minutes". There would be no result or return value, as usual, but the result would arrive a short time later, asynchronously, in a callback method that you define. This is what onDataAdded(...), onDataChanged(...) and onDataRemoved(...) are for.
Now it's not clear, yet, why you can't call get(...) and wait for data to arrive in those methods.
The answer to that question is Meteor being designed for real-time applications. This is why you can't say "get me all chat messages from the last 5 minutes". Instead, you have to say "I want to subscribe to all chat messages from the last 5 minutes and always be updated about changes".
So, in Meteor, you subscribe to data sets instead of requesting them via get(...).
All in all, this means the following:
If you want to get some messages, you subscribe to your data set that holds those messages.
When the initial rows are sent (!) and whenever new rows are added to the collection, you receive those in your onDataAdded(...) callback. When rows are modified, you receive those changes in your onDataChanged(...) callback. And, finally, when rows are deleted, you are informed about those deletions in your onDataRemoved(...) callback.
When you don't want to get updates for your data set anymore, you unsubscribe from that set. This is optional.
With the Android-DDP library in your Android application, it translates to the following:
final String subscriptionId = mMeteor.subscribe("chats");
public void onDataAdded(String collection, String docID, String json) { ... }
mMeteor.unsubscribe(subscriptionId);
As you can see, what you have to learn is really Meteor and not the library Android-DDP. Meteor has some new concepts that one has to understand. But when you know how Meteor works, translating those things to Android-DDP is really simple and only a matter of looking up the method names.
I would like to clone Android Contacts Phone into my own SQLite db. In order to save time, the cloning should be triggered when the single contact is newly created or being updated in the Android system. Thus, I want to have "last modified time" of each contact.
For API level 18 or above, it seems that i would get the last modified time of a single person contact by using ContactsContract.Contacts.CONTACT_LAST_UPDATED_TIMESTAMP. However for API Level 17 or below, it seems that there are some discussions before which suggested the use of "ContactsContract.RawContacts.VERSION" or "CONTACT_STATUS_TIMESTAMP".
For "CONTACT_STATUS_TIMESTAMP", it always returns ZERO or null. For "ContactsContract.RawContacts.VERSION", the version remained the same when i updated the photo, phone number or email of a person's contact.
Glad if someone would point out the mistakes i have made...
Reference:
How to get the last modification date for Contacts list (Add/Delete/Modify)
You can get notified when something changes using a ContentObserver. You will then need to retrieve the correct contact yourself. Of course that means having to have your app open when the contact changes or (more reasonably) running a background service.
In your service, create a content observer:
myObserver = new ContentObserver(new Handler()) {
#Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
if (!selfChange) {
//Note: when this (older) callback is used you need to loop through
//and find the contact yourself (by using the dirty field)
}
}
#Override
public void onChange(boolean selfChange, Uri uri) {
super.onChange(selfChange, uri);
if (!selfChange) {
//Note: if you receive a uri, it has contact id
long rawContactId = ContentUris.parseId(uri);
//Note: be careful which thread you are on here (dependent on handler)
}
}
};
//NOTE: Then you need to remember to register and unregister the observer.
//getContentResolver().registerContentObserver(ContactsContract.Contacts.CONTENT_URI, true, myObserver);
//getContentResolver().unregisterContentObserver(myObserver);
Your suggestion to use dirty is not a good solution alone, as this only temporarily indicates that the aggregate contact (owner) should be updated because something in the RawContact changed. This means if the contact gets synced before your app gets opened, dirty is already false (0) again.
Also note that the documentation for the column mentions that it was added in API 18, so as you are aware, it is only below 18 that you need a workaround. so the first step is to make sure you are using the column when you can
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
//continue with existing code
} else {
//use workaround (start service, or at least register observer)
}
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.
What is difference between ContentObserver and DatasetObserver?
When one or another should be used?
I get Cursor with single row. I want to be notified about data changes - eg. when row is updated.
Which observer class should I register?
If you are using a ContentProvider (via ContentResolver or Activity.managedQuery()) to get your data, simply attach a ContentObserver to your Cursor. The code in onChange() will be called whenever the ContentResolver broadcasts a notification for the Uri associated with your cursor.
Cursor myCursor = managedQuery(myUri, projection, where, whereArgs, sortBy);
myCursor.registerContentObserver(new ContentObserver() {
#Override
public void onChange(boolean selfChange) {
// This cursor's Uri has been notified of a change
// Call cursor.requery() or run managedQuery() again
}
#Override
public boolean deliverSelfNotifications() {
return true;
}
}
Make sure your ContentProvider is a "good citizen" and registers the Uri with the cursor after a query:
cursor.setNotificationUri(getContentResolver(), uri);
It should also notify the ContentResolver of any changes to the underlying data (for instance, during insert, delete, and update operations on your SQLite database):
getContentResolver().notifyChange(uri, null);
This approach is a good example of the Observer Pattern of object-oriented design.
I'm not sure if this question is still on anyone's radar. I have been struggling with the same question for a little while now. What I came up with as my litmus test for deciding whether to use a DataSet Observer or a ContentObserver is pretty straight-forward:
If I need to send a URI in my notification I use a ContentObserver. If I simply need to notify one object that another object has changed -- I use a DataSetObserver.
The delimiting factor, for me at least, is does the object that is sending out the notification expose it's underlying resources (be they objects, records, queries, or cursors) as "Universal Resource Identifiers" to the rest of the application; or does the object hide the source of its data.
To provide the supplement to ptc's answer, DataSetObserver is used for handling content changes in the Adapter, for example, it can be used for updating listview dynamically with Adapter. You can register a DataSetObserver using the Adapter#registerDataSetObserver() method.
DataSetObserver can also be used to observe the content changes in the Cursor in a similar fashion.
From my last app developed I can say.
The main difference between ContentObserver and DataSetObserver, is that ContentObserver makes to Observer any change affects on ContentProvider. On the other hand, DataSetObserver Observer any change effect on the database.