Compare commits
15 Commits
feature/up
...
master
| Author | SHA1 | Date |
|---|---|---|
|
|
d08b7df5f0 | |
|
|
333cb3fa64 | |
|
|
098db7d2e3 | |
|
|
e1eed0c37f | |
|
|
de538e14ba | |
|
|
054b62424c | |
|
|
a863285016 | |
|
|
8383a41bca | |
|
|
b6d90da359 | |
|
|
f66d85c9c6 | |
|
|
4049f4e458 | |
|
|
dc2b9ef3ff | |
|
|
20a9597c35 | |
|
|
f7f98f0106 | |
|
|
2dbe8a45e3 |
37
build.gradle
37
build.gradle
|
|
@ -1,9 +1,7 @@
|
||||||
apply plugin: 'com.android.library'
|
apply plugin: 'com.android.library'
|
||||||
apply plugin: 'me.tatarka.retrolambda'
|
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion compileSdk
|
compileSdkVersion compileSdk
|
||||||
buildToolsVersion buildTools
|
|
||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
sourceCompatibility JavaVersion.VERSION_1_8
|
sourceCompatibility JavaVersion.VERSION_1_8
|
||||||
|
|
@ -17,39 +15,40 @@ android {
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
maven { url 'https://maven.fabric.io/public' }
|
maven { url 'https://maven.fabric.io/public' }
|
||||||
|
maven { url "http://dl.bintray.com/touchin/touchin-tools" }
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compile project(path: ':libraries:components')
|
api project(path: ':libraries:components')
|
||||||
|
|
||||||
compile 'net.danlew:android.joda:2.9.9'
|
api 'net.danlew:android.joda:2.9.9'
|
||||||
compile 'com.android.support:multidex:1.0.2'
|
api 'com.android.support:multidex:1.0.2'
|
||||||
|
api "io.reactivex:rxandroid:$rxAndroidVersion"
|
||||||
|
api "io.reactivex:rxjava:$rxJavaVersion"
|
||||||
|
|
||||||
compile "io.reactivex.rxjava2:rxandroid:$rxAndroidVersion"
|
compileOnly "com.android.support:appcompat-v7:$supportLibraryVersion"
|
||||||
compile "io.reactivex.rxjava2:rxjava:$rxJavaVersion"
|
compileOnly "com.android.support:recyclerview-v7:$supportLibraryVersion"
|
||||||
|
|
||||||
provided "com.android.support:recyclerview-v7:$supportLibraryVersion"
|
compileOnly 'com.squareup.retrofit2:retrofit:2.3.0'
|
||||||
provided "com.android.support:appcompat-v7:$supportLibraryVersion"
|
compileOnly('com.google.http-client:google-http-client-jackson2:1.23.0') {
|
||||||
|
exclude(group: 'org.apache.httpcomponents', module: 'httpclient')
|
||||||
|
}
|
||||||
|
|
||||||
provided "com.squareup.retrofit2:retrofit:$retrofitVersion"
|
compileOnly 'com.facebook.fresco:fresco:1.5.0'
|
||||||
|
compileOnly 'ru.touchin:logansquare:1.4.1'
|
||||||
|
|
||||||
//bug 1.7.0 https://github.com/facebook/fresco/issues/1991
|
compileOnly 'com.scottyab:aes-crypto:0.0.4'
|
||||||
//noinspection NewerVersionAvailable
|
|
||||||
provided 'com.facebook.fresco:fresco:1.5.0'
|
|
||||||
provided 'com.bluelinelabs:logansquare:1.3.7'
|
|
||||||
|
|
||||||
provided 'com.scottyab:aes-crypto:0.0.5'
|
|
||||||
|
|
||||||
// don't use latest(1.0 and above) because they don't support Socket.IO server 1.x version
|
// don't use latest(1.0 and above) because they don't support Socket.IO server 1.x version
|
||||||
//noinspection NewerVersionAvailable
|
//noinspection NewerVersionAvailable
|
||||||
provided('io.socket:socket.io-client:0.9.0') {
|
compileOnly('io.socket:socket.io-client:0.9.0') {
|
||||||
exclude group: 'org.json', module: 'json'
|
exclude group: 'org.json', module: 'json'
|
||||||
}
|
}
|
||||||
|
|
||||||
provided('com.crashlytics.sdk.android:crashlytics:2.6.8@aar') {
|
compileOnly('com.crashlytics.sdk.android:crashlytics:2.6.8@aar') {
|
||||||
transitive = true
|
transitive = true
|
||||||
}
|
}
|
||||||
|
|
||||||
provided 'com.facebook.stetho:stetho:1.5.0'
|
compileOnly 'com.facebook.stetho:stetho:1.5.0'
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,9 +19,7 @@
|
||||||
|
|
||||||
package ru.touchin.templates;
|
package ru.touchin.templates;
|
||||||
|
|
||||||
import android.content.BroadcastReceiver;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.IntentFilter;
|
import android.content.IntentFilter;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.net.ConnectivityManager;
|
import android.net.ConnectivityManager;
|
||||||
|
|
@ -29,19 +27,15 @@ import android.net.NetworkInfo;
|
||||||
import android.net.wifi.WifiManager;
|
import android.net.wifi.WifiManager;
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
|
||||||
import android.telephony.TelephonyManager;
|
import android.telephony.TelephonyManager;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
import io.reactivex.Completable;
|
|
||||||
import io.reactivex.Observable;
|
|
||||||
import io.reactivex.ObservableEmitter;
|
|
||||||
import ru.touchin.roboswag.core.log.Lc;
|
import ru.touchin.roboswag.core.log.Lc;
|
||||||
import ru.touchin.roboswag.core.observables.RxAndroidUtils;
|
import ru.touchin.roboswag.core.observables.RxAndroidUtils;
|
||||||
|
import rx.Observable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utility class that is providing common methods related to android device.
|
* Utility class that is providing common methods related to android device.
|
||||||
|
|
@ -177,52 +171,6 @@ public final class DeviceUtils {
|
||||||
.distinctUntilChanged();
|
.distinctUntilChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns observable to observe is device connected to the internet.
|
|
||||||
*
|
|
||||||
* @param context Context to register BroadcastReceiver to check connection to the internet;
|
|
||||||
* @return Observable of internet connection status.
|
|
||||||
*/
|
|
||||||
@NonNull
|
|
||||||
public static Observable<Boolean> observeIsNetworkConnected(@NonNull final Context context) {
|
|
||||||
return Observable.switchOnNext(Observable.fromCallable(() -> {
|
|
||||||
final NetworkStateReceiver networkStateReceiver = new NetworkStateReceiver();
|
|
||||||
return Observable
|
|
||||||
.<Boolean>create(subscriber -> {
|
|
||||||
context.registerReceiver(networkStateReceiver, NetworkStateReceiver.INTENT_FILTER);
|
|
||||||
subscriber.onNext(isNetworkConnected(context));
|
|
||||||
networkStateReceiver.setEmitter(subscriber);
|
|
||||||
})
|
|
||||||
.doOnDispose(() -> context.unregisterReceiver(networkStateReceiver))
|
|
||||||
.onErrorReturnItem(false)
|
|
||||||
.distinctUntilChanged();
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create an Observable that depends on network connection.
|
|
||||||
*
|
|
||||||
* @param processObservable - Observable to which we subscribe in the availability of the Internet;
|
|
||||||
*/
|
|
||||||
@NonNull
|
|
||||||
public static Observable<?> createNetworkDependentObservable(@NonNull final Context context, @NonNull final Observable<?> processObservable) {
|
|
||||||
return DeviceUtils.observeIsNetworkConnected(context)
|
|
||||||
.debounce(100, TimeUnit.MILLISECONDS)
|
|
||||||
.switchMap(connected -> !connected
|
|
||||||
? Observable.empty()
|
|
||||||
: processObservable);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create an Observable that depends on network connection.
|
|
||||||
*
|
|
||||||
* @param processObservable - Observable to which we subscribe in the availability of the Internet;
|
|
||||||
*/
|
|
||||||
@NonNull
|
|
||||||
public static Observable<?> createNetworkDependentObservable(@NonNull final Context context, @NonNull final Completable processObservable) {
|
|
||||||
return createNetworkDependentObservable(context, processObservable.toObservable());
|
|
||||||
}
|
|
||||||
|
|
||||||
private DeviceUtils() {
|
private DeviceUtils() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -272,27 +220,4 @@ public final class DeviceUtils {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class NetworkStateReceiver extends BroadcastReceiver {
|
|
||||||
|
|
||||||
private static final IntentFilter INTENT_FILTER = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
|
|
||||||
@Nullable
|
|
||||||
private ConnectivityManager connectivityManager;
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private ObservableEmitter<? super Boolean> emitter;
|
|
||||||
|
|
||||||
public void setEmitter(@Nullable final ObservableEmitter<? super Boolean> emitter) {
|
|
||||||
this.emitter = emitter;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onReceive(@NonNull final Context context, @NonNull final Intent intent) {
|
|
||||||
if (connectivityManager == null) {
|
|
||||||
connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
|
|
||||||
}
|
|
||||||
if (emitter != null) {
|
|
||||||
emitter.onNext(isNetworkConnected(context));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -21,7 +21,7 @@ package ru.touchin.templates;
|
||||||
|
|
||||||
import android.app.ActivityManager;
|
import android.app.ActivityManager;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.graphics.drawable.BitmapDrawable;
|
import android.graphics.BitmapFactory;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.ColorRes;
|
import android.support.annotation.ColorRes;
|
||||||
|
|
@ -62,9 +62,14 @@ public abstract class TouchinActivity<TLogic extends Logic> extends ViewControll
|
||||||
* @param primaryColorRes Color of application to show in task bar.
|
* @param primaryColorRes Color of application to show in task bar.
|
||||||
*/
|
*/
|
||||||
protected void setupTaskDescriptor(@NonNull final String label, @DrawableRes final int iconRes, @ColorRes final int primaryColorRes) {
|
protected void setupTaskDescriptor(@NonNull final String label, @DrawableRes final int iconRes, @ColorRes final int primaryColorRes) {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||||
final ActivityManager.TaskDescription taskDescription = new ActivityManager.TaskDescription(label,
|
final ActivityManager.TaskDescription taskDescription = new ActivityManager.TaskDescription(label,
|
||||||
((BitmapDrawable) ContextCompat.getDrawable(this, iconRes)).getBitmap(),
|
iconRes,
|
||||||
|
ContextCompat.getColor(this, primaryColorRes));
|
||||||
|
setTaskDescription(taskDescription);
|
||||||
|
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
|
final ActivityManager.TaskDescription taskDescription = new ActivityManager.TaskDescription(label,
|
||||||
|
BitmapFactory.decodeResource(getResources(), iconRes),
|
||||||
ContextCompat.getColor(this, primaryColorRes));
|
ContextCompat.getColor(this, primaryColorRes));
|
||||||
setTaskDescription(taskDescription);
|
setTaskDescription(taskDescription);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -38,11 +38,6 @@ import java.util.List;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import io.fabric.sdk.android.Fabric;
|
import io.fabric.sdk.android.Fabric;
|
||||||
import io.reactivex.Scheduler;
|
|
||||||
import io.reactivex.android.plugins.RxAndroidPlugins;
|
|
||||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
|
||||||
import io.reactivex.disposables.Disposable;
|
|
||||||
import io.reactivex.disposables.Disposables;
|
|
||||||
import ru.touchin.roboswag.components.adapters.ObservableCollectionAdapter;
|
import ru.touchin.roboswag.components.adapters.ObservableCollectionAdapter;
|
||||||
import ru.touchin.roboswag.components.navigation.fragments.ViewControllerFragment;
|
import ru.touchin.roboswag.components.navigation.fragments.ViewControllerFragment;
|
||||||
import ru.touchin.roboswag.components.utils.UiUtils;
|
import ru.touchin.roboswag.components.utils.UiUtils;
|
||||||
|
|
@ -54,6 +49,13 @@ import ru.touchin.roboswag.core.log.LcGroup;
|
||||||
import ru.touchin.roboswag.core.log.LcLevel;
|
import ru.touchin.roboswag.core.log.LcLevel;
|
||||||
import ru.touchin.roboswag.core.log.LogProcessor;
|
import ru.touchin.roboswag.core.log.LogProcessor;
|
||||||
import ru.touchin.roboswag.core.utils.ShouldNotHappenException;
|
import ru.touchin.roboswag.core.utils.ShouldNotHappenException;
|
||||||
|
import rx.Scheduler;
|
||||||
|
import rx.Subscription;
|
||||||
|
import rx.android.plugins.RxAndroidPlugins;
|
||||||
|
import rx.android.plugins.RxAndroidSchedulersHook;
|
||||||
|
import rx.android.schedulers.AndroidSchedulers;
|
||||||
|
import rx.functions.Action0;
|
||||||
|
import rx.subscriptions.Subscriptions;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by Gavriil Sitnikov on 10/03/16.
|
* Created by Gavriil Sitnikov on 10/03/16.
|
||||||
|
|
@ -80,16 +82,17 @@ public abstract class TouchinApp extends Application {
|
||||||
@Override
|
@Override
|
||||||
public void onCreate() {
|
public void onCreate() {
|
||||||
super.onCreate();
|
super.onCreate();
|
||||||
RxAndroidPlugins.setMainThreadSchedulerHandler(schedulerCallable -> MainThreadScheduler.INSTANCE);
|
RxAndroidPlugins.getInstance().registerSchedulersHook(new RxAndroidSchedulersHook() {
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public Scheduler getMainThreadScheduler() {
|
||||||
|
return new MainThreadScheduler();
|
||||||
|
}
|
||||||
|
});
|
||||||
JodaTimeAndroid.init(this);
|
JodaTimeAndroid.init(this);
|
||||||
if (isDebug()) {
|
if (isDebug()) {
|
||||||
enableStrictMode();
|
enableStrictMode();
|
||||||
try {
|
ObservableCollectionAdapter.setInDebugMode();
|
||||||
ObservableCollectionAdapter.setInDebugMode();
|
|
||||||
} catch (final NoClassDefFoundError error) {
|
|
||||||
Lc.w("RecyclerView initialization error! Did you forget to add debugCompile "
|
|
||||||
+ "'com.android.support:recyclerview-v7:+' to your build.gradle?");
|
|
||||||
}
|
|
||||||
ViewControllerFragment.setInDebugMode();
|
ViewControllerFragment.setInDebugMode();
|
||||||
TypefacedEditText.setInDebugMode();
|
TypefacedEditText.setInDebugMode();
|
||||||
TypefacedTextView.setInDebugMode();
|
TypefacedTextView.setInDebugMode();
|
||||||
|
|
@ -186,8 +189,6 @@ public abstract class TouchinApp extends Application {
|
||||||
*/
|
*/
|
||||||
private static class MainThreadScheduler extends Scheduler {
|
private static class MainThreadScheduler extends Scheduler {
|
||||||
|
|
||||||
public static final MainThreadScheduler INSTANCE = new MainThreadScheduler();
|
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public Worker createWorker() {
|
public Worker createWorker() {
|
||||||
|
|
@ -201,28 +202,28 @@ public abstract class TouchinApp extends Application {
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public Disposable schedule(@NonNull final Runnable action) {
|
public Subscription schedule(@NonNull final Action0 action) {
|
||||||
if (Looper.getMainLooper().equals(Looper.myLooper())) {
|
if (Looper.getMainLooper().equals(Looper.myLooper())) {
|
||||||
action.run();
|
action.call();
|
||||||
return Disposables.disposed();
|
return Subscriptions.unsubscribed();
|
||||||
}
|
}
|
||||||
return parentWorker.schedule(action);
|
return parentWorker.schedule(action);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public Disposable schedule(@NonNull final Runnable action, final long delayTime, @NonNull final TimeUnit unit) {
|
public Subscription schedule(@NonNull final Action0 action, final long delayTime, @NonNull final TimeUnit unit) {
|
||||||
return parentWorker.schedule(action, delayTime, unit);
|
return parentWorker.schedule(action, delayTime, unit);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void dispose() {
|
public void unsubscribe() {
|
||||||
parentWorker.dispose();
|
parentWorker.unsubscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isDisposed() {
|
public boolean isUnsubscribed() {
|
||||||
return parentWorker.isDisposed();
|
return parentWorker.isUnsubscribed();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,26 +25,25 @@ import android.os.Handler;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
|
|
||||||
import io.reactivex.Completable;
|
|
||||||
import io.reactivex.CompletableEmitter;
|
|
||||||
import io.reactivex.Emitter;
|
|
||||||
import io.reactivex.Maybe;
|
|
||||||
import io.reactivex.MaybeEmitter;
|
|
||||||
import io.reactivex.Observable;
|
|
||||||
import io.reactivex.Single;
|
|
||||||
import io.reactivex.SingleEmitter;
|
|
||||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
|
||||||
import io.reactivex.disposables.Disposable;
|
|
||||||
import io.reactivex.functions.Action;
|
|
||||||
import io.reactivex.functions.Consumer;
|
|
||||||
import io.reactivex.internal.functions.Functions;
|
|
||||||
import io.reactivex.subjects.BehaviorSubject;
|
|
||||||
import ru.touchin.roboswag.components.navigation.activities.ViewControllerActivity;
|
import ru.touchin.roboswag.components.navigation.activities.ViewControllerActivity;
|
||||||
import ru.touchin.roboswag.components.utils.Logic;
|
import ru.touchin.roboswag.components.utils.Logic;
|
||||||
import ru.touchin.roboswag.components.utils.UiUtils;
|
import ru.touchin.roboswag.components.utils.UiUtils;
|
||||||
import ru.touchin.roboswag.core.log.Lc;
|
import ru.touchin.roboswag.core.log.Lc;
|
||||||
import ru.touchin.roboswag.core.utils.ServiceBinder;
|
import ru.touchin.roboswag.core.utils.ServiceBinder;
|
||||||
import ru.touchin.roboswag.core.utils.ShouldNotHappenException;
|
import ru.touchin.roboswag.core.utils.ShouldNotHappenException;
|
||||||
|
import rx.Completable;
|
||||||
|
import rx.CompletableSubscriber;
|
||||||
|
import rx.Observable;
|
||||||
|
import rx.Single;
|
||||||
|
import rx.SingleSubscriber;
|
||||||
|
import rx.Subscriber;
|
||||||
|
import rx.Subscription;
|
||||||
|
import rx.android.schedulers.AndroidSchedulers;
|
||||||
|
import rx.exceptions.OnErrorThrowable;
|
||||||
|
import rx.functions.Action0;
|
||||||
|
import rx.functions.Action1;
|
||||||
|
import rx.functions.Actions;
|
||||||
|
import rx.subjects.BehaviorSubject;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by Gavriil Sitnikov on 10/01/17.
|
* Created by Gavriil Sitnikov on 10/01/17.
|
||||||
|
|
@ -101,11 +100,11 @@ public abstract class TouchinService<TLogic extends Logic> extends Service {
|
||||||
*
|
*
|
||||||
* @param observable {@link Observable} to subscribe until onDestroy;
|
* @param observable {@link Observable} to subscribe until onDestroy;
|
||||||
* @param <T> Type of emitted by observable items;
|
* @param <T> Type of emitted by observable items;
|
||||||
* @return {@link Disposable} which is wrapping source observable to unsubscribe from it onDestroy.
|
* @return {@link Subscription} which is wrapping source observable to unsubscribe from it onDestroy.
|
||||||
*/
|
*/
|
||||||
@NonNull
|
@NonNull
|
||||||
public <T> Disposable untilDestroy(@NonNull final Observable<T> observable) {
|
public <T> Subscription untilDestroy(@NonNull final Observable<T> observable) {
|
||||||
return untilDestroy(observable, Functions.emptyConsumer(), getActionThrowableForAssertion(Lc.getCodePoint(this, 1)), Functions.EMPTY_ACTION);
|
return untilDestroy(observable, Actions.empty(), getActionThrowableForAssertion(Lc.getCodePoint(this, 1)), Actions.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -114,14 +113,14 @@ public abstract class TouchinService<TLogic extends Logic> extends Service {
|
||||||
* Don't forget to process errors if observable can emit them.
|
* Don't forget to process errors if observable can emit them.
|
||||||
*
|
*
|
||||||
* @param observable {@link Observable} to subscribe until onDestroy;
|
* @param observable {@link Observable} to subscribe until onDestroy;
|
||||||
* @param onNextAction Action which will raise on every {@link Emitter#onNext(Object)} item;
|
* @param onNextAction Action which will raise on every {@link Subscriber#onNext(Object)} item;
|
||||||
* @param <T> Type of emitted by observable items;
|
* @param <T> Type of emitted by observable items;
|
||||||
* @return {@link Disposable} which is wrapping source observable to unsubscribe from it onDestroy.
|
* @return {@link Subscription} which is wrapping source observable to unsubscribe from it onDestroy.
|
||||||
*/
|
*/
|
||||||
@NonNull
|
@NonNull
|
||||||
public <T> Disposable untilDestroy(@NonNull final Observable<T> observable,
|
public <T> Subscription untilDestroy(@NonNull final Observable<T> observable,
|
||||||
@NonNull final Consumer<T> onNextAction) {
|
@NonNull final Action1<T> onNextAction) {
|
||||||
return untilDestroy(observable, onNextAction, getActionThrowableForAssertion(Lc.getCodePoint(this, 1)), Functions.EMPTY_ACTION);
|
return untilDestroy(observable, onNextAction, getActionThrowableForAssertion(Lc.getCodePoint(this, 1)), Actions.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -130,16 +129,16 @@ public abstract class TouchinService<TLogic extends Logic> extends Service {
|
||||||
* Don't forget to process errors if observable can emit them.
|
* Don't forget to process errors if observable can emit them.
|
||||||
*
|
*
|
||||||
* @param observable {@link Observable} to subscribe until onDestroy;
|
* @param observable {@link Observable} to subscribe until onDestroy;
|
||||||
* @param onNextAction Action which will raise on every {@link Emitter#onNext(Object)} item;
|
* @param onNextAction Action which will raise on every {@link Subscriber#onNext(Object)} item;
|
||||||
* @param onErrorAction Action which will raise on every {@link Emitter#onError(Throwable)} throwable;
|
* @param onErrorAction Action which will raise on every {@link Subscriber#onError(Throwable)} throwable;
|
||||||
* @param <T> Type of emitted by observable items;
|
* @param <T> Type of emitted by observable items;
|
||||||
* @return {@link Disposable} which is wrapping source observable to unsubscribe from it onDestroy.
|
* @return {@link Subscription} which is wrapping source observable to unsubscribe from it onDestroy.
|
||||||
*/
|
*/
|
||||||
@NonNull
|
@NonNull
|
||||||
public <T> Disposable untilDestroy(@NonNull final Observable<T> observable,
|
public <T> Subscription untilDestroy(@NonNull final Observable<T> observable,
|
||||||
@NonNull final Consumer<T> onNextAction,
|
@NonNull final Action1<T> onNextAction,
|
||||||
@NonNull final Consumer<Throwable> onErrorAction) {
|
@NonNull final Action1<Throwable> onErrorAction) {
|
||||||
return untilDestroy(observable, onNextAction, onErrorAction, Functions.EMPTY_ACTION);
|
return untilDestroy(observable, onNextAction, onErrorAction, Actions.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -148,17 +147,17 @@ public abstract class TouchinService<TLogic extends Logic> extends Service {
|
||||||
* Don't forget to process errors if observable can emit them.
|
* Don't forget to process errors if observable can emit them.
|
||||||
*
|
*
|
||||||
* @param observable {@link Observable} to subscribe until onDestroy;
|
* @param observable {@link Observable} to subscribe until onDestroy;
|
||||||
* @param onNextAction Action which will raise on every {@link Emitter#onNext(Object)} item;
|
* @param onNextAction Action which will raise on every {@link Subscriber#onNext(Object)} item;
|
||||||
* @param onErrorAction Action which will raise on every {@link Emitter#onError(Throwable)} throwable;
|
* @param onErrorAction Action which will raise on every {@link Subscriber#onError(Throwable)} throwable;
|
||||||
* @param onCompletedAction Action which will raise at {@link Emitter#onComplete()} on completion of observable;
|
* @param onCompletedAction Action which will raise at {@link Subscriber#onCompleted()} on completion of observable;
|
||||||
* @param <T> Type of emitted by observable items;
|
* @param <T> Type of emitted by observable items;
|
||||||
* @return {@link Disposable} which is wrapping source observable to unsubscribe from it onDestroy.
|
* @return {@link Subscription} which is wrapping source observable to unsubscribe from it onDestroy.
|
||||||
*/
|
*/
|
||||||
@NonNull
|
@NonNull
|
||||||
public <T> Disposable untilDestroy(@NonNull final Observable<T> observable,
|
public <T> Subscription untilDestroy(@NonNull final Observable<T> observable,
|
||||||
@NonNull final Consumer<T> onNextAction,
|
@NonNull final Action1<T> onNextAction,
|
||||||
@NonNull final Consumer<Throwable> onErrorAction,
|
@NonNull final Action1<Throwable> onErrorAction,
|
||||||
@NonNull final Action onCompletedAction) {
|
@NonNull final Action0 onCompletedAction) {
|
||||||
return until(observable, isCreatedSubject.map(created -> !created), onNextAction, onErrorAction, onCompletedAction);
|
return until(observable, isCreatedSubject.map(created -> !created), onNextAction, onErrorAction, onCompletedAction);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -169,11 +168,11 @@ public abstract class TouchinService<TLogic extends Logic> extends Service {
|
||||||
*
|
*
|
||||||
* @param single {@link Single} to subscribe until onDestroy;
|
* @param single {@link Single} to subscribe until onDestroy;
|
||||||
* @param <T> Type of emitted by single items;
|
* @param <T> Type of emitted by single items;
|
||||||
* @return {@link Disposable} which is wrapping source single to unsubscribe from it onDestroy.
|
* @return {@link Subscription} which is wrapping source single to unsubscribe from it onDestroy.
|
||||||
*/
|
*/
|
||||||
@NonNull
|
@NonNull
|
||||||
public <T> Disposable untilDestroy(@NonNull final Single<T> single) {
|
public <T> Subscription untilDestroy(@NonNull final Single<T> single) {
|
||||||
return untilDestroy(single, Functions.emptyConsumer(), getActionThrowableForAssertion(Lc.getCodePoint(this, 1)));
|
return untilDestroy(single, Actions.empty(), getActionThrowableForAssertion(Lc.getCodePoint(this, 1)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -182,12 +181,12 @@ public abstract class TouchinService<TLogic extends Logic> extends Service {
|
||||||
* Don't forget to process errors if single can emit them.
|
* Don't forget to process errors if single can emit them.
|
||||||
*
|
*
|
||||||
* @param single {@link Single} to subscribe until onDestroy;
|
* @param single {@link Single} to subscribe until onDestroy;
|
||||||
* @param onSuccessAction Action which will raise on {@link SingleEmitter#onSuccess(Object)} item;
|
* @param onSuccessAction Action which will raise on {@link SingleSubscriber#onSuccess(Object)} item;
|
||||||
* @param <T> Type of emitted by single items;
|
* @param <T> Type of emitted by single items;
|
||||||
* @return {@link Disposable} which is wrapping source single to unsubscribe from it onDestroy.
|
* @return {@link Subscription} which is wrapping source single to unsubscribe from it onDestroy.
|
||||||
*/
|
*/
|
||||||
@NonNull
|
@NonNull
|
||||||
public <T> Disposable untilDestroy(@NonNull final Single<T> single, @NonNull final Consumer<T> onSuccessAction) {
|
public <T> Subscription untilDestroy(@NonNull final Single<T> single, @NonNull final Action1<T> onSuccessAction) {
|
||||||
return untilDestroy(single, onSuccessAction, getActionThrowableForAssertion(Lc.getCodePoint(this, 1)));
|
return untilDestroy(single, onSuccessAction, getActionThrowableForAssertion(Lc.getCodePoint(this, 1)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -197,16 +196,16 @@ public abstract class TouchinService<TLogic extends Logic> extends Service {
|
||||||
* Don't forget to process errors if single can emit them.
|
* Don't forget to process errors if single can emit them.
|
||||||
*
|
*
|
||||||
* @param single {@link Single} to subscribe until onDestroy;
|
* @param single {@link Single} to subscribe until onDestroy;
|
||||||
* @param onSuccessAction Action which will raise on {@link SingleEmitter#onSuccess(Object)} item;
|
* @param onSuccessAction Action which will raise on {@link SingleSubscriber#onSuccess(Object)} item;
|
||||||
* @param onErrorAction Action which will raise on every {@link SingleEmitter#onError(Throwable)} throwable;
|
* @param onErrorAction Action which will raise on every {@link SingleSubscriber#onError(Throwable)} throwable;
|
||||||
* @param <T> Type of emitted by single items;
|
* @param <T> Type of emitted by single items;
|
||||||
* @return {@link Disposable} which is wrapping source single to unsubscribe from it onDestroy.
|
* @return {@link Subscription} which is wrapping source single to unsubscribe from it onDestroy.
|
||||||
*/
|
*/
|
||||||
@NonNull
|
@NonNull
|
||||||
public <T> Disposable untilDestroy(@NonNull final Single<T> single,
|
public <T> Subscription untilDestroy(@NonNull final Single<T> single,
|
||||||
@NonNull final Consumer<T> onSuccessAction,
|
@NonNull final Action1<T> onSuccessAction,
|
||||||
@NonNull final Consumer<Throwable> onErrorAction) {
|
@NonNull final Action1<Throwable> onErrorAction) {
|
||||||
return until(single.toObservable(), isCreatedSubject.map(created -> !created), onSuccessAction, onErrorAction, Functions.EMPTY_ACTION);
|
return until(single.toObservable(), isCreatedSubject.map(created -> !created), onSuccessAction, onErrorAction, Actions.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -215,11 +214,11 @@ public abstract class TouchinService<TLogic extends Logic> extends Service {
|
||||||
* Don't forget to process errors if completable can emit them.
|
* Don't forget to process errors if completable can emit them.
|
||||||
*
|
*
|
||||||
* @param completable {@link Completable} to subscribe until onDestroy;
|
* @param completable {@link Completable} to subscribe until onDestroy;
|
||||||
* @return {@link Disposable} which is wrapping source completable to unsubscribe from it onDestroy.
|
* @return {@link Subscription} which is wrapping source completable to unsubscribe from it onDestroy.
|
||||||
*/
|
*/
|
||||||
@NonNull
|
@NonNull
|
||||||
public Disposable untilDestroy(@NonNull final Completable completable) {
|
public Subscription untilDestroy(@NonNull final Completable completable) {
|
||||||
return untilDestroy(completable, Functions.EMPTY_ACTION, getActionThrowableForAssertion(Lc.getCodePoint(this, 1)));
|
return untilDestroy(completable, Actions.empty(), getActionThrowableForAssertion(Lc.getCodePoint(this, 1)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -228,11 +227,11 @@ public abstract class TouchinService<TLogic extends Logic> extends Service {
|
||||||
* Don't forget to process errors if completable can emit them.
|
* Don't forget to process errors if completable can emit them.
|
||||||
*
|
*
|
||||||
* @param completable {@link Completable} to subscribe until onDestroy;
|
* @param completable {@link Completable} to subscribe until onDestroy;
|
||||||
* @param onCompletedAction Action which will raise on every {@link CompletableEmitter#onComplete()} item;
|
* @param onCompletedAction Action which will raise on every {@link CompletableSubscriber#onCompleted()} item;
|
||||||
* @return {@link Disposable} which is wrapping source completable to unsubscribe from it onDestroy.
|
* @return {@link Subscription} which is wrapping source completable to unsubscribe from it onDestroy.
|
||||||
*/
|
*/
|
||||||
@NonNull
|
@NonNull
|
||||||
public Disposable untilDestroy(@NonNull final Completable completable, @NonNull final Action onCompletedAction) {
|
public Subscription untilDestroy(@NonNull final Completable completable, @NonNull final Action0 onCompletedAction) {
|
||||||
return untilDestroy(completable, onCompletedAction, getActionThrowableForAssertion(Lc.getCodePoint(this, 1)));
|
return untilDestroy(completable, onCompletedAction, getActionThrowableForAssertion(Lc.getCodePoint(this, 1)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -242,87 +241,40 @@ public abstract class TouchinService<TLogic extends Logic> extends Service {
|
||||||
* Don't forget to process errors if completable can emit them.
|
* Don't forget to process errors if completable can emit them.
|
||||||
*
|
*
|
||||||
* @param completable {@link Single} to subscribe until onDestroy;
|
* @param completable {@link Single} to subscribe until onDestroy;
|
||||||
* @param onCompletedAction Action which will raise on {@link CompletableEmitter#onComplete()} item;
|
* @param onCompletedAction Action which will raise on {@link CompletableSubscriber#onCompleted()} item;
|
||||||
* @param onErrorAction Action which will raise on every {@link CompletableEmitter#onError(Throwable)} throwable;
|
* @param onErrorAction Action which will raise on every {@link CompletableSubscriber#onError(Throwable)} throwable;
|
||||||
* @return {@link Disposable} which is wrapping source completable to unsubscribe from it onDestroy.
|
* @return {@link Subscription} which is wrapping source completable to unsubscribe from it onDestroy.
|
||||||
*/
|
*/
|
||||||
@NonNull
|
@NonNull
|
||||||
public Disposable untilDestroy(@NonNull final Completable completable,
|
public Subscription untilDestroy(@NonNull final Completable completable,
|
||||||
@NonNull final Action onCompletedAction,
|
@NonNull final Action0 onCompletedAction,
|
||||||
@NonNull final Consumer<Throwable> onErrorAction) {
|
@NonNull final Action1<Throwable> onErrorAction) {
|
||||||
return until(completable.toObservable(), isCreatedSubject.map(created -> !created),
|
return until(completable.toObservable(), isCreatedSubject.map(created -> !created), Actions.empty(), onErrorAction, onCompletedAction);
|
||||||
Functions.emptyConsumer(), onErrorAction, onCompletedAction);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Method should be used to guarantee that maybe won't be subscribed after onDestroy.
|
|
||||||
* It is automatically subscribing to maybe.
|
|
||||||
* Don't forget to process errors if maybe can emit them.
|
|
||||||
*
|
|
||||||
* @param maybe {@link Maybe} to subscribe until onDestroy;
|
|
||||||
* @param <T> Type of emitted by maybe items;
|
|
||||||
* @return {@link Disposable} which is wrapping source maybe to unsubscribe from it onDestroy.
|
|
||||||
*/
|
|
||||||
@NonNull
|
|
||||||
public <T> Disposable untilDestroy(@NonNull final Maybe<T> maybe) {
|
|
||||||
return untilDestroy(maybe, Functions.emptyConsumer(), getActionThrowableForAssertion(Lc.getCodePoint(this, 1)));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Method should be used to guarantee that maybe won't be subscribed after onDestroy.
|
|
||||||
* It is automatically subscribing to maybe and calls onSuccessAction on emitted item.
|
|
||||||
* Don't forget to process errors if maybe can emit them.
|
|
||||||
*
|
|
||||||
* @param maybe {@link Maybe} to subscribe until onDestroy;
|
|
||||||
* @param onSuccessAction Action which will raise on {@link MaybeEmitter#onSuccess(Object)} item;
|
|
||||||
* @param <T> Type of emitted by maybe items;
|
|
||||||
* @return {@link Disposable} which is wrapping source maybe to unsubscribe from it onDestroy.
|
|
||||||
*/
|
|
||||||
@NonNull
|
|
||||||
public <T> Disposable untilDestroy(@NonNull final Maybe<T> maybe, @NonNull final Consumer<T> onSuccessAction) {
|
|
||||||
return untilDestroy(maybe, onSuccessAction, getActionThrowableForAssertion(Lc.getCodePoint(this, 1)));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Method should be used to guarantee that maybe won't be subscribed after onDestroy.
|
|
||||||
* It is automatically subscribing to maybe and calls onSuccessAction and onErrorAction on maybe events.
|
|
||||||
* Don't forget to process errors if maybe can emit them.
|
|
||||||
*
|
|
||||||
* @param maybe {@link Maybe} to subscribe until onDestroy;
|
|
||||||
* @param onSuccessAction Action which will raise on {@link MaybeEmitter#onSuccess(Object)} item;
|
|
||||||
* @param onErrorAction Action which will raise on every {@link MaybeEmitter#onError(Throwable)} throwable;
|
|
||||||
* @param <T> Type of emitted by maybe items;
|
|
||||||
* @return {@link Disposable} which is wrapping source maybe to unsubscribe from it onDestroy.
|
|
||||||
*/
|
|
||||||
@NonNull
|
|
||||||
public <T> Disposable untilDestroy(@NonNull final Maybe<T> maybe,
|
|
||||||
@NonNull final Consumer<T> onSuccessAction,
|
|
||||||
@NonNull final Consumer<Throwable> onErrorAction) {
|
|
||||||
return until(maybe.toObservable(), isCreatedSubject.map(created -> !created), onSuccessAction, onErrorAction, Functions.EMPTY_ACTION);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
private <T> Disposable until(@NonNull final Observable<T> observable,
|
private <T> Subscription until(@NonNull final Observable<T> observable,
|
||||||
@NonNull final Observable<Boolean> conditionSubject,
|
@NonNull final Observable<Boolean> conditionSubject,
|
||||||
@NonNull final Consumer<T> onNextAction,
|
@NonNull final Action1<T> onNextAction,
|
||||||
@NonNull final Consumer<Throwable> onErrorAction,
|
@NonNull final Action1<Throwable> onErrorAction,
|
||||||
@NonNull final Action onCompletedAction) {
|
@NonNull final Action0 onCompletedAction) {
|
||||||
final Observable<T> actualObservable;
|
final Observable<T> actualObservable;
|
||||||
if (onNextAction == Functions.emptyConsumer() && onErrorAction == (Consumer) Functions.emptyConsumer()
|
if (onNextAction == Actions.empty() && onErrorAction == (Action1) Actions.empty() && onCompletedAction == Actions.empty()) {
|
||||||
&& onCompletedAction == Functions.EMPTY_ACTION) {
|
|
||||||
actualObservable = observable;
|
actualObservable = observable;
|
||||||
} else {
|
} else {
|
||||||
actualObservable = observable.observeOn(AndroidSchedulers.mainThread())
|
actualObservable = observable.observeOn(AndroidSchedulers.mainThread())
|
||||||
.doOnComplete(onCompletedAction)
|
.doOnCompleted(onCompletedAction)
|
||||||
.doOnNext(onNextAction)
|
.doOnNext(onNextAction)
|
||||||
.doOnError(onErrorAction);
|
.doOnError(onErrorAction);
|
||||||
}
|
}
|
||||||
|
|
||||||
return isCreatedSubject.firstOrError()
|
return isCreatedSubject.first()
|
||||||
.flatMapObservable(created -> created ? actualObservable : Observable.empty())
|
.switchMap(created -> created ? actualObservable : Observable.empty())
|
||||||
.takeUntil(conditionSubject.filter(condition -> condition))
|
.takeUntil(conditionSubject.filter(condition -> condition))
|
||||||
.onErrorResumeNext(throwable -> {
|
.onErrorResumeNext(throwable -> {
|
||||||
if (throwable instanceof RuntimeException) {
|
final boolean isRxError = throwable instanceof OnErrorThrowable;
|
||||||
|
if ((!isRxError && throwable instanceof RuntimeException)
|
||||||
|
|| (isRxError && throwable.getCause() instanceof RuntimeException)) {
|
||||||
Lc.assertion(throwable);
|
Lc.assertion(throwable);
|
||||||
}
|
}
|
||||||
return Observable.empty();
|
return Observable.empty();
|
||||||
|
|
@ -353,7 +305,7 @@ public abstract class TouchinService<TLogic extends Logic> extends Service {
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
private Consumer<Throwable> getActionThrowableForAssertion(@NonNull final String codePoint) {
|
private Action1<Throwable> getActionThrowableForAssertion(@NonNull final String codePoint) {
|
||||||
return throwable -> Lc.assertion(new ShouldNotHappenException("Unexpected error on untilDestroy at " + codePoint, throwable));
|
return throwable -> Lc.assertion(new ShouldNotHappenException("Unexpected error on untilDestroy at " + codePoint, throwable));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,237 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2016 Touch Instinct
|
||||||
|
*
|
||||||
|
* This file is part of RoboSwag library.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ru.touchin.templates.chat;
|
||||||
|
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import ru.touchin.roboswag.core.log.Lc;
|
||||||
|
import ru.touchin.roboswag.core.observables.collections.ObservableCollection;
|
||||||
|
import ru.touchin.roboswag.core.observables.collections.ObservableList;
|
||||||
|
import rx.Completable;
|
||||||
|
import rx.Observable;
|
||||||
|
import rx.Scheduler;
|
||||||
|
import rx.Subscription;
|
||||||
|
import rx.functions.Actions;
|
||||||
|
import rx.schedulers.Schedulers;
|
||||||
|
import rx.subjects.BehaviorSubject;
|
||||||
|
import rx.subjects.PublishSubject;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Gavriil Sitnikov on 12/05/16.
|
||||||
|
* Object which is containing logic of sending messages as queue one-by-one.
|
||||||
|
*
|
||||||
|
* @param <TOutgoingMessage> Type of messages to send.
|
||||||
|
*/
|
||||||
|
public abstract class Chat<TOutgoingMessage> {
|
||||||
|
|
||||||
|
private static final long RETRY_SENDING_DELAY = TimeUnit.SECONDS.toMillis(5);
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private final ObservableList<TOutgoingMessage> sendingMessages = new ObservableList<>();
|
||||||
|
@NonNull
|
||||||
|
private final PublishSubject<?> retrySendingRequest = PublishSubject.create();
|
||||||
|
@NonNull
|
||||||
|
private final BehaviorSubject<Boolean> isSendingInError = BehaviorSubject.create(false);
|
||||||
|
@NonNull
|
||||||
|
private final Scheduler sendingScheduler = Schedulers.from(Executors.newSingleThreadExecutor());
|
||||||
|
@NonNull
|
||||||
|
private final Observable<?> messagesToSendObservable;
|
||||||
|
@Nullable
|
||||||
|
private Subscription activationSubscription;
|
||||||
|
|
||||||
|
public Chat(@Nullable final Collection<TOutgoingMessage> messagesToSend) {
|
||||||
|
if (messagesToSend != null) {
|
||||||
|
sendingMessages.addAll(messagesToSend);
|
||||||
|
}
|
||||||
|
|
||||||
|
messagesToSendObservable = sendingMessages.observeItems()
|
||||||
|
.first()
|
||||||
|
.concatMap(initialMessages -> {
|
||||||
|
final List<TOutgoingMessage> reversedMessages = new ArrayList<>(initialMessages);
|
||||||
|
Collections.reverse(reversedMessages);
|
||||||
|
return Observable.from(reversedMessages)
|
||||||
|
.concatWith(sendingMessages.observeChanges().concatMap(changes ->
|
||||||
|
changes.getInsertedItems().isEmpty() ? Observable.empty() : Observable.from(changes.getInsertedItems())))
|
||||||
|
//observe on some scheduler?
|
||||||
|
.flatMap(message -> internalSendMessage(message).toObservable());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns {@link Observable} to check if sending have failed so it is in error state and user have to retry send messages.
|
||||||
|
*
|
||||||
|
* @return {@link Observable} to check if sending have failed.
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
public Observable<Boolean> observeIsSendingInError() {
|
||||||
|
return isSendingInError.distinctUntilChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns {@link ObservableCollection} of currently sending messages.
|
||||||
|
*
|
||||||
|
* @return Collection of sending messages.
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
public ObservableCollection<TOutgoingMessage> getSendingMessages() {
|
||||||
|
return sendingMessages;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns {@link Observable} to determine if message is in cache stored on disk.
|
||||||
|
* It is needed to not send message which is already loaded from server and cached.
|
||||||
|
*
|
||||||
|
* @param message Message to check if it is in cache;
|
||||||
|
* @return {@link Observable} which is checking if message is in cache.
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
protected abstract Observable<Boolean> isMessageInCacheObservable(@NonNull final TOutgoingMessage message);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns {@link Observable} to determine if message is in actually loaded messages.
|
||||||
|
* It is needed to not send message which is already loaded from server and showing to user at this moment.
|
||||||
|
*
|
||||||
|
* @param message Message to check if it is in actual data;
|
||||||
|
* @return {@link Observable} which is checking if message is in actual data.
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
protected abstract Observable<Boolean> isMessageInActualObservable(@NonNull final TOutgoingMessage message);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method to create {@link Observable} which is sending message to server.
|
||||||
|
*
|
||||||
|
* @param message Message to send;
|
||||||
|
* @return {@link Observable} to send message.
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
protected abstract Observable<?> createSendMessageObservable(@NonNull final TOutgoingMessage message);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method to start sending message.
|
||||||
|
*
|
||||||
|
* @param message Message to send.
|
||||||
|
*/
|
||||||
|
public void sendMessage(@NonNull final TOutgoingMessage message) {
|
||||||
|
sendingMessages.add(0, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method to start sending collection of messages.
|
||||||
|
*
|
||||||
|
* @param messages Messages to send.
|
||||||
|
*/
|
||||||
|
public void sendMessages(@NonNull final Collection<TOutgoingMessage> messages) {
|
||||||
|
sendingMessages.addAll(0, messages);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Activates chat so it will start sending messages.
|
||||||
|
*/
|
||||||
|
public void activate() {
|
||||||
|
if (activationSubscription != null) {
|
||||||
|
Lc.assertion("Chat already activated");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
activationSubscription = messagesToSendObservable.subscribe();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method to retry send messages.
|
||||||
|
*/
|
||||||
|
public void retrySend() {
|
||||||
|
retrySendingRequest.onNext(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method to cancel sending current message.
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
public Observable<?> observeCancelEvent(@NonNull final TOutgoingMessage message) {
|
||||||
|
return Observable.never();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deactivates chat so it will stop sending messages.
|
||||||
|
*/
|
||||||
|
public void deactivate() {
|
||||||
|
if (activationSubscription == null) {
|
||||||
|
Lc.assertion("Chat not activated yet");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
activationSubscription.unsubscribe();
|
||||||
|
activationSubscription = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private Completable internalSendMessage(@NonNull final TOutgoingMessage message) {
|
||||||
|
final SubscriptionHolder subscriptionHolder = new SubscriptionHolder();
|
||||||
|
return Completable
|
||||||
|
.create(subscriber -> {
|
||||||
|
subscriptionHolder.subscription = sendingScheduler.createWorker().schedule(() -> {
|
||||||
|
final CountDownLatch blocker = new CountDownLatch(1);
|
||||||
|
final Subscription sendSubscription = Observable
|
||||||
|
.combineLatest(isMessageInCacheObservable(message), isMessageInActualObservable(message),
|
||||||
|
(messageInCache, messageInActual) -> !messageInCache && !messageInActual)
|
||||||
|
.subscribeOn(Schedulers.computation())
|
||||||
|
.first()
|
||||||
|
.switchMap(shouldSendMessage -> shouldSendMessage
|
||||||
|
? createSendMessageObservable(message).ignoreElements() : Observable.empty())
|
||||||
|
.takeUntil(observeCancelEvent(message))
|
||||||
|
.retryWhen(attempts -> attempts.switchMap(ignored -> {
|
||||||
|
isSendingInError.onNext(true);
|
||||||
|
return Observable
|
||||||
|
.merge(retrySendingRequest, Observable.timer(RETRY_SENDING_DELAY, TimeUnit.MILLISECONDS))
|
||||||
|
.first()
|
||||||
|
.doOnCompleted(() -> isSendingInError.onNext(false));
|
||||||
|
}))
|
||||||
|
.doOnUnsubscribe(blocker::countDown)
|
||||||
|
.subscribe(Actions.empty(), Lc::assertion, () -> sendingMessages.remove(message));
|
||||||
|
try {
|
||||||
|
blocker.await();
|
||||||
|
} catch (final InterruptedException exception) {
|
||||||
|
sendSubscription.unsubscribe();
|
||||||
|
}
|
||||||
|
subscriber.onCompleted();
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.doOnUnsubscribe(() -> {
|
||||||
|
if (subscriptionHolder.subscription != null && !subscriptionHolder.subscription.isUnsubscribed()) {
|
||||||
|
subscriptionHolder.subscription.unsubscribe();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private class SubscriptionHolder {
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private Subscription subscription;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,91 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2016 Touch Instinct
|
||||||
|
*
|
||||||
|
* This file is part of RoboSwag library.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ru.touchin.templates.googlejson;
|
||||||
|
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
|
||||||
|
import com.google.api.client.http.json.JsonHttpContent;
|
||||||
|
import com.google.api.client.json.jackson2.JacksonFactory;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
|
||||||
|
import okhttp3.RequestBody;
|
||||||
|
import okhttp3.ResponseBody;
|
||||||
|
import retrofit2.Converter;
|
||||||
|
import retrofit2.Retrofit;
|
||||||
|
import ru.touchin.templates.retrofit.JsonRequestBodyConverter;
|
||||||
|
import ru.touchin.templates.retrofit.JsonResponseBodyConverter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Gavriil Sitnikov on 2/06/2016.
|
||||||
|
* Converter class to use with {@link Retrofit} to parse and generate models based on Google Jackson library {@link JacksonFactory}.
|
||||||
|
*/
|
||||||
|
public class GoogleJsonFactory extends Converter.Factory {
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public Converter<ResponseBody, ?> responseBodyConverter(@NonNull final Type type,
|
||||||
|
@NonNull final Annotation[] annotations,
|
||||||
|
@NonNull final Retrofit retrofit) {
|
||||||
|
return new GoogleJsonResponseBodyConverter<>(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public Converter<?, RequestBody> requestBodyConverter(@NonNull final Type type,
|
||||||
|
@NonNull final Annotation[] parameterAnnotations,
|
||||||
|
@NonNull final Annotation[] methodAnnotations,
|
||||||
|
@NonNull final Retrofit retrofit) {
|
||||||
|
return new GoogleJsonRequestBodyConverter<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class GoogleJsonResponseBodyConverter<T> extends JsonResponseBodyConverter<T> {
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private final Type type;
|
||||||
|
|
||||||
|
public GoogleJsonResponseBodyConverter(@NonNull final Type type) {
|
||||||
|
super();
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
protected T parseResponse(@NonNull final ResponseBody value) throws IOException {
|
||||||
|
return (T) GoogleJsonModel.DEFAULT_JSON_FACTORY.createJsonParser(value.charStream()).parse(type, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class GoogleJsonRequestBodyConverter<T> extends JsonRequestBodyConverter<T> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void writeValueToByteArray(@NonNull final T value, @NonNull final ByteArrayOutputStream byteArrayOutputStream)
|
||||||
|
throws IOException {
|
||||||
|
new JsonHttpContent(GoogleJsonModel.DEFAULT_JSON_FACTORY, value).writeTo(byteArrayOutputStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,117 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2016 Touch Instinct
|
||||||
|
*
|
||||||
|
* This file is part of RoboSwag library.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ru.touchin.templates.googlejson;
|
||||||
|
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
|
||||||
|
import com.google.api.client.json.JsonFactory;
|
||||||
|
import com.google.api.client.json.jackson2.JacksonFactory;
|
||||||
|
import com.google.api.client.util.Data;
|
||||||
|
|
||||||
|
import ru.touchin.roboswag.core.log.Lc;
|
||||||
|
import ru.touchin.templates.ApiModel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Gavriil Sitnikov on 13/11/2015.
|
||||||
|
* Simple class with helpers inside to work with models generated by {@link GoogleJsonFactory}.
|
||||||
|
* Mostly used to validate models returned from server.
|
||||||
|
*/
|
||||||
|
public abstract class GoogleJsonModel extends ApiModel {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Just a simple default Google JSON factory to create model parers and generators.
|
||||||
|
*/
|
||||||
|
public static final JsonFactory DEFAULT_JSON_FACTORY = new JacksonFactory();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns if this object not responded from server (no parameter in JSON file).
|
||||||
|
*
|
||||||
|
* @param object Value of field;
|
||||||
|
* @return True if missed.
|
||||||
|
*/
|
||||||
|
protected static boolean isMissed(@Nullable final Object object) {
|
||||||
|
return object == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns if this object is responded from server as null (parameter in JSON file equals null).
|
||||||
|
*
|
||||||
|
* @param object Value of field;
|
||||||
|
* @return True if null.
|
||||||
|
*/
|
||||||
|
protected static boolean isNull(@Nullable final Object object) {
|
||||||
|
return Data.isNull(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns if this object is responded from server as null (parameter in JSON file equals null)
|
||||||
|
* or if this object not responded from server (no parameter in JSON file).
|
||||||
|
*
|
||||||
|
* @param object Value of field;
|
||||||
|
* @return True if null or missed.
|
||||||
|
*/
|
||||||
|
protected static boolean isNullOrMissed(@Nullable final Object object) {
|
||||||
|
return isMissed(object) || isNull(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Throws exception if object is missed or null.
|
||||||
|
*
|
||||||
|
* @param object Value of field to check;
|
||||||
|
* @throws ValidationException Exception of validation.
|
||||||
|
*/
|
||||||
|
protected static void validateNotNull(@Nullable final Object object) throws ValidationException {
|
||||||
|
if (isNull(object)) {
|
||||||
|
throw new ValidationException("Not nullable object is null at " + Lc.getCodePoint(null, 1));
|
||||||
|
}
|
||||||
|
if (isMissed(object)) {
|
||||||
|
throw new ValidationException("Not nullable object is missed at " + Lc.getCodePoint(null, 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Throws exception if object is missed.
|
||||||
|
*
|
||||||
|
* @param object Value of field to check;
|
||||||
|
* @throws ValidationException Exception of validation.
|
||||||
|
*/
|
||||||
|
protected static void validateNotMissed(@Nullable final Object object) throws ValidationException {
|
||||||
|
if (isMissed(object)) {
|
||||||
|
throw new ValidationException("Object missed at " + Lc.getCodePoint(null, 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Throws exception if object is null.
|
||||||
|
*
|
||||||
|
* @param object Value of field to check;
|
||||||
|
* @throws ValidationException Exception of validation.
|
||||||
|
*/
|
||||||
|
protected static void validateMissedOrNotNull(@Nullable final Object object) throws ValidationException {
|
||||||
|
if (isNull(object)) {
|
||||||
|
throw new ValidationException("Not null or not missed object is null at " + Lc.getCodePoint(null, 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected GoogleJsonModel() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,149 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2016 Touch Instinct
|
||||||
|
*
|
||||||
|
* This file is part of RoboSwag library.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ru.touchin.templates.googlejson;
|
||||||
|
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
|
||||||
|
import com.google.api.client.http.json.JsonHttpContent;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import ru.touchin.roboswag.components.utils.storables.PreferenceStore;
|
||||||
|
import ru.touchin.roboswag.core.observables.storable.Converter;
|
||||||
|
import ru.touchin.roboswag.core.observables.storable.Storable;
|
||||||
|
import ru.touchin.roboswag.core.observables.storable.concrete.NonNullStorable;
|
||||||
|
import ru.touchin.roboswag.core.utils.ShouldNotHappenException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Gavriil Sitnikov on 23/08/2016.
|
||||||
|
* Utility class to get {@link Storable} that is storing Google Json generated object into preferences.
|
||||||
|
*/
|
||||||
|
public final class GoogleJsonPreferences {
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static <T> Storable<String, T, String> jsonStorable(@NonNull final String name,
|
||||||
|
@NonNull final Class<T> jsonClass,
|
||||||
|
@NonNull final SharedPreferences preferences) {
|
||||||
|
return new Storable.Builder<String, T, String>(name, jsonClass, String.class, new PreferenceStore<>(preferences), new JsonConverter<>())
|
||||||
|
.setObserveStrategy(Storable.ObserveStrategy.CACHE_ACTUAL_VALUE)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static <T> NonNullStorable<String, T, String> jsonStorable(@NonNull final String name,
|
||||||
|
@NonNull final Class<T> jsonClass,
|
||||||
|
@NonNull final SharedPreferences preferences,
|
||||||
|
@NonNull final T defaultValue) {
|
||||||
|
return new Storable.Builder<String, T, String>(name, jsonClass, String.class, new PreferenceStore<>(preferences), new JsonConverter<>())
|
||||||
|
.setObserveStrategy(Storable.ObserveStrategy.CACHE_ACTUAL_VALUE)
|
||||||
|
.setDefaultValue(defaultValue)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static <T> Storable<String, List<T>, String> jsonListStorable(@NonNull final String name,
|
||||||
|
@NonNull final Class<T> jsonListItemClass,
|
||||||
|
@NonNull final SharedPreferences preferences) {
|
||||||
|
return new Storable.Builder<>(name, List.class, String.class, new PreferenceStore<>(preferences), new JsonListConverter<>(jsonListItemClass))
|
||||||
|
.setObserveStrategy(Storable.ObserveStrategy.CACHE_ACTUAL_VALUE)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static <T> NonNullStorable<String, List<T>, String> jsonListStorable(@NonNull final String name,
|
||||||
|
@NonNull final Class<T> jsonListItemClass,
|
||||||
|
@NonNull final SharedPreferences preferences,
|
||||||
|
@NonNull final List<T> defaultValue) {
|
||||||
|
return new Storable.Builder<>(name, List.class, String.class, new PreferenceStore<>(preferences), new JsonListConverter<>(jsonListItemClass))
|
||||||
|
.setObserveStrategy(Storable.ObserveStrategy.CACHE_ACTUAL_VALUE)
|
||||||
|
.setDefaultValue(defaultValue)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private GoogleJsonPreferences() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class JsonConverter<TJsonObject> implements Converter<TJsonObject, String> {
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public String toStoreObject(@NonNull final Type jsonObjectType, @NonNull final Type stringType,
|
||||||
|
@Nullable final TJsonObject object) {
|
||||||
|
if (object == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final JsonHttpContent content = new JsonHttpContent(GoogleJsonModel.DEFAULT_JSON_FACTORY, object);
|
||||||
|
final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
||||||
|
try {
|
||||||
|
content.writeTo(byteArrayOutputStream);
|
||||||
|
} catch (final IOException exception) {
|
||||||
|
throw new ShouldNotHappenException(exception);
|
||||||
|
}
|
||||||
|
return new String(byteArrayOutputStream.toByteArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public TJsonObject toObject(@NonNull final Type jsonObjectType, @NonNull final Type stringType, @Nullable final String storeValue) {
|
||||||
|
if (storeValue == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return (TJsonObject) GoogleJsonModel.DEFAULT_JSON_FACTORY.createJsonParser(storeValue).parse(jsonObjectType, true);
|
||||||
|
} catch (final IOException exception) {
|
||||||
|
throw new ShouldNotHappenException(exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class JsonListConverter<T> extends JsonConverter<List<T>> {
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private final Class<T> itemClass;
|
||||||
|
|
||||||
|
public JsonListConverter(@NonNull final Class<T> itemClass) {
|
||||||
|
super();
|
||||||
|
this.itemClass = itemClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public List<T> toObject(@NonNull final Type jsonObjectType, @NonNull final Type stringType, @Nullable final String storeValue) {
|
||||||
|
if (storeValue == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return new ArrayList<>(GoogleJsonModel.DEFAULT_JSON_FACTORY.createJsonParser(storeValue).parseArray(ArrayList.class, itemClass));
|
||||||
|
} catch (final IOException exception) {
|
||||||
|
throw new ShouldNotHappenException(exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,87 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2015 RoboSwag (Gavriil Sitnikov, Vsevolod Ivanov)
|
||||||
|
*
|
||||||
|
* This file is part of RoboSwag library.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ru.touchin.templates.googlejson;
|
||||||
|
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
|
||||||
|
import okhttp3.Request;
|
||||||
|
import okhttp3.RequestBody;
|
||||||
|
import ru.touchin.roboswag.core.log.Lc;
|
||||||
|
import ru.touchin.templates.requests.HttpRequest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Gavriil Sitnikov on 07/14.
|
||||||
|
* Request that responses data in Google JSON format
|
||||||
|
*/
|
||||||
|
public abstract class GoogleJsonRequest<T> extends HttpRequest<T> {
|
||||||
|
|
||||||
|
protected GoogleJsonRequest(@NonNull final Class<T> responseResultType) {
|
||||||
|
super(responseResultType);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
protected T parse(@NonNull final Class<T> responseResultType, @NonNull final Charset charset, @NonNull final InputStream inputStream)
|
||||||
|
throws IOException {
|
||||||
|
return GoogleJsonModel.DEFAULT_JSON_FACTORY.createJsonObjectParser().parseAndClose(inputStream, charset, responseResultType);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
protected Request.Builder createHttpRequest() throws IOException {
|
||||||
|
switch (getRequestType()) {
|
||||||
|
case POST:
|
||||||
|
if (getBody() == null) {
|
||||||
|
Lc.assertion("Do you forget to implement getBody() class during POST-request?");
|
||||||
|
return super.createHttpRequest().get();
|
||||||
|
}
|
||||||
|
return super.createHttpRequest().post(getBody());
|
||||||
|
case GET:
|
||||||
|
return super.createHttpRequest().get();
|
||||||
|
default:
|
||||||
|
Lc.assertion("Unknown request type " + getRequestType());
|
||||||
|
return super.createHttpRequest().get();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type of request. Basically GET or POST.
|
||||||
|
*
|
||||||
|
* @return Request type.
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
protected abstract RequestType getRequestType();
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
protected RequestBody getBody() throws IOException {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected enum RequestType {
|
||||||
|
GET,
|
||||||
|
POST
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,54 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2016 Touch Instinct
|
||||||
|
*
|
||||||
|
* This file is part of RoboSwag library.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ru.touchin.templates.googlejson;
|
||||||
|
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import ru.touchin.templates.socket.SocketEvent;
|
||||||
|
import ru.touchin.templates.socket.SocketMessageHandler;
|
||||||
|
import ru.touchin.templates.ApiModel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Gavriil Sitnikov on 01/09/2016.
|
||||||
|
* Socket event that response JSON objects and could be parsed by Google Json lib.
|
||||||
|
*
|
||||||
|
* @param <TMessage> Type of message.
|
||||||
|
*/
|
||||||
|
public class GoogleJsonSocketEvent<TMessage> extends SocketEvent<TMessage> {
|
||||||
|
|
||||||
|
public GoogleJsonSocketEvent(@NonNull final String name, @NonNull final Class<TMessage> clz,
|
||||||
|
@Nullable final SocketMessageHandler<TMessage> eventDataHandler) {
|
||||||
|
super(name, clz, eventDataHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public TMessage parse(@NonNull final byte[] data) throws IOException {
|
||||||
|
final TMessage message = GoogleJsonModel.DEFAULT_JSON_FACTORY.createJsonParser(new String(data, "UTF-8")).parseAndClose(getMessageClass());
|
||||||
|
if (message instanceof ApiModel) {
|
||||||
|
((ApiModel) message).validate();
|
||||||
|
}
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -42,7 +42,7 @@ public class LoganSquareJodaTimeConverter implements TypeConverter<DateTime> {
|
||||||
@Override
|
@Override
|
||||||
public DateTime parse(@NonNull final JsonParser jsonParser) throws IOException {
|
public DateTime parse(@NonNull final JsonParser jsonParser) throws IOException {
|
||||||
final String dateString = jsonParser.getValueAsString();
|
final String dateString = jsonParser.getValueAsString();
|
||||||
if (dateString == null) {
|
if (dateString == null || dateString.isEmpty()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
|
@ -60,9 +60,9 @@ public class LoganSquareJodaTimeConverter implements TypeConverter<DateTime> {
|
||||||
@NonNull final JsonGenerator jsonGenerator)
|
@NonNull final JsonGenerator jsonGenerator)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
if (fieldName != null) {
|
if (fieldName != null) {
|
||||||
jsonGenerator.writeStringField(fieldName, object != null ? object.toString() : null);
|
jsonGenerator.writeStringField(fieldName, object != null && !object.toString().isEmpty() ? object.toString() : null);
|
||||||
} else {
|
} else {
|
||||||
jsonGenerator.writeString(object != null ? object.toString() : null);
|
jsonGenerator.writeString(object != null && !object.toString().isEmpty() ? object.toString() : null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,12 +20,15 @@
|
||||||
package ru.touchin.templates.logansquare;
|
package ru.touchin.templates.logansquare;
|
||||||
|
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
|
||||||
import com.bluelinelabs.logansquare.ConverterUtils;
|
import com.bluelinelabs.logansquare.ConverterUtils;
|
||||||
import com.bluelinelabs.logansquare.LoganSquare;
|
import com.bluelinelabs.logansquare.LoganSquare;
|
||||||
|
import com.fasterxml.jackson.core.JsonGenerator;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.StringWriter;
|
||||||
import java.lang.annotation.Annotation;
|
import java.lang.annotation.Annotation;
|
||||||
import java.lang.reflect.ParameterizedType;
|
import java.lang.reflect.ParameterizedType;
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
|
|
@ -62,6 +65,16 @@ public class LoganSquareJsonFactory extends Converter.Factory {
|
||||||
return new LoganSquareRequestBodyConverter<>();
|
return new LoganSquareRequestBodyConverter<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public Converter<?, String> stringConverter(@NonNull final Type type, @NonNull final Annotation[] annotations, @NonNull final Retrofit retrofit) {
|
||||||
|
if (type instanceof Class && ((Class) type).getSuperclass() == Enum.class) {
|
||||||
|
return new LoganSquareStringEnumConverter<>();
|
||||||
|
} else {
|
||||||
|
return super.stringConverter(type, annotations, retrofit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static class LoganSquareJsonResponseBodyConverter<T> extends JsonResponseBodyConverter<T> {
|
public static class LoganSquareJsonResponseBodyConverter<T> extends JsonResponseBodyConverter<T> {
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
|
|
@ -100,11 +113,29 @@ public class LoganSquareJsonFactory extends Converter.Factory {
|
||||||
public static class LoganSquareRequestBodyConverter<T> extends JsonRequestBodyConverter<T> {
|
public static class LoganSquareRequestBodyConverter<T> extends JsonRequestBodyConverter<T> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void writeValueToByteArray(@NonNull final T value, @NonNull final ByteArrayOutputStream byteArrayOutputStream)
|
protected void writeValueToByteArray(@NonNull final T value, @NonNull final ByteArrayOutputStream byteArrayOutputStream) throws IOException {
|
||||||
throws IOException {
|
|
||||||
LoganSquare.serialize(value, byteArrayOutputStream);
|
LoganSquare.serialize(value, byteArrayOutputStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
public static class LoganSquareStringEnumConverter<T> implements Converter<T, String> {
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@SuppressWarnings({"unchecked", "TryFinallyCanBeTryWithResources"})
|
||||||
|
@Override
|
||||||
|
public String convert(@NonNull final T value) throws IOException {
|
||||||
|
final StringWriter writer = new StringWriter();
|
||||||
|
try {
|
||||||
|
final JsonGenerator generator = LoganSquare.JSON_FACTORY.createGenerator(writer);
|
||||||
|
LoganSquare.typeConverterFor((Class<T>) value.getClass()).serialize(value, null, false, generator);
|
||||||
|
generator.close();
|
||||||
|
return writer.toString().replaceAll("\"", "");
|
||||||
|
} finally {
|
||||||
|
writer.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ import java.util.List;
|
||||||
import ru.touchin.roboswag.components.utils.storables.PreferenceStore;
|
import ru.touchin.roboswag.components.utils.storables.PreferenceStore;
|
||||||
import ru.touchin.roboswag.core.observables.storable.Converter;
|
import ru.touchin.roboswag.core.observables.storable.Converter;
|
||||||
import ru.touchin.roboswag.core.observables.storable.Storable;
|
import ru.touchin.roboswag.core.observables.storable.Storable;
|
||||||
import ru.touchin.roboswag.core.observables.storable.NonNullStorable;
|
import ru.touchin.roboswag.core.observables.storable.concrete.NonNullStorable;
|
||||||
import ru.touchin.roboswag.core.utils.ShouldNotHappenException;
|
import ru.touchin.roboswag.core.utils.ShouldNotHappenException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -28,8 +28,6 @@ import java.nio.charset.Charset;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import io.reactivex.Observable;
|
|
||||||
import io.reactivex.schedulers.Schedulers;
|
|
||||||
import okhttp3.Call;
|
import okhttp3.Call;
|
||||||
import okhttp3.Headers;
|
import okhttp3.Headers;
|
||||||
import okhttp3.HttpUrl;
|
import okhttp3.HttpUrl;
|
||||||
|
|
@ -42,6 +40,8 @@ import okhttp3.ResponseBody;
|
||||||
import okio.Buffer;
|
import okio.Buffer;
|
||||||
import ru.touchin.roboswag.core.log.Lc;
|
import ru.touchin.roboswag.core.log.Lc;
|
||||||
import ru.touchin.roboswag.core.log.LcLevel;
|
import ru.touchin.roboswag.core.log.LcLevel;
|
||||||
|
import rx.Observable;
|
||||||
|
import rx.schedulers.Schedulers;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by Gavriil Sitnikov on 13/11/2015.
|
* Created by Gavriil Sitnikov on 13/11/2015.
|
||||||
|
|
@ -208,7 +208,7 @@ public abstract class HttpRequest<T> {
|
||||||
.fromCallable(() -> executeSyncInternal(requestController))
|
.fromCallable(() -> executeSyncInternal(requestController))
|
||||||
.subscribeOn(Schedulers.io())
|
.subscribeOn(Schedulers.io())
|
||||||
.unsubscribeOn(Schedulers.io())
|
.unsubscribeOn(Schedulers.io())
|
||||||
.doOnDispose(requestController.call::cancel));
|
.doOnUnsubscribe(requestController.call::cancel));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -29,14 +29,14 @@ import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
|
|
||||||
import io.reactivex.Observable;
|
|
||||||
import io.reactivex.Scheduler;
|
|
||||||
import io.reactivex.functions.Consumer;
|
|
||||||
import io.reactivex.schedulers.Schedulers;
|
|
||||||
import io.socket.client.Socket;
|
import io.socket.client.Socket;
|
||||||
import io.socket.emitter.Emitter;
|
import io.socket.emitter.Emitter;
|
||||||
import ru.touchin.roboswag.core.log.Lc;
|
import ru.touchin.roboswag.core.log.Lc;
|
||||||
import ru.touchin.templates.ApiModel;
|
import ru.touchin.templates.ApiModel;
|
||||||
|
import rx.Observable;
|
||||||
|
import rx.Scheduler;
|
||||||
|
import rx.functions.Action1;
|
||||||
|
import rx.schedulers.Schedulers;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by Gavriil Sitnikov on 29/02/16.
|
* Created by Gavriil Sitnikov on 29/02/16.
|
||||||
|
|
@ -99,14 +99,14 @@ public abstract class SocketConnection {
|
||||||
socket.on(Socket.EVENT_RECONNECT_ERROR, args -> emitter.onNext(new Pair<>(socket, State.CONNECTION_ERROR)));
|
socket.on(Socket.EVENT_RECONNECT_ERROR, args -> emitter.onNext(new Pair<>(socket, State.CONNECTION_ERROR)));
|
||||||
socket.on(Socket.EVENT_RECONNECT_FAILED, args -> emitter.onNext(new Pair<>(socket, State.CONNECTION_ERROR)));
|
socket.on(Socket.EVENT_RECONNECT_FAILED, args -> emitter.onNext(new Pair<>(socket, State.CONNECTION_ERROR)));
|
||||||
emitter.onNext(new Pair<>(socket, State.DISCONNECTED));
|
emitter.onNext(new Pair<>(socket, State.DISCONNECTED));
|
||||||
})
|
}, rx.Emitter.BackpressureMode.LATEST)
|
||||||
.distinctUntilChanged()
|
.distinctUntilChanged()
|
||||||
.doOnSubscribe(disposable -> {
|
.doOnSubscribe(() -> {
|
||||||
if (autoConnectOnAnySubscription) {
|
if (autoConnectOnAnySubscription) {
|
||||||
socket.connect();
|
socket.connect();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.doOnDispose(() -> {
|
.doOnUnsubscribe(() -> {
|
||||||
if (autoConnectOnAnySubscription) {
|
if (autoConnectOnAnySubscription) {
|
||||||
socket.disconnect();
|
socket.disconnect();
|
||||||
}
|
}
|
||||||
|
|
@ -136,9 +136,10 @@ public abstract class SocketConnection {
|
||||||
if (result == null) {
|
if (result == null) {
|
||||||
result = getSocket()
|
result = getSocket()
|
||||||
.switchMap(socket -> Observable
|
.switchMap(socket -> Observable
|
||||||
.<T>create(emitter -> socket.on(socketEvent.getName(), new SocketListener<>(socketEvent, emitter::onNext)))
|
.<T>create(emitter -> socket.on(socketEvent.getName(), new SocketListener<>(socketEvent, emitter::onNext)),
|
||||||
|
rx.Emitter.BackpressureMode.BUFFER)
|
||||||
.unsubscribeOn(scheduler)
|
.unsubscribeOn(scheduler)
|
||||||
.doOnDispose(() -> {
|
.doOnUnsubscribe(() -> {
|
||||||
socket.off(socketEvent.getName());
|
socket.off(socketEvent.getName());
|
||||||
messagesObservableCache.remove(socketEvent);
|
messagesObservableCache.remove(socketEvent);
|
||||||
}))
|
}))
|
||||||
|
|
@ -171,9 +172,9 @@ public abstract class SocketConnection {
|
||||||
@NonNull
|
@NonNull
|
||||||
private final SocketEvent<TMessage> socketEvent;
|
private final SocketEvent<TMessage> socketEvent;
|
||||||
@NonNull
|
@NonNull
|
||||||
private final Consumer<TMessage> onMessageAction;
|
private final Action1<TMessage> onMessageAction;
|
||||||
|
|
||||||
public SocketListener(@NonNull final SocketEvent<TMessage> socketEvent, @NonNull final Consumer<TMessage> onMessageAction) {
|
public SocketListener(@NonNull final SocketEvent<TMessage> socketEvent, @NonNull final Action1<TMessage> onMessageAction) {
|
||||||
this.socketEvent = socketEvent;
|
this.socketEvent = socketEvent;
|
||||||
this.onMessageAction = onMessageAction;
|
this.onMessageAction = onMessageAction;
|
||||||
}
|
}
|
||||||
|
|
@ -189,7 +190,7 @@ public abstract class SocketConnection {
|
||||||
if (socketEvent.getEventDataHandler() != null) {
|
if (socketEvent.getEventDataHandler() != null) {
|
||||||
socketEvent.getEventDataHandler().handleMessage(message);
|
socketEvent.getEventDataHandler().handleMessage(message);
|
||||||
}
|
}
|
||||||
onMessageAction.accept(message);
|
onMessageAction.call(message);
|
||||||
} catch (final RuntimeException throwable) {
|
} catch (final RuntimeException throwable) {
|
||||||
Lc.assertion(throwable);
|
Lc.assertion(throwable);
|
||||||
} catch (final JsonProcessingException exception) {
|
} catch (final JsonProcessingException exception) {
|
||||||
|
|
|
||||||
|
|
@ -4,13 +4,11 @@ import android.support.annotation.NonNull;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
import io.reactivex.functions.Function;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by Ilia Kurtov on 30/01/2017.
|
* Created by Ilia Kurtov on 30/01/2017.
|
||||||
* Simple interface that gets one parameter with {@link TInput} type as input and returns other type {@link TReturn} as a result.
|
* Simple interface that gets one parameter with {@link TInput} type as input and returns other type {@link TReturn} as a result.
|
||||||
* Interface extends {@link Serializable} to survive after {@link ru.touchin.roboswag.components.navigation.AbstractState} recreation.
|
* Interface extends {@link Serializable} to survive after {@link ru.touchin.roboswag.components.navigation.AbstractState} recreation.
|
||||||
* Created as a replace for {@link Function} because it needed to be {@link Serializable}
|
* Created as a replace for {@link rx.functions.Func1} because it needed to be {@link Serializable}
|
||||||
*
|
*
|
||||||
* @param <TInput> input type.
|
* @param <TInput> input type.
|
||||||
* @param <TReturn> return type.
|
* @param <TReturn> return type.
|
||||||
|
|
|
||||||
|
|
@ -21,9 +21,9 @@ package ru.touchin.templates.validation.validationcontrollers;
|
||||||
|
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
|
|
||||||
import io.reactivex.Observable;
|
|
||||||
import ru.touchin.templates.validation.ValidationState;
|
import ru.touchin.templates.validation.ValidationState;
|
||||||
import ru.touchin.templates.validation.validators.SameTypeValidator;
|
import ru.touchin.templates.validation.validators.SameTypeValidator;
|
||||||
|
import rx.Observable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by Ilia Kurtov on 24/01/2017.
|
* Created by Ilia Kurtov on 24/01/2017.
|
||||||
|
|
@ -46,7 +46,7 @@ public class BooleanValidationController extends ValidationController<Boolean, B
|
||||||
public Observable<?> validation(@NonNull final Observable<Boolean> activatedObservable) {
|
public Observable<?> validation(@NonNull final Observable<Boolean> activatedObservable) {
|
||||||
return Observable.combineLatest(activatedObservable, getValidator().getWrapperModel().observe(),
|
return Observable.combineLatest(activatedObservable, getValidator().getWrapperModel().observe(),
|
||||||
(activated, flag) -> {
|
(activated, flag) -> {
|
||||||
final boolean selected = flag.get() == null ? false : flag.get();
|
final boolean selected = flag == null ? false : flag;
|
||||||
if (activated && !selected) {
|
if (activated && !selected) {
|
||||||
return ValidationState.ERROR_NO_DESCRIPTION;
|
return ValidationState.ERROR_NO_DESCRIPTION;
|
||||||
} else if (!activated && !selected) {
|
} else if (!activated && !selected) {
|
||||||
|
|
|
||||||
|
|
@ -25,11 +25,10 @@ import android.text.TextUtils;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
import io.reactivex.Observable;
|
|
||||||
import ru.touchin.roboswag.core.utils.Optional;
|
|
||||||
import ru.touchin.roboswag.core.utils.pairs.NonNullPair;
|
import ru.touchin.roboswag.core.utils.pairs.NonNullPair;
|
||||||
import ru.touchin.templates.validation.ValidationState;
|
import ru.touchin.templates.validation.ValidationState;
|
||||||
import ru.touchin.templates.validation.validators.EditTextValidator;
|
import ru.touchin.templates.validation.validators.EditTextValidator;
|
||||||
|
import rx.Observable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by Ilia Kurtov on 24/01/2017.
|
* Created by Ilia Kurtov on 24/01/2017.
|
||||||
|
|
@ -76,17 +75,17 @@ public class EditTextValidationController<TModel extends Serializable>
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
|
@SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.NPathComplexity"})
|
||||||
private NonNullPair<Boolean, Observable<ValidationState>> getValidationPair(final boolean activated,
|
private NonNullPair<Boolean, Observable<ValidationState>> getValidationPair(final boolean activated,
|
||||||
@NonNull final Optional<String> optionalText,
|
@Nullable final String text,
|
||||||
@Nullable final Boolean focusIn,
|
@Nullable final Boolean focusIn,
|
||||||
final boolean showError) {
|
final boolean showError) {
|
||||||
final String text = optionalText.get();
|
|
||||||
if (focusIn == null && TextUtils.isEmpty(text) && !activated && !showError) {
|
if (focusIn == null && TextUtils.isEmpty(text) && !activated && !showError) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
final boolean focus = focusIn != null && focusIn;
|
final boolean focus = focusIn == null ? false : focusIn;
|
||||||
if (TextUtils.isEmpty(text)) {
|
if (TextUtils.isEmpty(text)) {
|
||||||
return new NonNullPair<>(focus, activated || showError
|
return new NonNullPair<>(focus, (activated || showError)
|
||||||
? getValidator().getValidationStateWhenEmpty().observe()
|
? getValidator().getValidationStateWhenEmpty().observe()
|
||||||
: Observable.just(ValidationState.INITIAL));
|
: Observable.just(ValidationState.INITIAL));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,9 @@ import android.support.annotation.NonNull;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
import io.reactivex.Observable;
|
|
||||||
import ru.touchin.templates.validation.ValidationState;
|
import ru.touchin.templates.validation.ValidationState;
|
||||||
import ru.touchin.templates.validation.validators.Validator;
|
import ru.touchin.templates.validation.validators.Validator;
|
||||||
|
import rx.Observable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by Ilia Kurtov on 24/01/2017.
|
* Created by Ilia Kurtov on 24/01/2017.
|
||||||
|
|
|
||||||
|
|
@ -24,13 +24,12 @@ import android.support.annotation.Nullable;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
import io.reactivex.Observable;
|
|
||||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
|
||||||
import io.reactivex.functions.Consumer;
|
|
||||||
import ru.touchin.roboswag.core.utils.Optional;
|
|
||||||
import ru.touchin.templates.validation.ValidationState;
|
import ru.touchin.templates.validation.ValidationState;
|
||||||
import ru.touchin.templates.validation.ViewWithError;
|
import ru.touchin.templates.validation.ViewWithError;
|
||||||
import ru.touchin.templates.validation.validators.Validator;
|
import ru.touchin.templates.validation.validators.Validator;
|
||||||
|
import rx.Observable;
|
||||||
|
import rx.android.schedulers.AndroidSchedulers;
|
||||||
|
import rx.functions.Action1;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by Ilia Kurtov on 24/01/2017.
|
* Created by Ilia Kurtov on 24/01/2017.
|
||||||
|
|
@ -63,9 +62,8 @@ public class ValidationController
|
||||||
*/
|
*/
|
||||||
@NonNull
|
@NonNull
|
||||||
public Observable<?> modelAndViewUpdating(@Nullable final Observable<TWrapperModel> viewStateObservable,
|
public Observable<?> modelAndViewUpdating(@Nullable final Observable<TWrapperModel> viewStateObservable,
|
||||||
@NonNull final Consumer<Optional<TWrapperModel>> updateViewAction,
|
@NonNull final Action1<TWrapperModel> updateViewAction,
|
||||||
@NonNull final ViewWithError viewWithError) {
|
@NonNull final ViewWithError viewWithError) {
|
||||||
|
|
||||||
final Observable<?> stateObservable = viewStateObservable != null
|
final Observable<?> stateObservable = viewStateObservable != null
|
||||||
? viewStateObservable.doOnNext(flag -> getValidator().getWrapperModel().set(flag))
|
? viewStateObservable.doOnNext(flag -> getValidator().getWrapperModel().set(flag))
|
||||||
: Observable.empty();
|
: Observable.empty();
|
||||||
|
|
|
||||||
|
|
@ -24,13 +24,13 @@ import android.support.annotation.Nullable;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
import io.reactivex.Observable;
|
|
||||||
import io.reactivex.schedulers.Schedulers;
|
|
||||||
import ru.touchin.roboswag.core.observables.Changeable;
|
import ru.touchin.roboswag.core.observables.Changeable;
|
||||||
import ru.touchin.roboswag.core.observables.NonNullChangeable;
|
import ru.touchin.roboswag.core.observables.NonNullChangeable;
|
||||||
import ru.touchin.roboswag.core.utils.pairs.HalfNullablePair;
|
import ru.touchin.roboswag.core.utils.pairs.HalfNullablePair;
|
||||||
import ru.touchin.templates.validation.ValidationFunc;
|
import ru.touchin.templates.validation.ValidationFunc;
|
||||||
import ru.touchin.templates.validation.ValidationState;
|
import ru.touchin.templates.validation.ValidationState;
|
||||||
|
import rx.Observable;
|
||||||
|
import rx.schedulers.Schedulers;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by Ilia Kurtov on 24/01/2017.
|
* Created by Ilia Kurtov on 24/01/2017.
|
||||||
|
|
@ -107,13 +107,19 @@ public abstract class EditTextValidator<TModel extends Serializable> extends Val
|
||||||
primaryCheck.observe().observeOn(Schedulers.computation()),
|
primaryCheck.observe().observeOn(Schedulers.computation()),
|
||||||
(finalCheck, primaryCheck) -> {
|
(finalCheck, primaryCheck) -> {
|
||||||
try {
|
try {
|
||||||
return validateText(finalCheck.get(), primaryCheck.get(), text, fullCheck);
|
return validateText(finalCheck, primaryCheck, text, fullCheck);
|
||||||
} catch (final Throwable exception) {
|
} catch (final Throwable exception) {
|
||||||
return new HalfNullablePair<>(ValidationState.ERROR_CONVERSION, null);
|
return new HalfNullablePair<>(ValidationState.ERROR_CONVERSION, null);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private Observable<ValidationState> processChecks(@NonNull final String text, final boolean fullCheck) {
|
||||||
|
return createValidationObservable(text, fullCheck)
|
||||||
|
.map(HalfNullablePair::getFirst);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validates text with primary check.
|
* Validates text with primary check.
|
||||||
*
|
*
|
||||||
|
|
@ -147,13 +153,8 @@ public abstract class EditTextValidator<TModel extends Serializable> extends Val
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public Observable<HalfNullablePair<ValidationState, TModel>> fullValidateAndGetModel(@NonNull final String text) {
|
public Observable<HalfNullablePair<ValidationState, TModel>> fullValidateAndGetModel(@NonNull final String text) {
|
||||||
return createValidationObservable(text, true);
|
return createValidationObservable(text, true)
|
||||||
}
|
.first();
|
||||||
|
|
||||||
@NonNull
|
|
||||||
private Observable<ValidationState> processChecks(@NonNull final String text, final boolean fullCheck) {
|
|
||||||
return createValidationObservable(text, fullCheck)
|
|
||||||
.map(HalfNullablePair::getFirst);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -5,9 +5,9 @@ import android.support.annotation.NonNull;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
import io.reactivex.Observable;
|
|
||||||
import ru.touchin.roboswag.core.utils.pairs.HalfNullablePair;
|
import ru.touchin.roboswag.core.utils.pairs.HalfNullablePair;
|
||||||
import ru.touchin.templates.validation.ValidationState;
|
import ru.touchin.templates.validation.ValidationState;
|
||||||
|
import rx.Observable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by Ilia Kurtov on 24/01/2017.
|
* Created by Ilia Kurtov on 24/01/2017.
|
||||||
|
|
|
||||||
|
|
@ -23,12 +23,11 @@ import android.support.annotation.NonNull;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
import io.reactivex.Observable;
|
|
||||||
import io.reactivex.Single;
|
|
||||||
import ru.touchin.roboswag.core.observables.Changeable;
|
import ru.touchin.roboswag.core.observables.Changeable;
|
||||||
import ru.touchin.roboswag.core.observables.NonNullChangeable;
|
import ru.touchin.roboswag.core.observables.NonNullChangeable;
|
||||||
import ru.touchin.roboswag.core.utils.pairs.HalfNullablePair;
|
import ru.touchin.roboswag.core.utils.pairs.HalfNullablePair;
|
||||||
import ru.touchin.templates.validation.ValidationState;
|
import ru.touchin.templates.validation.ValidationState;
|
||||||
|
import rx.Observable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by Ilia Kurtov on 24/01/2017.
|
* Created by Ilia Kurtov on 24/01/2017.
|
||||||
|
|
@ -95,7 +94,7 @@ public abstract class Validator<TWrapperModel extends Serializable, TModel exten
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validates {@link TWrapperModel} and returns {@link Single} with {@link HalfNullablePair} of final state and resulting model.
|
* Validates {@link TWrapperModel} and returns {@link Observable} with {@link HalfNullablePair} of final state and resulting model.
|
||||||
*
|
*
|
||||||
* @param wrapperModel - not null value that should be validated.
|
* @param wrapperModel - not null value that should be validated.
|
||||||
* @return pair with final {@link ValidationState} that is always not null and a model that we get after converting the {@link TWrapperModel}.
|
* @return pair with final {@link ValidationState} that is always not null and a model that we get after converting the {@link TWrapperModel}.
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue