Skip to content

Instantly share code, notes, and snippets.

@chalup
Created August 13, 2014 18:57

Revisions

  1. chalup created this gist Aug 13, 2014.
    204 changes: 204 additions & 0 deletions MultiUriCursorWrapper.java
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,204 @@
    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);
    }
    }
    }