Skip to content

Instantly share code, notes, and snippets.

@magillus
Last active April 18, 2020 21:46
Show Gist options
  • Save magillus/4b7470458fa59e30bb2fe20fe3a059b0 to your computer and use it in GitHub Desktop.
Save magillus/4b7470458fa59e30bb2fe20fe3a059b0 to your computer and use it in GitHub Desktop.
RxContentObserver, wraps ContentObserver registration and on change with RxJava Observable that will emit updates.
package com.example.mat.rxutil;
import android.content.ContentResolver;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
import java.lang.ref.WeakReference;
import rx.Observable;
import rx.Subscriber;
import rx.Subscription;
import rx.functions.Action1;
import rx.functions.Func0;
import rx.functions.Func1;
import timber.log.Timber;
/**
* Subscriber that registers and un-registeres to content resolver itself as observer.
* On Subscription it will register observer based on passed 'observeUr' and unregister with un-subscribe.
* Copyright 2016 Mateusz Perlak - http://www.apache.org/licenses/LICENSE-2.0
* Created on 10/28/16.
*/
public abstract class ContentObserverSubscriber<T> implements Observable.OnSubscribe<T>, Subscription {
/**
* Weak reference to content resolver
*/
protected final WeakReference<ContentResolver> contentResolverRef;
/**
* Observed URI.
*/
protected final Uri observedUri;
/**
* Handler thread on which the observed changes will happen.
*/
protected HandlerThread handlerThread;
/**
* Content Observer.
*/
protected ContentObserver contentObserver;
/**
* Emits first value if true.
*/
protected boolean emitFirstValue = true;
/**
* Flag if the content observer was unregistered.
*/
private volatile boolean isObserverRegistered = false;
/**
* Subscriber instance.
*/
private Subscriber<? super T> subscriber;
/**
* Un-subscribes, with content observer un-registration.
* Stops HandlerThread
*/
@Override
public void unsubscribe() {
if (contentResolverRef != null && contentResolverRef.get() != null && isObserverRegistered) {
contentResolverRef.get().unregisterContentObserver(contentObserver);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
handlerThread.quitSafely();
} else {
handlerThread.quit();
}
handlerThread = null;
}
isObserverRegistered = false;
}
/**
* Is subscriber unsubscribed.
*
* @return
*/
@Override
public boolean isUnsubscribed() {
return isObserverRegistered;
}
/**
* Creates content observer observable. It will emit on any changes of the content and also emit first value.
*
* @param resolver ContentResolver instance for registration/un-registration
* @param observedUri Observed content URI
* @param fetchStatusFun method to fetch a type from content provider or from the source
* @param <T> Type of the Observable
* @return
*/
public static <T> Observable<T> create(ContentResolver resolver, Uri observedUri, Func1<Uri, T> fetchStatusFun) {
return Observable.defer(() -> Observable.create(new ContentObserverSubscriber<T>(resolver, observedUri) {
@Override
protected T fetchItem(Uri itemUri) {
return fetchStatusFun.call(itemUri);
}
}));
}
/**
* Creates instance of {@link ContentObserverSubscriber} for observed Uri.
* It will emit first item on subscription.
*
* @param resolver
* @param observedUri observed Uri
*/
public ContentObserverSubscriber(ContentResolver resolver, Uri observedUri) {
this(resolver, observedUri, true);
}
/**
* Creates instance of {@link ContentObserverSubscriber} for observed Uri.
* it may emit first value on subscription if {@param emitFirstValue} is true.
*
* @param resolver
* @param observedUri observed Uri
* @param emitFirstValue true will emit first value on subscription
*/
public ContentObserverSubscriber(ContentResolver resolver, Uri observedUri, boolean emitFirstValue) {
this.emitFirstValue = emitFirstValue;
this.contentResolverRef = new WeakReference<>(resolver);
this.observedUri = observedUri;
handlerThread = new HandlerThread("ContentObserverThread");
handlerThread.start();
contentObserver = new ContentObserver(new Handler(handlerThread.getLooper())) {
@Override
public void onChange(boolean selfChange, Uri uri) {
subscriber.onNext(fetchItem(uri));
}
};
}
/**
* Subscriber subscription call.
return ContentObserverSubscriber.create(getContentResolver(), TestContentProvider.fetchDataUri(), (changeUri) -> {
Cursor valueCursor = getContentResolver().query(changeUri, null, null, null, null);
if (valueCursor != null && valueCursor.moveToFirst()) {
return valueCursor.getString(1);
}
// todo ofcourse close the cursor
return "";
});
@arekolek
Copy link

looks like ContentObserverSubscriber.java file got cut off at line 138

@kevinmcampos
Copy link

For future reference, see his other Gist that is more complete: https://gist.github.com/magillus/25cfabd3542b31dd71879c062e38f6f1

@magillus
Copy link
Author

I totally missed the 2018's comment - sorry about that.
Thank you Kevin.

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