Last active
April 18, 2020 21:46
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 ""; | |
}); |
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
For future reference, see his other Gist that is more complete: https://gist.github.com/magillus/25cfabd3542b31dd71879c062e38f6f1