Skip to content

Instantly share code, notes, and snippets.

@chalup
Created August 13, 2014 18:57
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save chalup/4201307da02b9cfe4f40 to your computer and use it in GitHub Desktop.
Save chalup/4201307da02b9cfe4f40 to your computer and use it in GitHub Desktop.
CursorWrapper which a) allows setting multiple notification Uris on a Cursor and b) sets the "dirty" state after ContentResolver notification and dispatches change on ContentObserver registration
package com.futuresimple.base.provider;
import com.google.common.collect.Iterables;
import android.content.ContentResolver;
import android.database.ContentObservable;
import android.database.ContentObserver;
import android.database.Cursor;
import android.database.CursorWrapper;
import android.net.Uri;
import android.os.Build;
import java.lang.ref.WeakReference;
import java.util.Collections;
import java.util.LinkedHashSet;
public class MultiUriCursorWrapper extends CursorWrapper {
public MultiUriCursorWrapper(Cursor cursor) {
super(cursor);
}
protected boolean mClosed;
protected ContentResolver mContentResolver;
private final LinkedHashSet<Uri> mNotifyUris = new LinkedHashSet<Uri>();
private final Object mSelfObserverLock = new Object();
private ContentObserver mSelfObserver;
private boolean mSelfObserverRegistered;
private final LinkedHashSet<Uri> mChangedByUris = new LinkedHashSet<Uri>();
private final ContentObservable mContentObservable = new ContentObservable();
@Override
public void deactivate() {
onDeactivateOrClose();
super.deactivate();
}
protected void onDeactivateOrClose() {
if (mSelfObserver != null) {
mContentResolver.unregisterContentObserver(mSelfObserver);
mSelfObserverRegistered = false;
}
}
@Override
public boolean requery() {
if (mSelfObserver != null && !mSelfObserverRegistered) {
for (Uri notifyUri : mNotifyUris) {
mContentResolver.registerContentObserver(notifyUri, true, mSelfObserver);
}
mSelfObserverRegistered = true;
}
boolean success = super.requery();
if (success) {
mChangedByUris.clear();
}
return success;
}
@Override
public boolean isClosed() {
return super.isClosed() && mClosed;
}
@Override
public void close() {
super.close();
mClosed = true;
mContentObservable.unregisterAll();
onDeactivateOrClose();
}
@SuppressWarnings("deprecation")
@Override
public void registerContentObserver(ContentObserver observer) {
mContentObservable.registerObserver(observer);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
for (Uri changedByUri : mChangedByUris) {
observer.dispatchChange(false, changedByUri);
}
} else {
if (!mChangedByUris.isEmpty()) {
observer.dispatchChange(false);
}
}
}
@Override
public void unregisterContentObserver(ContentObserver observer) {
// cursor will unregister all observers when it close
if (!mClosed) {
mContentObservable.unregisterObserver(observer);
}
}
@SuppressWarnings("deprecation")
private void onChange(boolean selfChange, Uri uri) {
synchronized (mSelfObserverLock) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
mContentObservable.dispatchChange(selfChange, uri);
} else {
mContentObservable.dispatchChange(selfChange);
}
if (selfChange) {
for (Uri notifyUri : mNotifyUris) {
mContentResolver.notifyChange(notifyUri, mSelfObserver);
}
}
if (!selfChange) {
mChangedByUris.add(uri);
}
}
}
public MultiUriCursorWrapper withNotificationUris(ContentResolver cr, Iterable<Uri> uris) {
synchronized (mSelfObserverLock) {
Iterables.addAll(mNotifyUris, uris);
mContentResolver = cr;
if (mSelfObserver == null) {
mSelfObserver = new SelfContentObserver(this);
}
for (Uri uri : uris) {
mContentResolver.registerContentObserver(uri, true, mSelfObserver);
}
mSelfObserverRegistered = true;
}
return this;
}
public MultiUriCursorWrapper withNotificationUri(ContentResolver cr, Uri uri) {
return withNotificationUris(cr, Collections.singletonList(uri));
}
public LinkedHashSet<Uri> getNotificationUris() {
return mNotifyUris;
}
@Override
public void setNotificationUri(ContentResolver cr, Uri notifyUri) {
withNotificationUri(cr, notifyUri);
}
@Override
public Uri getNotificationUri() {
synchronized (mSelfObserverLock) {
return Iterables.getFirst(mNotifyUris, null);
}
}
@Override
protected void finalize() {
try {
super.finalize();
if (mSelfObserver != null && mSelfObserverRegistered) {
mContentResolver.unregisterContentObserver(mSelfObserver);
}
if (!mClosed) close();
} catch (Throwable e) {
// ignored
}
}
private static class SelfContentObserver extends ContentObserver {
WeakReference<MultiUriCursorWrapper> mCursor;
public SelfContentObserver(MultiUriCursorWrapper cursor) {
super(null);
mCursor = new WeakReference<MultiUriCursorWrapper>(cursor);
}
@Override
public boolean deliverSelfNotifications() {
return false;
}
@Override
public void onChange(boolean selfChange, Uri uri) {
MultiUriCursorWrapper cursor = mCursor.get();
if (cursor != null) {
cursor.onChange(false, uri);
}
}
@Override
public void onChange(boolean selfChange) {
onChange(selfChange, null);
}
}
}
@varyoo
Copy link

varyoo commented Aug 7, 2015

Awesome.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment