Skip to content

Commit

Permalink
RxFit methods are now non-static for better testability. Dependency u…
Browse files Browse the repository at this point in the history
…pdates.
  • Loading branch information
patloew committed Sep 28, 2016
1 parent a6fe60c commit 1290c43
Show file tree
Hide file tree
Showing 57 changed files with 1,392 additions and 893 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ android:
- tools

# The BuildTools version used by your project
- build-tools-24.0.0
- build-tools-24.0.2

# The SDK version used to compile your project
- android-24
Expand Down
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Changelog

## Version 1.4.0

* Updated Play Services (9.6.1) and RxJava (1.2.0).
* RxFit no longer provides static methods. Create an instance once and share it, e.g. via dependency injection or by providing the instance via your Application class.

## Version 1.3.0

* Updated to Play Services 9.2.0.
Expand Down
17 changes: 11 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@ This library wraps the Fit API in [RxJava](https://github.com/ReactiveX/RxJava)

# Usage

Initialize RxFit once, preferably in your Application `onCreate()` via `RxFit.init(...)`. Make sure to include all the APIs and Scopes that you need for your app. The RxFit class is very similar to the Fitness class provided by the Fit API. Instead of `Fitness.HistoryApi.readData(apiClient, dataReadRequest)` you can use `RxFit.History.read(dataReadRequest)`. Make sure to have the Location and Body Sensors permission from Marshmallow on, if they are needed by your Fit API requests. If the user didn’t already authorize your app for using fitness data, the lib handles showing the authorization dialog.
Create an RxFit instance once, preferably in your Application's `onCreate()` or by using a dependency injection framework. Make sure to include all the APIs and Scopes that you need for your app. The RxFit class is very similar to the Fitness class provided by the Fit API. Instead of `Fitness.HistoryApi.readData(apiClient, dataReadRequest)` you can use `rxFit.history().read(dataReadRequest)`. Make sure to have the Location and Body Sensors permission from Marshmallow on, if they are needed by your Fit API requests. If the user didn’t already authorize your app for using fitness data, the lib handles showing the authorization dialog.

Example:

```java
RxFit.init(
// Create one instance and share it
RxFit rxfit = new RxFit(
context,
new Api[] { Fitness.HISTORY_API },
new Scope[] { new Scope(Scopes.FITNESS_ACTIVITY_READ) }
Expand All @@ -24,16 +25,16 @@ DataReadRequest dataReadRequest = new DataReadRequest.Builder()
.setTimeRange(startTime, endTime, TimeUnit.MILLISECONDS)
.build();

RxFit.History.read(dataReadRequest)
rxFit.history().read(dataReadRequest)
.flatMapObservable(dataReadResult -> Observable.from(dataReadResult.getBuckets()))
.subscribe(bucket -> {
/* do something */
});
```

An `OnExceptionResumeNext` Transformer is available in the lib, which resumes with another Single/Observable when an Exception is thrown, except when the exception was a GoogleAPIConnectionException which was caused by an unresolved resolution.
An `RxFitOnExceptionResumeNext` Transformer is available in the lib, which resumes with another Single/Observable when an Exception is thrown, except when the exception was a GoogleAPIConnectionException which was caused by an unresolved resolution.

An optional global default timeout for all Fit API requests made through the library can be set via `RxFit.setDefaultTimeout(...)`. In addition, timeouts can be set when creating a new Observable by providing timeout parameters, e.g. `RxFit.History.read(dataReadRequest, 15, TimeUnit.SECONDS)`. These parameters override the default timeout. When a timeout occurs, a StatusException is provided via `onError()`. The RxJava timeout operators can be used instead, but these do not cancel the Fit API request immediately.
An optional global default timeout for all Fit API requests made through the library can be set via `rxFit.setDefaultTimeout(...)`. In addition, timeouts can be set when creating a new Observable by providing timeout parameters, e.g. `rxFit.history().read(dataReadRequest, 15, TimeUnit.SECONDS)`. These parameters override the default timeout. When a timeout occurs, a StatusException is provided via `onError()`. The RxJava timeout operators can be used instead, but these do not cancel the Fit API request immediately.

You can also obtain a `Single<GoogleApiClient>`, which connects on subscribe and disconnects on unsubscribe via `GoogleAPIClientSingle.create(...)`.

Expand All @@ -53,9 +54,13 @@ A basic sample app is available in the `sample` project. You need to create an O
The lib is available on jCenter. Add the following to your `build.gradle`:

dependencies {
compile 'com.patloew.rxfit:rxfit:1.3.0'
compile 'com.patloew.rxfit:rxfit:1.4.0'
}

# Testing

When unit testing your app's classes, RxFit behavior can be mocked easily. See the `MainPresenterTest` in the `sample` project for an example test.

# Credits

The code for managing the GoogleApiClient is taken from the [Android-ReactiveLocation](https://github.com/mcharmas/Android-ReactiveLocation) library by Michał Charmas, which is licensed under the Apache License, Version 2.0.
Expand Down
11 changes: 4 additions & 7 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,17 @@ buildscript {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.1.2'
classpath 'com.android.tools.build:gradle:2.2.0'

classpath 'me.tatarka:gradle-retrolambda:3.3.0-beta4'
classpath 'me.tatarka.retrolambda.projectlombok:lombok.ast:0.2.3.a2'
classpath 'me.tatarka:gradle-retrolambda:3.3.0'

classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.6'
classpath "com.github.dcendents:android-maven-gradle-plugin:1.3"
classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.7.1'
classpath "com.github.dcendents:android-maven-gradle-plugin:1.5"


// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}

configurations.classpath.exclude group: 'com.android.tools.external.lombok'
}

allprojects {
Expand Down
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.13-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-3.1-all.zip
12 changes: 6 additions & 6 deletions library/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,18 @@ apply plugin: 'com.jfrog.bintray'
apply plugin: 'com.github.dcendents.android-maven'

group = 'com.patloew.rxfit'
version = '1.3.0'
version = '1.4.0'
project.archivesBaseName = 'rxfit'

android {
compileSdkVersion 24
buildToolsVersion "24.0.0"
buildToolsVersion "24.0.2"

defaultConfig {
minSdkVersion 9
targetSdkVersion 24
versionCode 6
versionName "1.3.0"
versionCode 7
versionName "1.4.0"
}
buildTypes {
release {
Expand All @@ -32,8 +32,8 @@ android {

dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'io.reactivex:rxjava:1.1.6'
compile 'com.google.android.gms:play-services-fitness:9.2.0'
compile 'io.reactivex:rxjava:1.2.0'
compile 'com.google.android.gms:play-services-fitness:9.6.1'

testCompile 'junit:junit:4.12'
testCompile 'org.mockito:mockito-core:1.10.19'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
* FILE MODIFIED by Patrick Löwenstein, 2016
*
*/
public abstract class BaseObservable<T> extends BaseRx<T> implements Observable.OnSubscribe<T> {
abstract class BaseObservable<T> extends BaseRx<T> implements Observable.OnSubscribe<T> {
private final boolean handleResolution;

private final Map<GoogleApiClient, Subscriber<? super T>> subscriptionInfoMap = new ConcurrentHashMap<>();
Expand Down
12 changes: 6 additions & 6 deletions library/src/main/java/com/patloew/rxfit/BaseRx.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
* FILE MODIFIED by Patrick Löwenstein, 2016
*
*/
public abstract class BaseRx<T> {
abstract class BaseRx<T> {
protected static final Set<BaseRx> observableSet = new HashSet<>();

protected final Context ctx;
Expand All @@ -44,16 +44,16 @@ public abstract class BaseRx<T> {
private final TimeUnit timeoutUnit;

protected BaseRx(@NonNull RxFit rxFit, Long timeout, TimeUnit timeUnit) {
this.ctx = rxFit.getContext();
this.services = rxFit.getApis();
this.scopes = rxFit.getScopes();
this.ctx = rxFit.ctx;
this.services = rxFit.apis;
this.scopes = rxFit.scopes;

if(timeout != null && timeUnit != null) {
this.timeoutTime = timeout;
this.timeoutUnit = timeUnit;
} else {
this.timeoutTime = RxFit.getDefaultTimeout();
this.timeoutUnit = RxFit.getDefaultTimeoutUnit();
this.timeoutTime = rxFit.timeoutTime;
this.timeoutUnit = rxFit.timeoutUnit;
}
}

Expand Down
2 changes: 1 addition & 1 deletion library/src/main/java/com/patloew/rxfit/BaseSingle.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
* FILE MODIFIED by Patrick Löwenstein, 2016
*
*/
public abstract class BaseSingle<T> extends BaseRx<T> implements Single.OnSubscribe<T> {
abstract class BaseSingle<T> extends BaseRx<T> implements Single.OnSubscribe<T> {
private final boolean handleResolution;

private final Map<GoogleApiClient, SingleSubscriber<? super T>> subscriptionInfoMap = new ConcurrentHashMap<>();
Expand Down
157 changes: 157 additions & 0 deletions library/src/main/java/com/patloew/rxfit/Ble.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
package com.patloew.rxfit;

import android.support.annotation.NonNull;
import android.support.annotation.RequiresPermission;

import com.google.android.gms.common.api.Status;
import com.google.android.gms.fitness.data.BleDevice;
import com.google.android.gms.fitness.data.DataType;

import java.util.List;
import java.util.concurrent.TimeUnit;

import rx.Observable;
import rx.Single;
import rx.functions.Func1;

/* Copyright 2016 Patrick Löwenstein
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License. */
public class Ble {

private final RxFit rxFit;

Ble(RxFit rxFit) {
this.rxFit = rxFit;
}

// claimDevice

public Single<Status> claimDevice(@NonNull BleDevice bleDevice) {
return claimDeviceInternal(bleDevice, null, null, null);
}

public Single<Status> claimDevice(@NonNull BleDevice bleDevice, long timeout, @NonNull TimeUnit timeUnit) {
return claimDeviceInternal(bleDevice, null, timeout, timeUnit);
}

public Single<Status> claimDevice(@NonNull String deviceAddress) {
return claimDeviceInternal(null, deviceAddress, null, null);
}

public Single<Status> claimDevice(@NonNull String deviceAddress, long timeout, @NonNull TimeUnit timeUnit) {
return claimDeviceInternal(null, deviceAddress, timeout, timeUnit);
}

private Single<Status> claimDeviceInternal(BleDevice bleDevice, String deviceAddress, Long timeout, TimeUnit timeUnit) {
return Single.create(new BleClaimDeviceSingle(rxFit, bleDevice, deviceAddress, timeout, timeUnit));
}

// getClaimedDevices

public Observable<BleDevice> getClaimedDevices() {
return getClaimedDeviceListInternal(null, null, null);
}

public Observable<BleDevice> getClaimedDevices(long timeout, @NonNull TimeUnit timeUnit) {
return getClaimedDeviceListInternal(null, timeout, timeUnit);
}

public Observable<BleDevice> getClaimedDevices(DataType dataType) {
return getClaimedDeviceListInternal(dataType, null, null);
}

private Observable<BleDevice> getClaimedDevicesInternal(DataType dataType, long timeout, @NonNull TimeUnit timeUnit) {
return getClaimedDeviceListInternal(dataType, timeout, timeUnit);
}

private Observable<BleDevice> getClaimedDeviceListInternal(DataType dataType, Long timeout, TimeUnit timeUnit) {
return Single.create(new BleListClaimedDevicesSingle(rxFit, dataType, timeout, timeUnit))
.flatMapObservable(new Func1<List<BleDevice>, Observable<BleDevice>>() {
@Override
public Observable<BleDevice> call(List<BleDevice> bleDevices) {
return Observable.from(bleDevices);
}
});
}

// scan

@RequiresPermission("android.permission.BLUETOOTH_ADMIN")
public Observable<BleDevice> scan() {
return scanInternal(null, null, null, null);
}

@RequiresPermission("android.permission.BLUETOOTH_ADMIN")
public Observable<BleDevice> scan(long timeout, @NonNull TimeUnit timeUnit) {
return scanInternal(null, null, timeout, timeUnit);
}

@RequiresPermission("android.permission.BLUETOOTH_ADMIN")
public Observable<BleDevice> scan(@NonNull DataType... dataTypes) {
return scanInternal(dataTypes, null, null, null);
}

@RequiresPermission("android.permission.BLUETOOTH_ADMIN")
public Observable<BleDevice> scan(@NonNull DataType[] dataTypes, long timeout, @NonNull TimeUnit timeUnit) {
return scanInternal(dataTypes, null, timeout, timeUnit);
}

@RequiresPermission("android.permission.BLUETOOTH_ADMIN")
public Observable<BleDevice> scan(int stopTimeSecs) {
return scanInternal(null, stopTimeSecs, null, null);
}

@RequiresPermission("android.permission.BLUETOOTH_ADMIN")
public Observable<BleDevice> scan(int stopTimeSecs, long timeout, @NonNull TimeUnit timeUnit) {
return scanInternal(null, stopTimeSecs, timeout, timeUnit);
}

@RequiresPermission("android.permission.BLUETOOTH_ADMIN")
public Observable<BleDevice> scan(@NonNull DataType[] dataTypes, int stopTimeSecs) {
return scanInternal(dataTypes, stopTimeSecs, null, null);
}

@RequiresPermission("android.permission.BLUETOOTH_ADMIN")
public Observable<BleDevice> scan(@NonNull DataType[] dataTypes, int stopTimeSecs, long timeout, @NonNull TimeUnit timeUnit) {
return scanInternal(dataTypes, stopTimeSecs, timeout, timeUnit);
}

@SuppressWarnings("MissingPermission")
private Observable<BleDevice> scanInternal(DataType[] dataTypes, Integer stopTimeSecs, Long timeout, TimeUnit timeUnit) {
return Observable.create(new BleScanObservable(rxFit, dataTypes, stopTimeSecs, timeout, timeUnit));
}

// unclaim Device

public Single<Status> unclaimDevice(@NonNull BleDevice bleDevice) {
return unclaimDeviceInternal(bleDevice, null, null, null);
}

public Single<Status> unclaimDevice(@NonNull BleDevice bleDevice, long timeout, @NonNull TimeUnit timeUnit) {
return unclaimDeviceInternal(bleDevice, null, timeout, timeUnit);
}

public Single<Status> unclaimDevice(@NonNull String deviceAddress) {
return unclaimDeviceInternal(null, deviceAddress, null, null);
}

public Single<Status> unclaimDevice(@NonNull String deviceAddress, long timeout, @NonNull TimeUnit timeUnit) {
return unclaimDeviceInternal(null, deviceAddress, timeout, timeUnit);
}

private Single<Status> unclaimDeviceInternal(BleDevice bleDevice, String deviceAddress, Long timeout, TimeUnit timeUnit) {
return Single.create(new BleUnclaimDeviceSingle(rxFit, bleDevice, deviceAddress, timeout, timeUnit));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License. */
public class BleClaimDeviceSingle extends BaseSingle<Status> {
class BleClaimDeviceSingle extends BaseSingle<Status> {

private final BleDevice bleDevice;
private final String deviceAddress;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License. */
public class BleListClaimedDevicesSingle extends BaseSingle<List<BleDevice>> {
class BleListClaimedDevicesSingle extends BaseSingle<List<BleDevice>> {

private final DataType dataType;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License. */
public class BleScanObservable extends BaseObservable<BleDevice> {
class BleScanObservable extends BaseObservable<BleDevice> {

private final DataType[] dataTypes;
private final Integer stopTimeSecs;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License. */
public class BleUnclaimDeviceSingle extends BaseSingle<Status> {
class BleUnclaimDeviceSingle extends BaseSingle<Status> {

private final BleDevice bleDevice;
private final String deviceAddress;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License. */
public class CheckConnectionObservable extends BaseObservable<Void> {
class CheckConnectionObservable extends BaseObservable<Void> {

CheckConnectionObservable(RxFit rxFit) {
super(rxFit, null, null);
Expand Down
Loading

0 comments on commit 1290c43

Please sign in to comment.