├── sample ├── .gitignore ├── src │ └── main │ │ ├── res │ │ ├── drawable-hdpi │ │ │ └── ic_launcher.png │ │ ├── drawable-mdpi │ │ │ └── ic_launcher.png │ │ ├── drawable-xhdpi │ │ │ └── ic_launcher.png │ │ ├── drawable-xxhdpi │ │ │ └── ic_launcher.png │ │ ├── values │ │ │ ├── strings.xml │ │ │ └── styles.xml │ │ └── layout │ │ │ ├── activity_places.xml │ │ │ ├── activity_places_result.xml │ │ │ ├── activity_main.xml │ │ │ ├── activity_mocklocations.xml │ │ │ └── activity_geofence.xml │ │ ├── java │ │ └── pl │ │ │ └── charmas │ │ │ └── android │ │ │ └── reactivelocation2 │ │ │ └── sample │ │ │ ├── utils │ │ │ ├── UnsubscribeIfPresent.java │ │ │ ├── DisplayTextOnViewAction.java │ │ │ ├── LocationToStringFunc.java │ │ │ ├── ToMostProbableActivity.java │ │ │ ├── AddressToStringFunc.java │ │ │ ├── DetectedActivityToString.java │ │ │ ├── RxTextView.java │ │ │ └── TextViewTextOnSubscribe.java │ │ │ ├── BaseActivity.java │ │ │ ├── GeofenceBroadcastReceiver.java │ │ │ ├── PlacesResultActivity.java │ │ │ ├── GeofenceActivity.java │ │ │ ├── PlacesActivity.java │ │ │ ├── MockLocationsActivity.java │ │ │ └── MainActivity.java │ │ └── AndroidManifest.xml ├── proguard-rules.txt └── build.gradle ├── android-reactive-location ├── .gitignore ├── gradle.properties ├── src │ └── main │ │ ├── AndroidManifest.xml │ │ └── java │ │ └── pl │ │ └── charmas │ │ └── android │ │ └── reactivelocation2 │ │ ├── observables │ │ ├── BaseLocationObservableOnSubscribe.java │ │ ├── GoogleAPIConnectionSuspendedException.java │ │ ├── StatusException.java │ │ ├── activity │ │ │ ├── BaseActivityObservableOnSubscribe.java │ │ │ └── ActivityUpdatesObservableOnSubscribe.java │ │ ├── GoogleAPIConnectionException.java │ │ ├── ObservableContext.java │ │ ├── ObservableEmitterWrapper.java │ │ ├── GoogleAPIClientObservableOnSubscribe.java │ │ ├── location │ │ │ ├── LastKnownLocationObservableOnSubscribe.java │ │ │ ├── RemoveLocationIntentUpdatesObservableOnSubscribe.java │ │ │ ├── AddLocationIntentUpdatesObservableOnSubscribe.java │ │ │ ├── LocationUpdatesObservableOnSubscribe.java │ │ │ └── MockLocationObservableOnSubscribe.java │ │ ├── PendingResultObservableOnSubscribe.java │ │ ├── ObservableFactory.java │ │ ├── geofence │ │ │ ├── RemoveGeofenceObservableOnSubscribe.java │ │ │ ├── RemoveGeofenceByPendingIntentObservableOnSubscribe.java │ │ │ ├── RemoveGeofenceRequestIdsObservableOnSubscribe.java │ │ │ └── AddGeofenceObservableOnSubscribe.java │ │ ├── geocode │ │ │ ├── ReverseGeocodeObservable.java │ │ │ ├── GeocodeObservable.java │ │ │ └── FallbackReverseGeocodeObservable.java │ │ └── BaseObservableOnSubscribe.java │ │ ├── DataBufferObservable.java │ │ ├── ReactiveLocationProviderConfiguration.java │ │ └── ReactiveLocationProvider.java └── build.gradle ├── settings.gradle ├── .gitignore ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradle.properties ├── CHANGELOG.md ├── gradlew.bat ├── maven_push.gradle ├── gradlew └── README.md /sample/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /android-reactive-location/.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':sample', ':android-reactive-location' 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | /.idea/ 3 | /local.properties 4 | /.idea 5 | build/ 6 | .DS_Store 7 | *.iml 8 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcharmas/Android-ReactiveLocation/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /android-reactive-location/gradle.properties: -------------------------------------------------------------------------------- 1 | POM_NAME=Android ReactiveLocation 2 | POM_ARTIFACT_ID=android-reactive-location2 3 | POM_PACKAGING=aar 4 | -------------------------------------------------------------------------------- /sample/src/main/res/drawable-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcharmas/Android-ReactiveLocation/HEAD/sample/src/main/res/drawable-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample/src/main/res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcharmas/Android-ReactiveLocation/HEAD/sample/src/main/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample/src/main/res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcharmas/Android-ReactiveLocation/HEAD/sample/src/main/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample/src/main/res/drawable-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcharmas/Android-ReactiveLocation/HEAD/sample/src/main/res/drawable-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | ReactiveLocation 4 | 5 | -------------------------------------------------------------------------------- /android-reactive-location/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /sample/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Sep 29 10:11:52 CEST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip 7 | -------------------------------------------------------------------------------- /android-reactive-location/src/main/java/pl/charmas/android/reactivelocation2/observables/BaseLocationObservableOnSubscribe.java: -------------------------------------------------------------------------------- 1 | package pl.charmas.android.reactivelocation2.observables; 2 | 3 | import com.google.android.gms.location.LocationServices; 4 | 5 | public abstract class BaseLocationObservableOnSubscribe extends BaseObservableOnSubscribe { 6 | protected BaseLocationObservableOnSubscribe(ObservableContext ctx) { 7 | super(ctx, LocationServices.API); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /android-reactive-location/src/main/java/pl/charmas/android/reactivelocation2/observables/GoogleAPIConnectionSuspendedException.java: -------------------------------------------------------------------------------- 1 | package pl.charmas.android.reactivelocation2.observables; 2 | 3 | public class GoogleAPIConnectionSuspendedException extends RuntimeException { 4 | private final int cause; 5 | 6 | GoogleAPIConnectionSuspendedException(int cause) { 7 | this.cause = cause; 8 | } 9 | 10 | public int getErrorCause() { 11 | return cause; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /sample/src/main/java/pl/charmas/android/reactivelocation2/sample/utils/UnsubscribeIfPresent.java: -------------------------------------------------------------------------------- 1 | package pl.charmas.android.reactivelocation2.sample.utils; 2 | 3 | import io.reactivex.disposables.Disposable; 4 | 5 | public final class UnsubscribeIfPresent { 6 | private UnsubscribeIfPresent() {//no instance 7 | } 8 | 9 | public static void dispose(Disposable disposable) { 10 | if (disposable != null && !disposable.isDisposed()) { 11 | disposable.dispose(); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /android-reactive-location/src/main/java/pl/charmas/android/reactivelocation2/observables/StatusException.java: -------------------------------------------------------------------------------- 1 | package pl.charmas.android.reactivelocation2.observables; 2 | 3 | import com.google.android.gms.common.api.Status; 4 | 5 | public class StatusException extends Throwable { 6 | private final Status status; 7 | 8 | public StatusException(Status status) { 9 | super(status.getStatusCode() + ": " + status.getStatusMessage()); 10 | this.status = status; 11 | } 12 | 13 | public Status getStatus() { 14 | return status; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /sample/src/main/java/pl/charmas/android/reactivelocation2/sample/utils/DisplayTextOnViewAction.java: -------------------------------------------------------------------------------- 1 | package pl.charmas.android.reactivelocation2.sample.utils; 2 | 3 | import android.widget.TextView; 4 | 5 | import io.reactivex.functions.Consumer; 6 | 7 | public class DisplayTextOnViewAction implements Consumer { 8 | private final TextView target; 9 | 10 | public DisplayTextOnViewAction(TextView target) { 11 | this.target = target; 12 | } 13 | 14 | @Override 15 | public void accept(String s) throws Exception { 16 | target.setText(s); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /sample/src/main/java/pl/charmas/android/reactivelocation2/sample/utils/LocationToStringFunc.java: -------------------------------------------------------------------------------- 1 | package pl.charmas.android.reactivelocation2.sample.utils; 2 | 3 | import android.location.Location; 4 | 5 | import io.reactivex.functions.Function; 6 | 7 | public class LocationToStringFunc implements Function { 8 | @Override 9 | public String apply(Location location) { 10 | if (location != null) 11 | return location.getLatitude() + " " + location.getLongitude() + " (" + location.getAccuracy() + ")"; 12 | return "no location available"; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /sample/src/main/java/pl/charmas/android/reactivelocation2/sample/utils/ToMostProbableActivity.java: -------------------------------------------------------------------------------- 1 | package pl.charmas.android.reactivelocation2.sample.utils; 2 | 3 | import com.google.android.gms.location.ActivityRecognitionResult; 4 | import com.google.android.gms.location.DetectedActivity; 5 | 6 | import io.reactivex.functions.Function; 7 | 8 | public class ToMostProbableActivity implements Function { 9 | @Override 10 | public DetectedActivity apply(ActivityRecognitionResult activityRecognitionResult) { 11 | return activityRecognitionResult.getMostProbableActivity(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /android-reactive-location/src/main/java/pl/charmas/android/reactivelocation2/observables/activity/BaseActivityObservableOnSubscribe.java: -------------------------------------------------------------------------------- 1 | package pl.charmas.android.reactivelocation2.observables.activity; 2 | 3 | import com.google.android.gms.location.ActivityRecognition; 4 | 5 | import pl.charmas.android.reactivelocation2.observables.BaseObservableOnSubscribe; 6 | import pl.charmas.android.reactivelocation2.observables.ObservableContext; 7 | 8 | abstract class BaseActivityObservableOnSubscribe extends BaseObservableOnSubscribe { 9 | BaseActivityObservableOnSubscribe(ObservableContext ctx) { 10 | super(ctx, ActivityRecognition.API); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /sample/src/main/java/pl/charmas/android/reactivelocation2/sample/utils/AddressToStringFunc.java: -------------------------------------------------------------------------------- 1 | package pl.charmas.android.reactivelocation2.sample.utils; 2 | 3 | import android.location.Address; 4 | 5 | import io.reactivex.functions.Function; 6 | 7 | public class AddressToStringFunc implements Function { 8 | @Override 9 | public String apply(Address address) { 10 | if (address == null) return ""; 11 | 12 | String addressLines = ""; 13 | for (int i = 0; i <= address.getMaxAddressLineIndex(); i++) { 14 | addressLines += address.getAddressLine(i) + '\n'; 15 | } 16 | return addressLines; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /android-reactive-location/src/main/java/pl/charmas/android/reactivelocation2/observables/GoogleAPIConnectionException.java: -------------------------------------------------------------------------------- 1 | package pl.charmas.android.reactivelocation2.observables; 2 | 3 | import com.google.android.gms.common.ConnectionResult; 4 | 5 | public class GoogleAPIConnectionException extends RuntimeException { 6 | private final ConnectionResult connectionResult; 7 | 8 | GoogleAPIConnectionException(String detailMessage, ConnectionResult connectionResult) { 9 | super(detailMessage); 10 | this.connectionResult = connectionResult; 11 | } 12 | 13 | public ConnectionResult getConnectionResult() { 14 | return connectionResult; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | VERSION_NAME=2.1 2 | VERSION_CODE=102 3 | GROUP=pl.charmas.android 4 | 5 | 6 | POM_DESCRIPTION=Small library that wraps Google Play Service API in brilliant RxJava Observables reducing boilerplate to minimum. 7 | POM_URL=https://github.com/mcharmas/Android-ReactiveLocation 8 | POM_SCM_URL=https://github.com/mcharmas/Android-ReactiveLocation 9 | POM_SCM_CONNECTION=scm:git@github.com:mcharmas/Android-ReactiveLocation.git 10 | POM_SCM_DEV_CONNECTION=scm:git@github.com:mcharmas/Android-ReactiveLocation.git 11 | POM_LICENCE_NAME=The Apache Software License, Version 2.0 12 | POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt 13 | POM_LICENCE_DIST=repo 14 | POM_DEVELOPER_ID=mcharmas 15 | POM_DEVELOPER_NAME=Michal Charmas 16 | -------------------------------------------------------------------------------- /sample/proguard-rules.txt: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /home/orbit/utils/android-studio/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the ProGuard 5 | # include property in project.properties. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} -------------------------------------------------------------------------------- /android-reactive-location/src/main/java/pl/charmas/android/reactivelocation2/observables/ObservableContext.java: -------------------------------------------------------------------------------- 1 | package pl.charmas.android.reactivelocation2.observables; 2 | 3 | import android.content.Context; 4 | import android.os.Handler; 5 | 6 | import pl.charmas.android.reactivelocation2.ReactiveLocationProviderConfiguration; 7 | 8 | public class ObservableContext { 9 | private final Context context; 10 | private final Handler handler; 11 | private final boolean retryOnConnectionSuspended; 12 | 13 | public ObservableContext(Context context, ReactiveLocationProviderConfiguration configuration) { 14 | this.context = context; 15 | this.handler = configuration.getCustomCallbackHandler(); 16 | this.retryOnConnectionSuspended = configuration.isRetryOnConnectionSuspended(); 17 | } 18 | 19 | public Context getContext() { 20 | return context; 21 | } 22 | 23 | Handler getHandler() { 24 | return handler; 25 | } 26 | 27 | boolean isRetryOnConnectionSuspended() { 28 | return retryOnConnectionSuspended; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /android-reactive-location/src/main/java/pl/charmas/android/reactivelocation2/observables/ObservableEmitterWrapper.java: -------------------------------------------------------------------------------- 1 | package pl.charmas.android.reactivelocation2.observables; 2 | 3 | import io.reactivex.ObservableEmitter; 4 | import io.reactivex.Observer; 5 | import io.reactivex.disposables.Disposable; 6 | 7 | public class ObservableEmitterWrapper implements Observer { 8 | private final ObservableEmitter emitter; 9 | 10 | public ObservableEmitterWrapper(ObservableEmitter emitter) { 11 | this.emitter = emitter; 12 | } 13 | 14 | @Override 15 | public void onSubscribe(Disposable d) { 16 | } 17 | 18 | @Override 19 | public void onNext(T t) { 20 | if (!emitter.isDisposed()){ 21 | emitter.onNext(t); 22 | } 23 | } 24 | 25 | @Override 26 | public void onError(Throwable e) { 27 | if (!emitter.isDisposed()) { 28 | emitter.onError(e); 29 | } 30 | } 31 | 32 | @Override 33 | public void onComplete() { 34 | if (!emitter.isDisposed()) { 35 | emitter.onComplete(); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /sample/src/main/java/pl/charmas/android/reactivelocation2/sample/BaseActivity.java: -------------------------------------------------------------------------------- 1 | package pl.charmas.android.reactivelocation2.sample; 2 | 3 | import android.Manifest; 4 | import android.support.v7.app.AppCompatActivity; 5 | import android.widget.Toast; 6 | 7 | import com.tbruyelle.rxpermissions2.RxPermissions; 8 | 9 | import io.reactivex.functions.Consumer; 10 | 11 | public abstract class BaseActivity extends AppCompatActivity { 12 | 13 | @Override 14 | protected void onStart() { 15 | super.onStart(); 16 | new RxPermissions(this) 17 | .request(Manifest.permission.ACCESS_FINE_LOCATION) 18 | .subscribe(new Consumer() { 19 | @Override 20 | public void accept(Boolean granted) throws Exception { 21 | if (granted) { 22 | onLocationPermissionGranted(); 23 | } else { 24 | Toast.makeText(BaseActivity.this, "Sorry, no demo without permission...", Toast.LENGTH_SHORT).show(); 25 | } 26 | } 27 | }); 28 | } 29 | 30 | protected abstract void onLocationPermissionGranted(); 31 | } 32 | -------------------------------------------------------------------------------- /android-reactive-location/src/main/java/pl/charmas/android/reactivelocation2/observables/GoogleAPIClientObservableOnSubscribe.java: -------------------------------------------------------------------------------- 1 | package pl.charmas.android.reactivelocation2.observables; 2 | 3 | import com.google.android.gms.common.api.Api; 4 | import com.google.android.gms.common.api.GoogleApiClient; 5 | 6 | import io.reactivex.Observable; 7 | import io.reactivex.ObservableEmitter; 8 | 9 | public class GoogleAPIClientObservableOnSubscribe extends BaseObservableOnSubscribe { 10 | 11 | @SafeVarargs 12 | public static Observable create(ObservableContext context, ObservableFactory factory, Api... apis) { 13 | return factory.createObservable(new GoogleAPIClientObservableOnSubscribe(context, apis)); 14 | } 15 | 16 | @SafeVarargs 17 | private GoogleAPIClientObservableOnSubscribe(ObservableContext ctx, Api... apis) { 18 | super(ctx, apis); 19 | } 20 | 21 | @Override 22 | protected void onGoogleApiClientReady(GoogleApiClient apiClient, ObservableEmitter emitter) { 23 | if (emitter.isDisposed()) return; 24 | emitter.onNext(apiClient); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /android-reactive-location/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'maven' 3 | 4 | android { 5 | compileSdkVersion rootProject.ext.compileSdkVersion 6 | buildToolsVersion rootProject.ext.buildToolsVersion 7 | 8 | defaultConfig { 9 | minSdkVersion 14 10 | targetSdkVersion rootProject.ext.targetSdkVersion 11 | } 12 | 13 | compileOptions { 14 | sourceCompatibility JavaVersion.VERSION_1_7 15 | targetCompatibility JavaVersion.VERSION_1_7 16 | } 17 | } 18 | 19 | //TODO: local maven deployment 20 | 21 | dependencies { 22 | compile 'com.google.android.gms:play-services-location:11.0.4' 23 | compile 'com.google.android.gms:play-services-places:11.0.4' 24 | compile 'io.reactivex.rxjava2:rxjava:2.0.5' 25 | } 26 | 27 | // Comment this to deploy to local maven repository 28 | apply from: '../maven_push.gradle' 29 | 30 | 31 | //TODO: clean up 32 | //This is for local maven deployment 33 | 34 | //apply plugin: 'maven' 35 | //uploadArchives { 36 | // repositories { 37 | // mavenDeployer { 38 | // repository url: 'file://' + new File(System.getProperty('user.home'), '.m2/repository').absolutePath 39 | // } 40 | // } 41 | //} 42 | //task install(dependsOn: uploadArchives) 43 | -------------------------------------------------------------------------------- /sample/src/main/java/pl/charmas/android/reactivelocation2/sample/utils/DetectedActivityToString.java: -------------------------------------------------------------------------------- 1 | package pl.charmas.android.reactivelocation2.sample.utils; 2 | 3 | import com.google.android.gms.location.DetectedActivity; 4 | 5 | import io.reactivex.functions.Function; 6 | 7 | public class DetectedActivityToString implements Function { 8 | @Override 9 | public String apply(DetectedActivity detectedActivity) { 10 | return getNameFromType(detectedActivity.getType()) + " with confidence " + detectedActivity.getConfidence(); 11 | } 12 | 13 | private String getNameFromType(int activityType) { 14 | switch (activityType) { 15 | case DetectedActivity.RUNNING: 16 | return "running"; 17 | case DetectedActivity.IN_VEHICLE: 18 | return "in_vehicle"; 19 | case DetectedActivity.ON_BICYCLE: 20 | return "on_bicycle"; 21 | case DetectedActivity.ON_FOOT: 22 | return "on_foot"; 23 | case DetectedActivity.STILL: 24 | return "still"; 25 | case DetectedActivity.UNKNOWN: 26 | return "unknown"; 27 | case DetectedActivity.TILTING: 28 | return "tilting"; 29 | } 30 | return "unknown"; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /sample/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | def getGooglePlayServicesApiKey() { 4 | if (REACTIVE_LOCATION_GMS_API_KEY != null && !REACTIVE_LOCATION_GMS_API_KEY.isEmpty()) { 5 | return REACTIVE_LOCATION_GMS_API_KEY; 6 | } 7 | return "" 8 | } 9 | 10 | android { 11 | compileSdkVersion rootProject.ext.compileSdkVersion 12 | buildToolsVersion rootProject.ext.buildToolsVersion 13 | 14 | defaultConfig { 15 | minSdkVersion 14 16 | targetSdkVersion rootProject.ext.targetSdkVersion 17 | versionName project.VERSION_NAME 18 | versionCode Integer.parseInt(project.VERSION_CODE) 19 | resValue "string", "API_KEY", getGooglePlayServicesApiKey() 20 | } 21 | 22 | compileOptions { 23 | sourceCompatibility JavaVersion.VERSION_1_7 24 | targetCompatibility JavaVersion.VERSION_1_7 25 | } 26 | buildTypes { 27 | release { 28 | minifyEnabled false 29 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' 30 | } 31 | } 32 | lintOptions { 33 | abortOnError false 34 | } 35 | } 36 | 37 | dependencies { 38 | compile 'com.android.support:appcompat-v7:25.3.1' 39 | compile 'io.reactivex.rxjava2:rxandroid:2.0.1' 40 | compile 'com.tbruyelle.rxpermissions2:rxpermissions:0.9.1@aar' 41 | compile project(':android-reactive-location') 42 | } 43 | -------------------------------------------------------------------------------- /sample/src/main/java/pl/charmas/android/reactivelocation2/sample/utils/RxTextView.java: -------------------------------------------------------------------------------- 1 | package pl.charmas.android.reactivelocation2.sample.utils; 2 | 3 | import android.support.annotation.CheckResult; 4 | import android.support.annotation.NonNull; 5 | import android.widget.TextView; 6 | 7 | import io.reactivex.Observable; 8 | 9 | 10 | /** 11 | * RxTextView compatible to rxjava2 12 | */ 13 | 14 | public class RxTextView { 15 | 16 | /** 17 | * Create an observable of character sequences for text changes on {@code view}. 18 | *

19 | * Warning: Values emitted by this observable are mutable and owned by the host 20 | * {@code TextView} and thus are not safe to cache or delay reading (such as by observing 21 | * on a different thread). If you want to cache or delay reading the items emitted then you must 22 | * map values through a function which calls {@link String#valueOf} or 23 | * {@link CharSequence#toString() .toString()} to create a copy. 24 | *

25 | * Warning: The created observable keeps a strong reference to {@code view}. Unsubscribe 26 | * to free this reference. 27 | *

28 | * Note: A value will be emitted immediately on subscribe. 29 | */ 30 | @CheckResult 31 | @NonNull 32 | public static Observable textChanges(@NonNull TextView view) { 33 | return Observable.create(new TextViewTextOnSubscribe(view)); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /android-reactive-location/src/main/java/pl/charmas/android/reactivelocation2/observables/location/LastKnownLocationObservableOnSubscribe.java: -------------------------------------------------------------------------------- 1 | package pl.charmas.android.reactivelocation2.observables.location; 2 | 3 | import android.location.Location; 4 | 5 | import com.google.android.gms.common.api.GoogleApiClient; 6 | import com.google.android.gms.location.LocationServices; 7 | 8 | import io.reactivex.Observable; 9 | import io.reactivex.ObservableEmitter; 10 | import pl.charmas.android.reactivelocation2.observables.BaseLocationObservableOnSubscribe; 11 | import pl.charmas.android.reactivelocation2.observables.ObservableContext; 12 | import pl.charmas.android.reactivelocation2.observables.ObservableFactory; 13 | 14 | @SuppressWarnings("MissingPermission") 15 | public class LastKnownLocationObservableOnSubscribe extends BaseLocationObservableOnSubscribe { 16 | public static Observable createObservable(ObservableContext ctx, ObservableFactory factory) { 17 | return factory.createObservable(new LastKnownLocationObservableOnSubscribe(ctx)); 18 | } 19 | 20 | private LastKnownLocationObservableOnSubscribe(ObservableContext ctx) { 21 | super(ctx); 22 | } 23 | 24 | @Override 25 | protected void onGoogleApiClientReady(GoogleApiClient apiClient, ObservableEmitter emitter) { 26 | Location location = LocationServices.FusedLocationApi.getLastLocation(apiClient); 27 | if (emitter.isDisposed()) return; 28 | if (location != null) { 29 | emitter.onNext(location); 30 | } 31 | emitter.onComplete(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /android-reactive-location/src/main/java/pl/charmas/android/reactivelocation2/DataBufferObservable.java: -------------------------------------------------------------------------------- 1 | package pl.charmas.android.reactivelocation2; 2 | 3 | import com.google.android.gms.common.data.AbstractDataBuffer; 4 | 5 | import io.reactivex.Observable; 6 | import io.reactivex.ObservableEmitter; 7 | import io.reactivex.ObservableOnSubscribe; 8 | import io.reactivex.disposables.Disposables; 9 | import io.reactivex.functions.Action; 10 | 11 | 12 | /** 13 | * Util class that creates observable from buffer. 14 | */ 15 | public final class DataBufferObservable { 16 | 17 | private DataBufferObservable() { 18 | //no instance 19 | } 20 | 21 | /** 22 | * Creates observable from buffer. On unsubscribe buffer is automatically released. 23 | * 24 | * @param buffer source buffer 25 | * @param item type 26 | * @return observable that emits all items from buffer and on unsubscription releases it 27 | */ 28 | public static Observable from(final AbstractDataBuffer buffer) { 29 | return Observable.create(new ObservableOnSubscribe() { 30 | 31 | @Override 32 | public void subscribe(final ObservableEmitter emitter) { 33 | for (T item : buffer) { 34 | emitter.onNext(item); 35 | } 36 | emitter.setDisposable(Disposables.fromAction(new Action() { 37 | @Override 38 | public void run() throws Exception { 39 | buffer.release(); 40 | } 41 | })); 42 | } 43 | }); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /sample/src/main/java/pl/charmas/android/reactivelocation2/sample/GeofenceBroadcastReceiver.java: -------------------------------------------------------------------------------- 1 | package pl.charmas.android.reactivelocation2.sample; 2 | 3 | import android.app.Notification; 4 | import android.app.NotificationManager; 5 | import android.content.BroadcastReceiver; 6 | import android.content.Context; 7 | import android.content.Intent; 8 | import android.support.v4.app.NotificationCompat; 9 | 10 | import com.google.android.gms.location.Geofence; 11 | import com.google.android.gms.location.GeofencingEvent; 12 | 13 | public class GeofenceBroadcastReceiver extends BroadcastReceiver { 14 | @Override 15 | public void onReceive(Context context, Intent intent) { 16 | GeofencingEvent event = GeofencingEvent.fromIntent(intent); 17 | String transition = mapTransition(event.getGeofenceTransition()); 18 | 19 | NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); 20 | Notification notification = new NotificationCompat.Builder(context) 21 | .setSmallIcon(R.drawable.ic_launcher) 22 | .setContentTitle("Geofence action") 23 | .setContentText(transition) 24 | .setTicker("Geofence action") 25 | .build(); 26 | nm.notify(0, notification); 27 | } 28 | 29 | private String mapTransition(int event) { 30 | switch (event) { 31 | case Geofence.GEOFENCE_TRANSITION_ENTER: 32 | return "ENTER"; 33 | case Geofence.GEOFENCE_TRANSITION_EXIT: 34 | return "EXIT"; 35 | default: 36 | return "UNKNOWN"; 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /android-reactive-location/src/main/java/pl/charmas/android/reactivelocation2/observables/PendingResultObservableOnSubscribe.java: -------------------------------------------------------------------------------- 1 | package pl.charmas.android.reactivelocation2.observables; 2 | 3 | import android.support.annotation.NonNull; 4 | 5 | import com.google.android.gms.common.api.PendingResult; 6 | import com.google.android.gms.common.api.Result; 7 | import com.google.android.gms.common.api.ResultCallback; 8 | 9 | import io.reactivex.ObservableEmitter; 10 | import io.reactivex.ObservableOnSubscribe; 11 | import io.reactivex.disposables.Disposables; 12 | import io.reactivex.functions.Action; 13 | 14 | public class PendingResultObservableOnSubscribe implements ObservableOnSubscribe { 15 | private final PendingResult result; 16 | private boolean complete = false; 17 | 18 | public PendingResultObservableOnSubscribe(PendingResult result) { 19 | this.result = result; 20 | } 21 | 22 | @Override 23 | public void subscribe(final ObservableEmitter emitter) throws Exception { 24 | result.setResultCallback(new ResultCallback() { 25 | @Override 26 | public void onResult(@NonNull T t) { 27 | if (!emitter.isDisposed()) { 28 | emitter.onNext(t); 29 | emitter.onComplete(); 30 | } 31 | complete = true; 32 | } 33 | }); 34 | 35 | emitter.setDisposable(Disposables.fromAction(new Action() { 36 | @Override 37 | public void run() { 38 | if (!complete) { 39 | result.cancel(); 40 | } 41 | } 42 | })); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /sample/src/main/res/layout/activity_places.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 16 | 17 | 21 | 22 | 29 | 30 | 37 | 38 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /android-reactive-location/src/main/java/pl/charmas/android/reactivelocation2/observables/ObservableFactory.java: -------------------------------------------------------------------------------- 1 | package pl.charmas.android.reactivelocation2.observables; 2 | 3 | import io.reactivex.Observable; 4 | import io.reactivex.ObservableOnSubscribe; 5 | import io.reactivex.ObservableSource; 6 | import io.reactivex.ObservableTransformer; 7 | import io.reactivex.functions.BiPredicate; 8 | 9 | public class ObservableFactory { 10 | private final ObservableContext context; 11 | 12 | public ObservableFactory(ObservableContext context) { 13 | this.context = context; 14 | } 15 | 16 | public Observable createObservable(ObservableOnSubscribe source) { 17 | return Observable.create(source).compose(new RetryOnConnectionSuspension(context.isRetryOnConnectionSuspended())); 18 | } 19 | 20 | private static class RetryOnConnectionSuspension implements ObservableTransformer { 21 | private final boolean shouldRetry; 22 | 23 | RetryOnConnectionSuspension(boolean shouldRetry) { 24 | this.shouldRetry = shouldRetry; 25 | } 26 | 27 | @Override 28 | public ObservableSource apply(Observable upstream) { 29 | if (shouldRetry) { 30 | return upstream.retry(new IsConnectionSuspendedException()); 31 | } 32 | return upstream; 33 | } 34 | 35 | private static class IsConnectionSuspendedException implements BiPredicate { 36 | @Override 37 | public boolean test(Integer integer, Throwable throwable) throws Exception { 38 | return throwable instanceof GoogleAPIConnectionSuspendedException; 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /android-reactive-location/src/main/java/pl/charmas/android/reactivelocation2/observables/geofence/RemoveGeofenceObservableOnSubscribe.java: -------------------------------------------------------------------------------- 1 | package pl.charmas.android.reactivelocation2.observables.geofence; 2 | 3 | import android.app.PendingIntent; 4 | 5 | import com.google.android.gms.common.api.GoogleApiClient; 6 | import com.google.android.gms.common.api.Status; 7 | 8 | import java.util.List; 9 | 10 | import io.reactivex.Observable; 11 | import io.reactivex.ObservableEmitter; 12 | import pl.charmas.android.reactivelocation2.observables.BaseLocationObservableOnSubscribe; 13 | import pl.charmas.android.reactivelocation2.observables.ObservableContext; 14 | import pl.charmas.android.reactivelocation2.observables.ObservableFactory; 15 | 16 | 17 | public abstract class RemoveGeofenceObservableOnSubscribe extends BaseLocationObservableOnSubscribe { 18 | 19 | public static Observable createObservable(ObservableContext ctx, ObservableFactory factory, PendingIntent pendingIntent) { 20 | return factory.createObservable(new RemoveGeofenceByPendingIntentObservableOnSubscribe(ctx, pendingIntent)); 21 | } 22 | 23 | public static Observable createObservable(ObservableContext ctx, ObservableFactory factory, List requestIds) { 24 | return factory.createObservable(new RemoveGeofenceRequestIdsObservableOnSubscribe(ctx, requestIds)); 25 | } 26 | 27 | RemoveGeofenceObservableOnSubscribe(ObservableContext ctx) { 28 | super(ctx); 29 | } 30 | 31 | @Override 32 | protected void onGoogleApiClientReady(GoogleApiClient apiClient, final ObservableEmitter emitter) { 33 | removeGeofences(apiClient, emitter); 34 | } 35 | 36 | protected abstract void removeGeofences(GoogleApiClient locationClient, ObservableEmitter emitter); 37 | 38 | } 39 | -------------------------------------------------------------------------------- /android-reactive-location/src/main/java/pl/charmas/android/reactivelocation2/observables/geofence/RemoveGeofenceByPendingIntentObservableOnSubscribe.java: -------------------------------------------------------------------------------- 1 | package pl.charmas.android.reactivelocation2.observables.geofence; 2 | 3 | import android.app.PendingIntent; 4 | import android.support.annotation.NonNull; 5 | 6 | import com.google.android.gms.common.api.GoogleApiClient; 7 | import com.google.android.gms.common.api.ResultCallback; 8 | import com.google.android.gms.common.api.Status; 9 | import com.google.android.gms.location.LocationServices; 10 | 11 | import io.reactivex.ObservableEmitter; 12 | import pl.charmas.android.reactivelocation2.observables.ObservableContext; 13 | import pl.charmas.android.reactivelocation2.observables.StatusException; 14 | 15 | 16 | class RemoveGeofenceByPendingIntentObservableOnSubscribe extends RemoveGeofenceObservableOnSubscribe { 17 | private final PendingIntent pendingIntent; 18 | 19 | RemoveGeofenceByPendingIntentObservableOnSubscribe(ObservableContext ctx, PendingIntent pendingIntent) { 20 | super(ctx); 21 | this.pendingIntent = pendingIntent; 22 | } 23 | 24 | @Override 25 | protected void removeGeofences(GoogleApiClient locationClient, final ObservableEmitter emitter) { 26 | LocationServices.GeofencingApi.removeGeofences(locationClient, pendingIntent) 27 | .setResultCallback(new ResultCallback() { 28 | @Override 29 | public void onResult(@NonNull Status status) { 30 | if (emitter.isDisposed()) return; 31 | if (status.isSuccess()) { 32 | emitter.onNext(status); 33 | emitter.onComplete(); 34 | } else { 35 | emitter.onError(new StatusException(status)); 36 | } 37 | } 38 | }); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /android-reactive-location/src/main/java/pl/charmas/android/reactivelocation2/observables/geofence/RemoveGeofenceRequestIdsObservableOnSubscribe.java: -------------------------------------------------------------------------------- 1 | package pl.charmas.android.reactivelocation2.observables.geofence; 2 | 3 | import android.support.annotation.NonNull; 4 | 5 | import com.google.android.gms.common.api.GoogleApiClient; 6 | import com.google.android.gms.common.api.ResultCallback; 7 | import com.google.android.gms.common.api.Status; 8 | import com.google.android.gms.location.LocationServices; 9 | 10 | import java.util.List; 11 | 12 | import io.reactivex.ObservableEmitter; 13 | import pl.charmas.android.reactivelocation2.observables.ObservableContext; 14 | import pl.charmas.android.reactivelocation2.observables.StatusException; 15 | 16 | 17 | class RemoveGeofenceRequestIdsObservableOnSubscribe extends RemoveGeofenceObservableOnSubscribe { 18 | private final List geofenceRequestIds; 19 | 20 | RemoveGeofenceRequestIdsObservableOnSubscribe(ObservableContext ctx, List geofenceRequestIds) { 21 | super(ctx); 22 | this.geofenceRequestIds = geofenceRequestIds; 23 | } 24 | 25 | @Override 26 | protected void removeGeofences(GoogleApiClient locationClient, final ObservableEmitter emitter) { 27 | LocationServices.GeofencingApi.removeGeofences(locationClient, geofenceRequestIds) 28 | .setResultCallback(new ResultCallback() { 29 | @Override 30 | public void onResult(@NonNull Status status) { 31 | if (emitter.isDisposed()) return; 32 | if (status.isSuccess()) { 33 | emitter.onNext(status); 34 | emitter.onComplete(); 35 | } else { 36 | emitter.onError(new StatusException(status)); 37 | } 38 | } 39 | }); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /sample/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 9 | 10 | 11 | 16 | 17 | 20 | 21 | 24 | 25 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | Change Log 2 | ========== 3 | 4 | Version 2.1 5 | ---------------------------- 6 | * Fix issues with emitting values when observable is disposed 7 | * Add option to auto reconnect to GMS when connection is suspended 8 | 9 | Version 2.0 10 | ---------------------------- 11 | * Support for RxJava2 12 | 13 | Version 1.0 14 | ---------------------------- 15 | * Add Locale to geocode observable 16 | * Added option to pass GMS callback Handler while creating ReactiveLocationProvider 17 | * Bugfix: crash on unregistering activity BroadcastReceiver 18 | * Fixed memory leak in location observable 19 | * Checking if subscripion is still on before emitting values in few places 20 | 21 | Version 0.10 22 | ---------------------------- 23 | * Updated dependencies 24 | * Added permission annotations 25 | * Minor improvements and cleanups in sample app 26 | 27 | Version 0.8.1 28 | ---------------------------- 29 | * Updated dependencies 30 | * Added fallback reverse geocode observable that uses web apis to obtain address 31 | * Added API to pass locales for reverse geocoding 32 | 33 | Version 0.8 34 | ---------------------------- 35 | * Updated dependencies to Google Play Services 8.1 36 | 37 | Version 0.7 38 | ---------------------------- 39 | 40 | * Removed ```final``` from methods in ```ReactiveLocationProvider``` to enable mockito mocking. 41 | * Updated dependencies. 42 | * Added support to fetch Place by id. 43 | 44 | Version 0.6 45 | ---------------------------- 46 | 47 | * Added support for mock locations. 48 | * Corrected geocode to reverse geocode and proper geocode implemented. 49 | * Added support for PendingIntent location updates. 50 | * Simplified and unified observables that were based on status now return status instead of custom responses. 51 | * Updated to newest Play Services version. 52 | 53 | Version 0.5 54 | ---------------------------- 55 | 56 | * Fix: now last known location observable when location is disabled emits nothing and completes (instead of null). 57 | * Added Places API support from Google Play Services 7.0.0 58 | * Added Location Settings API from Google Play Services 7.0.0 59 | * Added support for obtaining connection to Google Play Services though observable. 60 | * Added utils to handle ```PendingResponse``` and ```Buffers``` from Google Play Services 61 | 62 | Version 0.4 63 | ---------------------------- 64 | 65 | Sorry - no changelog history here. 66 | -------------------------------------------------------------------------------- /android-reactive-location/src/main/java/pl/charmas/android/reactivelocation2/observables/location/RemoveLocationIntentUpdatesObservableOnSubscribe.java: -------------------------------------------------------------------------------- 1 | package pl.charmas.android.reactivelocation2.observables.location; 2 | 3 | import android.app.PendingIntent; 4 | import android.support.annotation.NonNull; 5 | 6 | import com.google.android.gms.common.api.GoogleApiClient; 7 | import com.google.android.gms.common.api.ResultCallback; 8 | import com.google.android.gms.common.api.Status; 9 | import com.google.android.gms.location.LocationServices; 10 | 11 | import io.reactivex.Observable; 12 | import io.reactivex.ObservableEmitter; 13 | import pl.charmas.android.reactivelocation2.observables.BaseLocationObservableOnSubscribe; 14 | import pl.charmas.android.reactivelocation2.observables.ObservableContext; 15 | import pl.charmas.android.reactivelocation2.observables.ObservableFactory; 16 | import pl.charmas.android.reactivelocation2.observables.StatusException; 17 | 18 | 19 | public class RemoveLocationIntentUpdatesObservableOnSubscribe extends BaseLocationObservableOnSubscribe { 20 | private final PendingIntent intent; 21 | 22 | public static Observable createObservable(ObservableContext ctx, ObservableFactory factory, PendingIntent intent) { 23 | return factory.createObservable(new RemoveLocationIntentUpdatesObservableOnSubscribe(ctx, intent)); 24 | } 25 | 26 | private RemoveLocationIntentUpdatesObservableOnSubscribe(ObservableContext ctx, PendingIntent intent) { 27 | super(ctx); 28 | this.intent = intent; 29 | } 30 | 31 | @Override 32 | protected void onGoogleApiClientReady(GoogleApiClient apiClient, final ObservableEmitter emitter) { 33 | LocationServices.FusedLocationApi.removeLocationUpdates(apiClient, intent) 34 | .setResultCallback(new ResultCallback() { 35 | @Override 36 | public void onResult(@NonNull Status status) { 37 | if (emitter.isDisposed()) return; 38 | if (status.isSuccess()) { 39 | emitter.onNext(status); 40 | emitter.onComplete(); 41 | } else { 42 | emitter.onError(new StatusException(status)); 43 | } 44 | } 45 | }); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /sample/src/main/res/layout/activity_places_result.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 13 | 14 | 21 | 22 | 27 | 28 | 36 | 37 | 42 | 43 | 51 | 52 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /android-reactive-location/src/main/java/pl/charmas/android/reactivelocation2/observables/geocode/ReverseGeocodeObservable.java: -------------------------------------------------------------------------------- 1 | package pl.charmas.android.reactivelocation2.observables.geocode; 2 | 3 | import android.content.Context; 4 | import android.location.Address; 5 | import android.location.Geocoder; 6 | 7 | import java.io.IOException; 8 | import java.util.List; 9 | import java.util.Locale; 10 | 11 | import io.reactivex.Observable; 12 | import io.reactivex.ObservableEmitter; 13 | import io.reactivex.ObservableOnSubscribe; 14 | import io.reactivex.schedulers.Schedulers; 15 | import pl.charmas.android.reactivelocation2.observables.ObservableEmitterWrapper; 16 | import pl.charmas.android.reactivelocation2.observables.ObservableFactory; 17 | 18 | 19 | public class ReverseGeocodeObservable implements ObservableOnSubscribe> { 20 | private final Context ctx; 21 | private final Locale locale; 22 | private final double latitude; 23 | private final double longitude; 24 | private final int maxResults; 25 | 26 | public static Observable> createObservable(Context ctx, ObservableFactory factory, Locale locale, double latitude, double longitude, int maxResults) { 27 | return factory.createObservable(new ReverseGeocodeObservable(ctx, locale, latitude, longitude, maxResults)); 28 | } 29 | 30 | private ReverseGeocodeObservable(Context ctx, Locale locale, double latitude, double longitude, int maxResults) { 31 | this.ctx = ctx; 32 | this.latitude = latitude; 33 | this.longitude = longitude; 34 | this.maxResults = maxResults; 35 | this.locale = locale; 36 | } 37 | 38 | @Override 39 | public void subscribe(ObservableEmitter> emitter) throws Exception { 40 | Geocoder geocoder = new Geocoder(ctx, locale); 41 | try { 42 | List

addresses = geocoder.getFromLocation(latitude, longitude, maxResults); 43 | if (!emitter.isDisposed()) { 44 | emitter.onNext(addresses); 45 | emitter.onComplete(); 46 | } 47 | } catch (IOException e) { 48 | // If it's a service not available error try a different approach using google web api 49 | if (!emitter.isDisposed()) { 50 | Observable 51 | .create(new FallbackReverseGeocodeObservable(locale, latitude, longitude, maxResults)) 52 | .subscribeOn(Schedulers.io()) 53 | .subscribe(new ObservableEmitterWrapper<>(emitter)); 54 | } 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /android-reactive-location/src/main/java/pl/charmas/android/reactivelocation2/ReactiveLocationProviderConfiguration.java: -------------------------------------------------------------------------------- 1 | package pl.charmas.android.reactivelocation2; 2 | 3 | import android.os.Handler; 4 | import android.support.annotation.Nullable; 5 | 6 | /** 7 | * Configuration for location provider. Pleas use builder to create an instance. 8 | */ 9 | public class ReactiveLocationProviderConfiguration { 10 | private final Handler customCallbackHandler; 11 | private final boolean retryOnConnectionSuspended; 12 | 13 | private ReactiveLocationProviderConfiguration(Builder builder) { 14 | this.customCallbackHandler = builder.customCallbackHandler; 15 | this.retryOnConnectionSuspended = builder.retryOnConnectionSuspended; 16 | } 17 | 18 | public Handler getCustomCallbackHandler() { 19 | return customCallbackHandler; 20 | } 21 | 22 | public boolean isRetryOnConnectionSuspended() { 23 | return retryOnConnectionSuspended; 24 | } 25 | 26 | public static Builder builder() { 27 | return new Builder(); 28 | } 29 | 30 | public static class Builder { 31 | private Handler customCallbackHandler = null; 32 | private boolean retryOnConnectionSuspended = false; 33 | 34 | /** 35 | * Allows to set custom handler on which all Google Play Services callbacks are called. 36 | *

37 | * Default: null 38 | * 39 | * @param customCallbackHandler handler instance 40 | * @return builder instance 41 | */ 42 | public Builder setCustomCallbackHandler(@Nullable Handler customCallbackHandler) { 43 | this.customCallbackHandler = customCallbackHandler; 44 | return this; 45 | } 46 | 47 | /** 48 | * Property that allows automatic retries of connection to Google Play Services when it has bean suspended. 49 | *

50 | * Default: false 51 | * 52 | * @param retryOnConnectionSuspended if should we retry on connection failure 53 | * @return builder instance 54 | */ 55 | public Builder setRetryOnConnectionSuspended(boolean retryOnConnectionSuspended) { 56 | this.retryOnConnectionSuspended = retryOnConnectionSuspended; 57 | return this; 58 | } 59 | 60 | /** 61 | * Builds configuration instance 62 | * 63 | * @return configuration instance 64 | */ 65 | public ReactiveLocationProviderConfiguration build() { 66 | return new ReactiveLocationProviderConfiguration(this); 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /android-reactive-location/src/main/java/pl/charmas/android/reactivelocation2/observables/location/AddLocationIntentUpdatesObservableOnSubscribe.java: -------------------------------------------------------------------------------- 1 | package pl.charmas.android.reactivelocation2.observables.location; 2 | 3 | import android.app.PendingIntent; 4 | import android.support.annotation.NonNull; 5 | 6 | import com.google.android.gms.common.api.GoogleApiClient; 7 | import com.google.android.gms.common.api.ResultCallback; 8 | import com.google.android.gms.common.api.Status; 9 | import com.google.android.gms.location.LocationRequest; 10 | import com.google.android.gms.location.LocationServices; 11 | 12 | import io.reactivex.Observable; 13 | import io.reactivex.ObservableEmitter; 14 | import pl.charmas.android.reactivelocation2.observables.BaseLocationObservableOnSubscribe; 15 | import pl.charmas.android.reactivelocation2.observables.ObservableContext; 16 | import pl.charmas.android.reactivelocation2.observables.ObservableFactory; 17 | import pl.charmas.android.reactivelocation2.observables.StatusException; 18 | 19 | 20 | @SuppressWarnings("MissingPermission") 21 | public class AddLocationIntentUpdatesObservableOnSubscribe extends BaseLocationObservableOnSubscribe { 22 | private final LocationRequest locationRequest; 23 | private final PendingIntent intent; 24 | 25 | public static Observable createObservable(ObservableContext ctx, ObservableFactory factory, LocationRequest locationRequest, PendingIntent intent) { 26 | return factory.createObservable(new AddLocationIntentUpdatesObservableOnSubscribe(ctx, locationRequest, intent)); 27 | } 28 | 29 | private AddLocationIntentUpdatesObservableOnSubscribe(ObservableContext ctx, LocationRequest locationRequest, PendingIntent intent) { 30 | super(ctx); 31 | this.locationRequest = locationRequest; 32 | this.intent = intent; 33 | } 34 | 35 | @Override 36 | protected void onGoogleApiClientReady(GoogleApiClient apiClient, final ObservableEmitter emitter) { 37 | LocationServices.FusedLocationApi.requestLocationUpdates(apiClient, locationRequest, intent) 38 | .setResultCallback(new ResultCallback() { 39 | @Override 40 | public void onResult(@NonNull Status status) { 41 | if (emitter.isDisposed()) return; 42 | if (!status.isSuccess()) { 43 | emitter.onError(new StatusException(status)); 44 | } else { 45 | emitter.onNext(status); 46 | emitter.onComplete(); 47 | } 48 | } 49 | }); 50 | 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /android-reactive-location/src/main/java/pl/charmas/android/reactivelocation2/observables/geofence/AddGeofenceObservableOnSubscribe.java: -------------------------------------------------------------------------------- 1 | package pl.charmas.android.reactivelocation2.observables.geofence; 2 | 3 | import android.app.PendingIntent; 4 | import android.support.annotation.NonNull; 5 | 6 | import com.google.android.gms.common.api.GoogleApiClient; 7 | import com.google.android.gms.common.api.ResultCallback; 8 | import com.google.android.gms.common.api.Status; 9 | import com.google.android.gms.location.GeofencingRequest; 10 | import com.google.android.gms.location.LocationServices; 11 | 12 | import io.reactivex.Observable; 13 | import io.reactivex.ObservableEmitter; 14 | import pl.charmas.android.reactivelocation2.observables.BaseLocationObservableOnSubscribe; 15 | import pl.charmas.android.reactivelocation2.observables.ObservableContext; 16 | import pl.charmas.android.reactivelocation2.observables.ObservableFactory; 17 | import pl.charmas.android.reactivelocation2.observables.StatusException; 18 | 19 | 20 | @SuppressWarnings("MissingPermission") 21 | public class AddGeofenceObservableOnSubscribe extends BaseLocationObservableOnSubscribe { 22 | private final GeofencingRequest request; 23 | private final PendingIntent geofenceTransitionPendingIntent; 24 | 25 | public static Observable createObservable(ObservableContext ctx, ObservableFactory factory, GeofencingRequest request, PendingIntent geofenceTransitionPendingIntent) { 26 | return factory.createObservable(new AddGeofenceObservableOnSubscribe(ctx, request, geofenceTransitionPendingIntent)); 27 | } 28 | 29 | private AddGeofenceObservableOnSubscribe(ObservableContext ctx, GeofencingRequest request, PendingIntent geofenceTransitionPendingIntent) { 30 | super(ctx); 31 | this.request = request; 32 | this.geofenceTransitionPendingIntent = geofenceTransitionPendingIntent; 33 | } 34 | 35 | @Override 36 | protected void onGoogleApiClientReady(GoogleApiClient apiClient, final ObservableEmitter emitter) { 37 | LocationServices.GeofencingApi.addGeofences(apiClient, request, geofenceTransitionPendingIntent) 38 | .setResultCallback(new ResultCallback() { 39 | @Override 40 | public void onResult(@NonNull Status status) { 41 | if (emitter.isDisposed()) return; 42 | if (status.isSuccess()) { 43 | emitter.onNext(status); 44 | emitter.onComplete(); 45 | 46 | } else { 47 | emitter.onError(new StatusException(status)); 48 | } 49 | } 50 | }); 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /android-reactive-location/src/main/java/pl/charmas/android/reactivelocation2/observables/geocode/GeocodeObservable.java: -------------------------------------------------------------------------------- 1 | package pl.charmas.android.reactivelocation2.observables.geocode; 2 | 3 | import android.content.Context; 4 | import android.location.Address; 5 | import android.location.Geocoder; 6 | import android.support.annotation.NonNull; 7 | 8 | import com.google.android.gms.maps.model.LatLngBounds; 9 | 10 | import java.io.IOException; 11 | import java.util.List; 12 | import java.util.Locale; 13 | 14 | import io.reactivex.Observable; 15 | import io.reactivex.ObservableEmitter; 16 | import io.reactivex.ObservableOnSubscribe; 17 | import pl.charmas.android.reactivelocation2.observables.ObservableFactory; 18 | 19 | public class GeocodeObservable implements ObservableOnSubscribe> { 20 | private final Context ctx; 21 | private final String locationName; 22 | private final int maxResults; 23 | private final LatLngBounds bounds; 24 | private final Locale locale; 25 | 26 | public static Observable> createObservable(Context ctx, ObservableFactory factory, String locationName, int maxResults, LatLngBounds bounds, Locale locale) { 27 | return factory.createObservable(new GeocodeObservable(ctx, locationName, maxResults, bounds, locale)); 28 | } 29 | 30 | private GeocodeObservable(Context ctx, String locationName, int maxResults, LatLngBounds bounds, Locale locale) { 31 | this.ctx = ctx; 32 | this.locationName = locationName; 33 | this.maxResults = maxResults; 34 | this.bounds = bounds; 35 | this.locale = locale; 36 | } 37 | 38 | @Override 39 | public void subscribe(ObservableEmitter> emitter) throws Exception { 40 | Geocoder geocoder = createGeocoder(); 41 | try { 42 | List

result = getAddresses(geocoder); 43 | if (!emitter.isDisposed()) { 44 | emitter.onNext(result); 45 | emitter.onComplete(); 46 | } 47 | } catch (IOException e) { 48 | if (!emitter.isDisposed()) { 49 | emitter.onError(e); 50 | } 51 | } 52 | } 53 | 54 | private List
getAddresses(Geocoder geocoder) throws IOException { 55 | List
result; 56 | if (bounds != null) { 57 | result = geocoder.getFromLocationName(locationName, maxResults, bounds.southwest.latitude, bounds.southwest.longitude, bounds.northeast.latitude, bounds.northeast.longitude); 58 | } else { 59 | result = geocoder.getFromLocationName(locationName, maxResults); 60 | } 61 | return result; 62 | } 63 | 64 | @NonNull 65 | private Geocoder createGeocoder() { 66 | if (locale != null) return new Geocoder(ctx, locale); 67 | return new Geocoder(ctx); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /sample/src/main/java/pl/charmas/android/reactivelocation2/sample/utils/TextViewTextOnSubscribe.java: -------------------------------------------------------------------------------- 1 | package pl.charmas.android.reactivelocation2.sample.utils; 2 | 3 | import android.os.Looper; 4 | import android.text.Editable; 5 | import android.text.TextWatcher; 6 | import android.widget.TextView; 7 | 8 | import java.util.concurrent.atomic.AtomicBoolean; 9 | 10 | import io.reactivex.ObservableEmitter; 11 | import io.reactivex.ObservableOnSubscribe; 12 | import io.reactivex.android.schedulers.AndroidSchedulers; 13 | import io.reactivex.disposables.Disposable; 14 | 15 | class TextViewTextOnSubscribe implements ObservableOnSubscribe { 16 | private final TextView view; 17 | 18 | TextViewTextOnSubscribe(TextView view) { 19 | this.view = view; 20 | } 21 | 22 | @Override 23 | public void subscribe(final ObservableEmitter emitter) throws Exception { 24 | 25 | final TextWatcher watcher = new TextWatcher() { 26 | @Override 27 | public void beforeTextChanged(CharSequence s, int start, int count, int after) { 28 | } 29 | 30 | @Override 31 | public void onTextChanged(CharSequence s, int start, int before, int count) { 32 | if (!emitter.isDisposed()) { 33 | emitter.onNext(s); 34 | } 35 | } 36 | 37 | @Override 38 | public void afterTextChanged(Editable s) { 39 | } 40 | }; 41 | 42 | emitter.setDisposable(new MainThreadDisposable() { 43 | @Override 44 | protected void onDisposed() { 45 | view.removeTextChangedListener(watcher); 46 | } 47 | }); 48 | 49 | view.addTextChangedListener(watcher); 50 | 51 | // Emit initial value. 52 | emitter.onNext(view.getText()); 53 | } 54 | 55 | abstract class MainThreadDisposable implements Disposable { 56 | 57 | private final AtomicBoolean dispose = new AtomicBoolean(); 58 | 59 | @Override 60 | public void dispose() { 61 | if (dispose.compareAndSet(false, true)) { 62 | if (Looper.myLooper() == Looper.getMainLooper()) { 63 | onDisposed(); 64 | } else { 65 | AndroidSchedulers.mainThread() 66 | .createWorker() 67 | .schedule(new Runnable() { 68 | @Override 69 | public void run() { 70 | onDisposed(); 71 | } 72 | }); 73 | } 74 | } 75 | } 76 | 77 | @Override 78 | public boolean isDisposed() { 79 | return dispose.get(); 80 | } 81 | 82 | abstract void onDisposed(); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /sample/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 11 | 12 | 19 | 20 | 25 | 26 | 34 | 35 | 40 | 41 | 49 | 50 | 55 | 56 | 64 | 65 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /sample/src/main/res/layout/activity_mocklocations.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 14 | 15 | 22 | 23 | 29 | 30 | 31 | 32 | 39 | 40 | 47 | 48 |