Skip to content

Commit

Permalink
Use Singles instead of Observables if only one item is emitted. Obser…
Browse files Browse the repository at this point in the history
…vables, which previously returned a List/Map, now emit the items/values of these Collections. Removed unnecessary CheckConnectionObservable.
  • Loading branch information
patloew committed Mar 12, 2016
1 parent b27ecff commit 0423120
Show file tree
Hide file tree
Showing 37 changed files with 775 additions and 648 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Changelog

## Version 1.1.0

* BREAKING CHANGE: The lib now uses Singles instead of Observables if only one item is emitted.
* BREAKING CHANGE: Removed unnecessary `getRemoteNodes()`.
* BREAKING CHANGE: Observables, which previously returned a List/Map, now emit the items/values of these Collections.
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
# Reactive Wearable API Library for Android

[![Build Status](https://travis-ci.org/patloew/RxWear.svg?branch=master)](https://travis-ci.org/patloew/RxWear) [ ![Download](https://api.bintray.com/packages/patloew/maven/RxWear/images/download.svg) ](https://bintray.com/patloew/maven/RxWear/_latestVersion) [![API](https://img.shields.io/badge/API-9%2B-brightgreen.svg?style=flat)](https://android-arsenal.com/api?level=9)
[![Build Status](https://travis-ci.org/patloew/RxWear.svg?branch=master)](https://travis-ci.org/patloew/RxWear) [ ![Download](https://api.bintray.com/packages/patloew/maven/RxWear/images/download.svg) ](https://bintray.com/patloew/maven/RxWear/_latestVersion) [![Android Arsenal](https://img.shields.io/badge/Android%20Arsenal-RxWear-brightgreen.svg?style=flat)](http://android-arsenal.com/details/1/3271) [![API](https://img.shields.io/badge/API-9%2B-brightgreen.svg?style=flat)](https://android-arsenal.com/api?level=9)

This library wraps the Wearable API in [RxJava](https://github.com/ReactiveX/RxJava) Observables. No more managing GoogleApiClients! Also, there are some helper classes, which ease communication between phone and wear app.
This library wraps the Wearable API in [RxJava](https://github.com/ReactiveX/RxJava) Observables and Singles. No more managing GoogleApiClients! Also, there are some helper classes, which ease communication between phone and wear app.

# Usage

Initialize RxWear once, preferably in your Application `onCreate()` via `RxWear.init(...)`. The RxWear class is very similar to the Wearable class provided by the Wearable API. Instead of `Wearable.MessageApi.sendMessage(apiClient, nodeId, path, data)` you can use `RxWear.Message.send(nodeId, path, data)`.

There are also some helper classes to ease the putting/sending of data:
There are also some helper classes to ease the putting/sending of data.
* `RxWear.Data.PutDataMap`: Use this to put a DataItem containing a DataMap to a path or a pathPrefix with an auto appended ID.
* `RxWear.Data.PutSerializable`: Use this to put a DataItem containing a Serializable object to a path or a pathPrefix with an auto appended ID.
* `RxWear.Message.SendDataMap`: Use this to send a message containing a DataMap to either one specific node or all remote nodes.
* `RxWear.Message.SendSerializable`: Use this to send a message containing a Serializable object to either one specific node or all remote nodes.

And a few Transformers to ease fetching the data:
A few Observable Transformers are included to ease fetching the data. Since these include filtering, they cannot operate on Singles by default, but you can use `single.toObservable().compose(...)`.
* `DataEventGetDataMap`: Use this Transformer to get the DataMap from a DataEvent and optionally filter the events.
* `DataEventGetSerializable`: Use this Transformer to get a Serializable object from a DataEvent and optionally filter the events.
* `DataItemGetDataMap`: Use this Transformer to get the DataMap from a DataItem and optionally filter the items.
Expand Down Expand Up @@ -62,7 +62,7 @@ RxWear.Data.listen()

An optional global default timeout for all Wearable API requests made through the library can be set via `RxWear.setDefaultTimeout(...)`. In addition, timeouts can be set when creating a new Observable by providing timeout parameters, e.g. `RxWear.Message.send(nodeId, path, data, 15, TimeUnit.SECONDS)`. These parameters override the default timeout. When a timeout occurs, a StatusException is provided via `onError()`. The timeouts specified here are only used for calls to the Wearable API, e.g. a timeout will not occur when a listener does not emit an item within the specified timeout. The RxJava timeout operators can be used for this use case.

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

The following Exceptions are thrown in the lib and provided via `onError()`:

Expand All @@ -79,7 +79,7 @@ A basic sample app is available in the `sample` and `wearsample` projects.
The lib is available on jCenter. Add the following to your `build.gradle`:

dependencies {
compile 'com.patloew.rxwear:rxwear:1.0.0'
compile 'com.patloew.rxwear:rxwear:1.1.0'
}

# Credits
Expand Down
6 changes: 3 additions & 3 deletions library/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ apply plugin: 'com.jfrog.bintray'
apply plugin: 'com.github.dcendents.android-maven'

group = 'com.patloew.rxwear'
version = '1.0.0'
version = '1.1.0'
project.archivesBaseName = 'rxwear'

android {
Expand All @@ -13,8 +13,8 @@ android {
defaultConfig {
minSdkVersion 9
targetSdkVersion 23
versionCode 1
versionName "1.0.0"
versionCode 2
versionName "1.1.0"
}
buildTypes {
release {
Expand Down
102 changes: 12 additions & 90 deletions library/src/main/java/com/patloew/rxwear/BaseObservable.java
Original file line number Diff line number Diff line change
@@ -1,22 +1,14 @@
package com.patloew.rxwear;

import android.content.Context;
import android.os.Bundle;
import android.support.annotation.NonNull;

import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.Api;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.PendingResult;
import com.google.android.gms.common.api.Result;
import com.google.android.gms.common.api.ResultCallback;
import com.google.android.gms.common.api.Scope;
import com.google.android.gms.wearable.Wearable;

import java.util.concurrent.TimeUnit;

import rx.Observable;
import rx.Observer;
import rx.Subscriber;
import rx.functions.Action0;
import rx.subscriptions.Subscriptions;
Expand All @@ -40,46 +32,15 @@
* FILE MODIFIED by Patrick Löwenstein, 2016
*
*/
public abstract class BaseObservable<T> implements Observable.OnSubscribe<T> {
private final Context ctx;
private final Api<? extends Api.ApiOptions.NotRequiredOptions>[] services;
private final Scope[] scopes;
private final Long timeoutTime;
private final TimeUnit timeoutUnit;
public abstract class BaseObservable<T> extends BaseRx<T> implements Observable.OnSubscribe<T> {

protected BaseObservable(@NonNull RxWear rxWear, Long timeout, TimeUnit timeUnit) {
this.ctx = rxWear.getContext();
this.services = new Api[] {Wearable.API };
this.scopes = null;

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

protected BaseObservable(@NonNull Context ctx, @NonNull Api<? extends Api.ApiOptions.NotRequiredOptions>[] services, Scope[] scopes) {
this.ctx = ctx;
this.services = services;
this.scopes = scopes;
timeoutTime = null;
timeoutUnit = null;
}

protected final <T extends Result> void setupWearPendingResult(PendingResult<T> pendingResult, ResultCallback<? super T> resultCallback) {
if(timeoutTime != null && timeoutUnit != null) {
pendingResult.setResultCallback(resultCallback, timeoutTime, timeoutUnit);
} else {
pendingResult.setResultCallback(resultCallback);
}
super(rxWear, timeout, timeUnit);
}

@Override
public final void call(Subscriber<? super T> subscriber) {
final GoogleApiClient apiClient = createApiClient(subscriber);
final GoogleApiClient apiClient = createApiClient(new ApiClientConnectionCallbacks(subscriber));

try {
apiClient.connect();
Expand All @@ -98,72 +59,33 @@ public void call() {
}));
}

protected abstract void onGoogleApiClientReady(GoogleApiClient apiClient, Subscriber<? super T> subscriber);

GoogleApiClient createApiClient(Subscriber<? super T> subscriber) {

ApiClientConnectionCallbacks apiClientConnectionCallbacks = new ApiClientConnectionCallbacks(subscriber);

GoogleApiClient.Builder apiClientBuilder = new GoogleApiClient.Builder(ctx);


for (Api<? extends Api.ApiOptions.NotRequiredOptions> service : services) {
apiClientBuilder.addApi(service);
}

if(scopes != null) {
for (Scope scope : scopes) {
apiClientBuilder.addScope(scope);
}
}

apiClientBuilder.addConnectionCallbacks(apiClientConnectionCallbacks);
apiClientBuilder.addOnConnectionFailedListener(apiClientConnectionCallbacks);

GoogleApiClient apiClient = apiClientBuilder.build();

apiClientConnectionCallbacks.setClient(apiClient);

return apiClient;

}

protected void onUnsubscribed(GoogleApiClient locationClient) { }

protected abstract void onGoogleApiClientReady(GoogleApiClient apiClient, Observer<? super T> observer);

private class ApiClientConnectionCallbacks implements
GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener {
protected class ApiClientConnectionCallbacks extends BaseRx.ApiClientConnectionCallbacks {

final private Observer<? super T> observer;
final protected Subscriber<? super T> subscriber;

private GoogleApiClient apiClient;

private ApiClientConnectionCallbacks(Observer<? super T> observer) {
this.observer = observer;
private ApiClientConnectionCallbacks(Subscriber<? super T> subscriber) {
this.subscriber = subscriber;
}

@Override
public void onConnected(Bundle bundle) {
try {
onGoogleApiClientReady(apiClient, observer);
onGoogleApiClientReady(apiClient, subscriber);
} catch (Throwable ex) {
observer.onError(ex);
subscriber.onError(ex);
}
}

@Override
public void onConnectionSuspended(int cause) {
observer.onError(new GoogleAPIConnectionSuspendedException(cause));
subscriber.onError(new GoogleAPIConnectionSuspendedException(cause));
}

@Override
public void onConnectionFailed(ConnectionResult connectionResult) {
observer.onError(new GoogleAPIConnectionException("Error connecting to GoogleApiClient.", connectionResult));
}

public void setClient(GoogleApiClient client) {
this.apiClient = client;
subscriber.onError(new GoogleAPIConnectionException("Error connecting to GoogleApiClient.", connectionResult));
}
}
}
111 changes: 111 additions & 0 deletions library/src/main/java/com/patloew/rxwear/BaseRx.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package com.patloew.rxwear;

import android.content.Context;
import android.support.annotation.NonNull;

import com.google.android.gms.common.api.Api;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.PendingResult;
import com.google.android.gms.common.api.Result;
import com.google.android.gms.common.api.ResultCallback;
import com.google.android.gms.common.api.Scope;
import com.google.android.gms.wearable.Wearable;

import java.util.concurrent.TimeUnit;

/* Copyright (C) 2015 Michał Charmas (http://blog.charmas.pl)
*
* 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.
*
* ---------------
*
* FILE MODIFIED by Patrick Löwenstein, 2016
*
*/
public abstract class BaseRx<T> {
private final Context ctx;
private final Api<? extends Api.ApiOptions.NotRequiredOptions>[] services;
private final Scope[] scopes;
private final Long timeoutTime;
private final TimeUnit timeoutUnit;

protected BaseRx(@NonNull RxWear rxWear, Long timeout, TimeUnit timeUnit) {
this.ctx = rxWear.getContext();
this.services = new Api[] {Wearable.API };
this.scopes = null;

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

protected BaseRx(@NonNull Context ctx, @NonNull Api<? extends Api.ApiOptions.NotRequiredOptions>[] services, Scope[] scopes) {
this.ctx = ctx;
this.services = services;
this.scopes = scopes;
timeoutTime = null;
timeoutUnit = null;
}

protected final <T extends Result> void setupWearPendingResult(PendingResult<T> pendingResult, ResultCallback<? super T> resultCallback) {
if(timeoutTime != null && timeoutUnit != null) {
pendingResult.setResultCallback(resultCallback, timeoutTime, timeoutUnit);
} else {
pendingResult.setResultCallback(resultCallback);
}
}

protected final GoogleApiClient createApiClient(ApiClientConnectionCallbacks apiClientConnectionCallbacks) {

GoogleApiClient.Builder apiClientBuilder = new GoogleApiClient.Builder(ctx);


for (Api<? extends Api.ApiOptions.NotRequiredOptions> service : services) {
apiClientBuilder.addApi(service);
}

if(scopes != null) {
for (Scope scope : scopes) {
apiClientBuilder.addScope(scope);
}
}

apiClientBuilder.addConnectionCallbacks(apiClientConnectionCallbacks);
apiClientBuilder.addOnConnectionFailedListener(apiClientConnectionCallbacks);

GoogleApiClient apiClient = apiClientBuilder.build();

apiClientConnectionCallbacks.setClient(apiClient);

return apiClient;
}

protected void onUnsubscribed(GoogleApiClient locationClient) { }

protected abstract class ApiClientConnectionCallbacks implements
GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener {

protected GoogleApiClient apiClient;

protected ApiClientConnectionCallbacks() { }

public void setClient(GoogleApiClient client) {
this.apiClient = client;
}
}
}
Loading

0 comments on commit 0423120

Please sign in to comment.