1

I have a ContentObserver onChange() declared as a subclasse in my activity. But it always returns false. Can anyone tell me why?

(Update) This code must call the fillList if the CallLog content provider changes. I mean, if I make a new call, so the data of the call will be inserted in the content provider, so it must return to the observer that something has changed there, so it will call the fillList().But it always return false, even If I make a new call on the emulator.

Here is the code.

    public class RatedCalls extends ListActivity {

private static final String LOG_TAG = "RatedCallsObserver";
private Handler handler = new Handler();
private RatedCallsContentObserver callsObserver = null;
private SQLiteDatabase db;
private CallDataHelper dh = null;
StringBuilder sb = new StringBuilder();
OpenHelper openHelper = new OpenHelper(RatedCalls.this);

class RatedCallsContentObserver extends ContentObserver {
    public RatedCallsContentObserver(Handler h) {
        super(h);
    }

    public void onChange(boolean selfChange) {
        Log.d(LOG_TAG, "RatedCallsContentObserver.onChange( " + selfChange
                + ")");

    }
}

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    registerContentObservers();
    fillList();
}

@Override
public void onStart() {

    super.onStart();
    registerContentObservers();

}

@Override
public void onStop() {

    super.onStop();
    unregisterContentObservers();

}

private void fillList() {

    Cursor cursor = getContentResolver().query(
            android.provider.CallLog.Calls.CONTENT_URI, null, null, null,
            android.provider.CallLog.Calls.DATE + " DESC ");

    cursor.setNotificationUri(getBaseContext().getContentResolver(),
            android.provider.CallLog.Calls.CONTENT_URI);

    dh = new CallDataHelper(this);
    db = openHelper.getWritableDatabase();

    startManagingCursor(cursor);
    int numberColumnId = cursor
            .getColumnIndex(android.provider.CallLog.Calls.NUMBER);
    int durationId = cursor
            .getColumnIndex(android.provider.CallLog.Calls.DURATION);
    int contactNameId = cursor
            .getColumnIndex(android.provider.CallLog.Calls.CACHED_NAME);
    int dateId = cursor.getColumnIndex(android.provider.CallLog.Calls.DATE);
    int numTypeId = cursor
            .getColumnIndex(android.provider.CallLog.Calls.CACHED_NUMBER_TYPE);
    // int contactIdColumnId =
    // cursor.getColumnIndex(android.provider.ContactsContract.RawContacts.CONTACT_ID);

    Date dt = new Date();
    int hours = dt.getHours();
    int minutes = dt.getMinutes();
    int seconds = dt.getSeconds();
    String currTime = hours + ":" + minutes + ":" + seconds;

    ArrayList<String> callList = new ArrayList<String>();
    if (cursor.moveToFirst()) {

        do {
            String contactNumber = cursor.getString(numberColumnId);
            String contactName = cursor.getString(contactNameId);
            String duration = cursor.getString(durationId);
            String callDate = DateFormat.getDateInstance().format(dateId);
            String numType = cursor.getString(numTypeId);

            ContentValues values = new ContentValues();

            values.put("contact_id", 1);
            values.put("contact_name", contactName);
            values.put("number_type", numType);
            values.put("contact_number", contactNumber);
            values.put("duration", duration);
            values.put("date", callDate);
            values.put("current_time", currTime);
            values.put("cont", 1);

            getBaseContext().getContentResolver().notifyChange(
                    android.provider.CallLog.Calls.CONTENT_URI, null);

            callList.add("Contact Number: " + contactNumber
                    + "\nContact Name: " + contactName + "\nDuration: "
                    + duration + "\nDate: " + callDate);
            this.db.insert(CallDataHelper.TABLE_NAME, null, values);
            Toast.makeText(getBaseContext(), "Inserted!", Toast.LENGTH_LONG);

        } while (cursor.moveToNext());
        setListAdapter(new ArrayAdapter<String>(this, R.layout.listitem,
                callList));
        ListView lv = getListView();
        lv.setTextFilterEnabled(true);

        lv.setOnItemClickListener(new android.widget.AdapterView.OnItemClickListener() {

            @Override
            public void onItemClick(AdapterView<?> parent, View view,
                    int position, long id) {

                Toast.makeText(getApplicationContext(),
                        ((TextView) view).getText(), Toast.LENGTH_SHORT)
                        .show();

            }
        });
    }
}

private void registerContentObservers() {
    ContentResolver cr = getContentResolver();
    callsObserver = new RatedCallsContentObserver(handler);
    cr.registerContentObserver(android.provider.CallLog.Calls.CONTENT_URI,
            true, callsObserver);
}

private void unregisterContentObservers() {

    ContentResolver cr = getContentResolver();
    if (callsObserver != null) { // just paranoia
        cr.unregisterContentObserver(callsObserver);
        callsObserver = null;
    }

}
}
rogcg
  • 10,451
  • 20
  • 91
  • 133

1 Answers1

2

The function doesn't return false, because it doesn't return anything. Its return type is void. It receives false as a parameter.

Why?

Well, I typed 'android onchange' into Google and selected the first result, and found the following:

This method is called when a change occurs to the cursor that is being observed.
Parameters
selfChange    true if the update was caused by a call to commit on the cursor
              that is being observed. 

So all that happened is that the cursor was changed, and not by calling its .commit() method. You will only log a 'true' input to this function if .commit() is called.

Carl Manaster
  • 39,912
  • 17
  • 102
  • 155
Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153
  • My point was more "this is how you apply some basic problem-solving skills to at least get a better idea of what is going on". But anyway... even if you call `.commit()` somewhere, that just results in another call to `onChange(true)`; it doesn't prevent or cancel out the call to `onChange(false)` that is being triggered by something else. So what I'm wondering at this point is, why does the fact that the parameter is `false` bother you? Do you know why the function is being called at all? Then you should already know what to do inside the function when it is called. – Karl Knechtel Dec 13 '10 at 17:59
  • And the method onChange is called when a change occurs to the cursor that is being observed. So I parse the URI of the content provider. And a change is happening to the cursor, because I did a new call, and the cursor, get the new data. but it gets the new data, with all the old data existing there in the content provider. So if I get the new data, its cause a change occurs. And why onChange is not called? Inside the onChange method, I will call the fillList method, that is the method that retrieve the data from the content provider and insert this data in the database. Got it? – rogcg Dec 13 '10 at 18:10
  • ... So you know that the function isn't being called in this particular situation, but you don't know when it is being called, only that it is, in fact, being called? What? – Karl Knechtel Dec 13 '10 at 18:17
  • It calls onChange() automatically parsing true if some change occurs to the cursor is being watched? What parse the true for the onChange()? Some method? Because I call the notifyChange and everything else, but I dont know, what should parse the parameter for the onChange() – rogcg Dec 13 '10 at 18:18
  • Not parse, pass. As in, that is the value that the parameter has, and the value that you see in your log. You don't have any say in the value that is passed. The Android library figures out why onChange() is being called, when it calls onChange() (notice how you don't call onChange() yourself, but notifyChange() instead - there is a good reason for that), and passes the correct value accordingly. – Karl Knechtel Dec 13 '10 at 18:25
  • So the notifyChange() calls onChange()?? So it should call, because I'm using the notifyChange(). getBaseContext().getContentResolver().notifyChange( android.provider.CallLog.Calls.CONTENT_URI, null); Unless I'm using the wrong URI. So it won't detect the right content provider to listen for changes, and it returns always false. But If I want to get the last change on the CallLog.Calls content provider, I should pass this URI right? Or am I using the wrong URI. (I dont think its wrong). – rogcg Dec 13 '10 at 18:28
  • Beyond this point I have no idea about the details... you will have to do some more research for yourself. I can only explain how this kind of callback architecture works, because it is common for APIs to do this kind of thing. – Karl Knechtel Dec 13 '10 at 18:35
  • When I run the application, it insert in the database the same data, even if nothing has changed in the content provider. I need to know how to implement this ContentObserver. Where to put the notifyChange() and and when call it! – rogcg Dec 14 '10 at 00:10
  • @KarlKnechtel: Wow man, what a rally! This guy has lost me from his very first line in the OP, and you have managed to actually answer him and his comments too! +1 for tenacity – kellogs Mar 19 '12 at 17:46