Unverified Commit 5bbd3f7f authored by Rafael Kellermann Streit's avatar Rafael Kellermann Streit Committed by GitHub

Merge pull request #560 from RocketChat/develop

[RELEASE] Merge develop into master
parents 1f3713b3 92ee59e8
...@@ -19,4 +19,4 @@ git clone https://github.com/RocketChat/Rocket.Chat.Android ...@@ -19,4 +19,4 @@ git clone https://github.com/RocketChat/Rocket.Chat.Android
### Code style guide ### Code style guide
Before submitting a PR you should follow our [Coding Style](https://github.com/RocketChat/Rocket.Chat.Android/blob/develop/CODING_STYLE.md). Before submitting a PR you should follow our [Coding Style](https://github.com/RocketChat/java-code-styles/blob/master/CODING_STYLE.md).
apply plugin: 'com.android.library' apply plugin: 'com.android.library'
apply plugin: 'me.tatarka.retrolambda'
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.3.3'
classpath 'me.tatarka:gradle-retrolambda:3.6.1'
classpath 'me.tatarka.retrolambda.projectlombok:lombok.ast:0.2.3.a2'
}
}
android { android {
compileSdkVersion rootProject.ext.compileSdkVersion compileSdkVersion rootProject.ext.compileSdkVersion
buildToolsVersion rootProject.ext.buildToolsVersion buildToolsVersion rootProject.ext.buildToolsVersion
......
# -*- coding:utf-8 -*-
a='''
@Override
public void onOpen(WebSocket webSocket, Response response) {
}
@Override
public void onFailure(IOException e, Response response) {
}
@Override
public void onMessage(ResponseBody responseBody) throws IOException {
}
@Override
public void onPong(Buffer payload) {
}
@Override
public void onClose(int code, String reason) {
}
'''.strip().split('@Override')
for m in a[1:]:
m= " @Override\n "+m.strip()
mn = m.split("\n")[1].strip().split(" ")[2].split("(")[0]
if mn.startswith("on"):
d=dict()
d["classname"]=mn[2:]
params = [p for p in " ".join(m.split("\n")[1].strip()[:-1].split(" throws ")[0].split(" ")[2:]).strip()[len(mn)+1:-1].split(", ") if p.split(" ")[0]!="WebSocket"]
d["params"]="".join([", "+p for p in params])
paramnames = [p.split(" ")[-1] for p in params]
d["paramdefs"]="\n".join([" public "+p+";" for p in params])
d["thisis"]="\n".join([" this.{param} = {param};".format(param=p) for p in paramnames])
# print '''
# public static class {classname} extends Base {{
# {paramdefs}
# public {classname}(WebSocket websocket{params}) {{
# super("{classname}", websocket);
# {thisis}
# }}
# }}'''.format(**d)
######################
x=m.split("\n")
x[2]=''' mSubscriber.onNext(new RxWebSocketCallback.{classname}(mWebSocket, {params}));'''.format(classname=mn[2:],params=", ".join(paramnames))
print "\n".join(x)
package chat.rocket.android_ddp; package chat.rocket.android_ddp;
import io.reactivex.Flowable; import android.text.TextUtils;
import io.reactivex.Maybe;
import io.reactivex.annotations.Nullable;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONException;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicReference;
import bolts.Task; import bolts.Task;
import bolts.TaskCompletionSource; import bolts.TaskCompletionSource;
import chat.rocket.android.log.RCLog;
import chat.rocket.android_ddp.rx.RxWebSocketCallback; import chat.rocket.android_ddp.rx.RxWebSocketCallback;
import io.reactivex.Flowable;
import io.reactivex.Maybe;
import io.reactivex.annotations.NonNull;
import io.reactivex.annotations.Nullable;
import okhttp3.OkHttpClient; import okhttp3.OkHttpClient;
public class DDPClient { public class DDPClient {
// reference: https://github.com/eddflrs/meteor-ddp/blob/master/meteor-ddp.js // reference: https://github.com/eddflrs/meteor-ddp/blob/master/meteor-ddp.js
public static final int REASON_CLOSED_BY_USER = 1000;
public static final int REASON_NETWORK_ERROR = 1001;
private static volatile DDPClient singleton;
private static volatile OkHttpClient client;
private final DDPClientImpl impl; private final DDPClientImpl impl;
private final AtomicReference<String> hostname = new AtomicReference<>();
public DDPClient(OkHttpClient client) { public static void initialize(OkHttpClient okHttpClient) {
impl = new DDPClientImpl(this, client); client = okHttpClient;
} }
public Task<DDPClientCallback.Connect> connect(String url) { public static DDPClient get() {
return connect(url, null); DDPClient result = singleton;
if (result == null) {
synchronized (DDPClient.class) {
result = singleton;
if (result == null) {
singleton = result = new DDPClient(client);
}
}
}
return result;
} }
public Task<DDPClientCallback.Connect> connect(String url, String session) { private DDPClient(OkHttpClient client) {
impl = new DDPClientImpl(this, client);
}
private Task<DDPClientCallback.Connect> connect(String url, String session) {
hostname.set(url);
TaskCompletionSource<DDPClientCallback.Connect> task = new TaskCompletionSource<>(); TaskCompletionSource<DDPClientCallback.Connect> task = new TaskCompletionSource<>();
impl.connect(task, url, session); impl.connect(task, url, session);
return task.getTask(); return task.getTask();
} }
public Task<DDPClientCallback.Ping> ping(@Nullable String id) { private Task<DDPClientCallback.Ping> ping(@Nullable String id) {
TaskCompletionSource<DDPClientCallback.Ping> task = new TaskCompletionSource<>(); TaskCompletionSource<DDPClientCallback.Ping> task = new TaskCompletionSource<>();
impl.ping(task, id); impl.ping(task, id);
return task.getTask(); return task.getTask();
} }
public Maybe<DDPClientCallback.Base> doPing(@Nullable String id) { private Maybe<DDPClientCallback.Base> doPing(@Nullable String id) {
return impl.ping(id); return impl.ping(id);
} }
public Task<DDPClientCallback.RPC> rpc(String method, JSONArray params, String id, private Task<DDPSubscription.Ready> sub(String id, String name, JSONArray params) {
long timeoutMs) {
TaskCompletionSource<DDPClientCallback.RPC> task = new TaskCompletionSource<>();
impl.rpc(task, method, params, id, timeoutMs);
return task.getTask();
}
public Task<DDPSubscription.Ready> sub(String id, String name, JSONArray params) {
TaskCompletionSource<DDPSubscription.Ready> task = new TaskCompletionSource<>(); TaskCompletionSource<DDPSubscription.Ready> task = new TaskCompletionSource<>();
impl.sub(task, name, params, id); impl.sub(task, name, params, id);
return task.getTask(); return task.getTask();
} }
public Task<DDPSubscription.NoSub> unsub(String id) { private Task<DDPSubscription.NoSub> unsub(String id) {
TaskCompletionSource<DDPSubscription.NoSub> task = new TaskCompletionSource<>(); TaskCompletionSource<DDPSubscription.NoSub> task = new TaskCompletionSource<>();
impl.unsub(task, id); impl.unsub(task, id);
return task.getTask(); return task.getTask();
} }
public Task<RxWebSocketCallback.Close> getOnCloseCallback() {
return impl.getOnCloseCallback();
}
public void close() {
impl.close(REASON_CLOSED_BY_USER, "closed by DDPClient#close()");
}
/**
* check WebSocket connectivity with ping.
*/
public Task<Void> ping() {
final String pingId = UUID.randomUUID().toString();
RCLog.d("ping[%s] >", pingId);
return ping(pingId)
.continueWithTask(task -> {
if (task.isFaulted()) {
RCLog.d(task.getError(), "ping[%s] xxx failed xxx", pingId);
return Task.forError(task.getError());
} else {
RCLog.d("pong[%s] <", pingId);
return Task.forResult(null);
}
});
}
/**
* check WebSocket connectivity with ping.
*/
public Maybe<DDPClientCallback.Base> doPing() {
final String pingId = UUID.randomUUID().toString();
RCLog.d("ping[%s] >", pingId);
return doPing(pingId);
}
/**
* Connect to WebSocket server with DDP client.
*/
public Task<DDPClientCallback.Connect> connect(@NonNull String hostname, @Nullable String session,
boolean usesSecureConnection) {
final String protocol = usesSecureConnection ? "wss://" : "ws://";
return connect(protocol + hostname + "/websocket", session);
}
/**
* Subscribe with DDP client.
*/
public Task<DDPSubscription.Ready> subscribe(final String name, JSONArray param) {
final String subscriptionId = UUID.randomUUID().toString();
RCLog.d("sub:[%s]> %s(%s)", subscriptionId, name, param);
return sub(subscriptionId, name, param);
}
/**
* Unsubscribe with DDP client.
*/
public Task<DDPSubscription.NoSub> unsubscribe(final String subscriptionId) {
RCLog.d("unsub:[%s]>", subscriptionId);
return unsub(subscriptionId);
}
/**
* Returns Observable for handling DDP subscription.
*/
public Flowable<DDPSubscription.Event> getSubscriptionCallback() { public Flowable<DDPSubscription.Event> getSubscriptionCallback() {
return impl.getDDPSubscription(); return impl.getDDPSubscription();
} }
public Task<RxWebSocketCallback.Close> getOnCloseCallback() { /**
return impl.getOnCloseCallback(); * Execute raw RPC.
*/
public Task<DDPClientCallback.RPC> rpc(String methodCallId, String methodName, String params,
long timeoutMs) {
TaskCompletionSource<DDPClientCallback.RPC> task = new TaskCompletionSource<>();
RCLog.d("rpc:[%s]> %s(%s) timeout=%d", methodCallId, methodName, params, timeoutMs);
if (TextUtils.isEmpty(params)) {
impl.rpc(task, methodName, null, methodCallId, timeoutMs);
return task.getTask().continueWithTask(task_ -> {
if (task_.isFaulted()) {
RCLog.d("rpc:[%s]< error = %s", methodCallId, task_.getError());
} else {
RCLog.d("rpc:[%s]< result = %s", methodCallId, task_.getResult().result);
}
return task_;
});
} }
public void close() { try {
impl.close(1000, "closed by DDPClient#close()"); impl.rpc(task, methodName, new JSONArray(params), methodCallId, timeoutMs);
return task.getTask().continueWithTask(task_ -> {
if (task_.isFaulted()) {
RCLog.d("rpc:[%s]< error = %s", methodCallId, task_.getError());
} else {
RCLog.d("rpc:[%s]< result = %s", methodCallId, task_.getResult().result);
}
return task_;
});
} catch (JSONException exception) {
return Task.forError(exception);
}
} }
} }
...@@ -2,6 +2,7 @@ package chat.rocket.android_ddp; ...@@ -2,6 +2,7 @@ package chat.rocket.android_ddp;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import org.json.JSONObject; import org.json.JSONObject;
public class DDPClientCallback { public class DDPClientCallback {
......
...@@ -52,14 +52,14 @@ public class DDPClientImpl { ...@@ -52,14 +52,14 @@ public class DDPClientImpl {
} }
} }
public void connect(final TaskCompletionSource<DDPClientCallback.Connect> task, final String url, /* package */ void connect(final TaskCompletionSource<DDPClientCallback.Connect> task, final String url,
String session) { String session) {
try { try {
flowable = websocket.connect(url).autoConnect(2); flowable = websocket.connect(url).autoConnect(2);
CompositeDisposable disposables = new CompositeDisposable(); CompositeDisposable disposables = new CompositeDisposable();
disposables.add( disposables.add(
flowable.retry().filter(callback -> callback instanceof RxWebSocketCallback.Open) flowable.filter(callback -> callback instanceof RxWebSocketCallback.Open)
.subscribe( .subscribe(
callback -> callback ->
sendMessage("connect", sendMessage("connect",
...@@ -115,6 +115,7 @@ public class DDPClientImpl { ...@@ -115,6 +115,7 @@ public class DDPClientImpl {
if (requested) { if (requested) {
return flowable.filter(callback -> callback instanceof RxWebSocketCallback.Message) return flowable.filter(callback -> callback instanceof RxWebSocketCallback.Message)
.timeout(8, TimeUnit.SECONDS)
.map(callback -> ((RxWebSocketCallback.Message) callback).responseBodyString) .map(callback -> ((RxWebSocketCallback.Message) callback).responseBodyString)
.map(DDPClientImpl::toJson) .map(DDPClientImpl::toJson)
.filter(response -> "pong".equalsIgnoreCase(extractMsg(response))) .filter(response -> "pong".equalsIgnoreCase(extractMsg(response)))
......
package chat.rocket.android_ddp; package chat.rocket.android_ddp;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONObject; import org.json.JSONObject;
......
package chat.rocket.android_ddp.rx; package chat.rocket.android_ddp.rx;
import java.io.IOException; import java.io.IOException;
import java.util.concurrent.TimeUnit;
import chat.rocket.android.log.RCLog; import chat.rocket.android.log.RCLog;
import io.reactivex.BackpressureStrategy; import io.reactivex.BackpressureStrategy;
......
...@@ -8,35 +8,10 @@ repositories { ...@@ -8,35 +8,10 @@ repositories {
apply plugin: 'kotlin-android' apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions' apply plugin: 'kotlin-android-extensions'
apply plugin: 'me.tatarka.retrolambda'
apply plugin: 'com.jakewharton.hugo' apply plugin: 'com.jakewharton.hugo'
apply plugin: 'com.github.triplet.play' apply plugin: 'com.github.triplet.play'
apply from: '../config/quality/quality.gradle' apply from: '../config/quality/quality.gradle'
buildscript {
repositories {
jcenter()
mavenCentral()
maven { url 'https://maven.fabric.io/public' }
}
dependencies {
classpath 'com.android.tools.build:gradle:2.3.3'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$rootProject.ext.kotlinVersion"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
classpath 'me.tatarka:gradle-retrolambda:3.5.0'
classpath 'me.tatarka.retrolambda.projectlombok:lombok.ast:0.2.3.a2'
classpath 'io.realm:realm-gradle-plugin:2.3.1'
classpath 'com.jakewharton.hugo:hugo-plugin:1.2.1'
classpath 'com.google.gms:google-services:3.0.0'
classpath 'com.github.triplet.gradle:play-publisher:1.1.5'
classpath 'io.fabric.tools:gradle:1.+'
}
// Exclude the version that the android plugin depends on.
configurations.classpath.exclude group: 'com.android.tools.external.lombok'
}
android { android {
compileSdkVersion rootProject.ext.compileSdkVersion compileSdkVersion rootProject.ext.compileSdkVersion
buildToolsVersion rootProject.ext.buildToolsVersion buildToolsVersion rootProject.ext.buildToolsVersion
...@@ -45,8 +20,8 @@ android { ...@@ -45,8 +20,8 @@ android {
applicationId "chat.rocket.android" applicationId "chat.rocket.android"
minSdkVersion 16 minSdkVersion 16
targetSdkVersion rootProject.ext.targetSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 44 versionCode 54
versionName "1.0.22" versionName "1.0.31"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true vectorDrawables.useSupportLibrary = true
multiDexEnabled true multiDexEnabled true
...@@ -108,6 +83,13 @@ android { ...@@ -108,6 +83,13 @@ android {
androidTest.java.srcDirs += 'src/androidTest/kotlin' androidTest.java.srcDirs += 'src/androidTest/kotlin'
} }
} }
dexOptions {
if(System.getenv()["CIRCLECI"] as boolean) {
javaMaxHeapSize "1536M"
preDexLibraries false
}
}
} }
play { play {
...@@ -115,7 +97,7 @@ play { ...@@ -115,7 +97,7 @@ play {
track = "${track}" track = "${track}"
} }
ext { ext {
playLibVersion = '11.0.4' playLibVersion = '11.6.0'
stethoVersion = '1.5.0' stethoVersion = '1.5.0'
stethoOkhttp3Version = '1.5.0' stethoOkhttp3Version = '1.5.0'
stethoRealmVersion = '2.1.0' stethoRealmVersion = '2.1.0'
...@@ -132,47 +114,37 @@ dependencies { ...@@ -132,47 +114,37 @@ dependencies {
compile extraDependencies.okHTTP compile extraDependencies.okHTTP
compile extraDependencies.rxJava compile extraDependencies.rxJava
compile extraDependencies.boltTask compile extraDependencies.boltTask
compile supportDependencies.multidex
compile supportDependencies.designSupportLibrary compile supportDependencies.designSupportLibrary
compile supportDependencies.annotation compile supportDependencies.annotation
compile supportDependencies.kotlin;
compile rxbindingDependencies.rxBinding compile rxbindingDependencies.rxBinding
compile rxbindingDependencies.rxBindingSupport compile rxbindingDependencies.rxBindingSupport
compile rxbindingDependencies.rxBindingAppcompact compile rxbindingDependencies.rxBindingAppcompact
compile 'com.android.support:multidex:1.0.1' compile "org.jetbrains.kotlin:kotlin-stdlib-jre7:$rootProject.ext.kotlinVersion"
compile "com.google.firebase:firebase-core:$playLibVersion" compile "com.google.firebase:firebase-core:$playLibVersion"
compile "com.google.firebase:firebase-crash:$playLibVersion" compile "com.google.firebase:firebase-crash:$playLibVersion"
compile "com.google.android.gms:play-services-gcm:$playLibVersion" compile "com.google.android.gms:play-services-gcm:$playLibVersion"
debugCompile "com.facebook.stetho:stetho:$stethoVersion"
debugCompile "com.facebook.stetho:stetho-okhttp3:$stethoOkhttp3Version"
debugCompile "com.uphyca:stetho_realm:$stethoRealmVersion"
compile "com.trello.rxlifecycle2:rxlifecycle:$rxlifecycleVersion" compile "com.trello.rxlifecycle2:rxlifecycle:$rxlifecycleVersion"
compile "com.trello.rxlifecycle2:rxlifecycle-android:$rxlifecycleVersion" compile "com.trello.rxlifecycle2:rxlifecycle-android:$rxlifecycleVersion"
compile "com.trello.rxlifecycle2:rxlifecycle-components:$rxlifecycleVersion" compile "com.trello.rxlifecycle2:rxlifecycle-components:$rxlifecycleVersion"
compile 'nl.littlerobots.rxlint:rxlint:1.2' compile 'nl.littlerobots.rxlint:rxlint:1.2'
compile "frankiesardo:icepick:$icepickVersion" compile "frankiesardo:icepick:$icepickVersion"
provided "frankiesardo:icepick-processor:$icepickVersion" annotationProcessor "frankiesardo:icepick-processor:$icepickVersion"
compile "com.github.hotchemi:permissionsdispatcher:$permissionsdispatcherVersion" compile "com.github.hotchemi:permissionsdispatcher:$permissionsdispatcherVersion"
annotationProcessor "com.github.hotchemi:permissionsdispatcher-processor:$permissionsdispatcherVersion" annotationProcessor "com.github.hotchemi:permissionsdispatcher-processor:$permissionsdispatcherVersion"
compile('com.crashlytics.sdk.android:crashlytics:2.6.8@aar') { compile('com.crashlytics.sdk.android:crashlytics:2.6.8@aar') {
transitive = true; transitive = true;
} }
debugCompile "com.facebook.stetho:stetho:$stethoVersion"
debugCompile "com.facebook.stetho:stetho-okhttp3:$stethoOkhttp3Version"
debugCompile "com.uphyca:stetho_realm:$stethoRealmVersion"
debugCompile "com.tspoon.traceur:traceur:1.0.1" debugCompile "com.tspoon.traceur:traceur:1.0.1"
compile 'com.aurelhubert:ahbottomnavigation:2.0.6'
compile('com.h6ah4i.android.widget.advrecyclerview:advrecyclerview:0.10.6@aar') {
transitive = true
}
compile 'com.github.JakeWharton:ViewPagerIndicator:2.4.1@aar'
compile 'com.jakewharton:butterknife:8.5.1'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.5.1'
compile 'com.jakewharton.timber:timber:4.5.1'
compile 'com.github.matrixxun:MaterialBadgeTextView:c5a27e8243'
compile 'com.github.chrisbanes:PhotoView:2.0.0'
provided 'io.reactivex:rxjava:1.3.0'
provided "com.github.akarnokd:rxjava2-interop:0.10.2"
provided 'com.hadisatrio:Optional:v1.0.1'
testCompile 'junit:junit:4.12' testCompile 'junit:junit:4.12'
testCompile "org.mockito:mockito-core:2.7.19" testCompile 'org.robolectric:robolectric:3.3'
testCompile "org.jetbrains.kotlin:kotlin-test:$rootProject.ext.kotlinVersion" testCompile "org.jetbrains.kotlin:kotlin-test:$rootProject.ext.kotlinVersion"
testCompile "org.jetbrains.kotlin:kotlin-test-junit:$rootProject.ext.kotlinVersion" testCompile "org.jetbrains.kotlin:kotlin-test-junit:$rootProject.ext.kotlinVersion"
testCompile "org.jetbrains.kotlin:kotlin-reflect:$rootProject.ext.kotlinVersion"
testCompile "com.nhaarman:mockito-kotlin:1.5.0"
testCompile 'org.amshove.kluent:kluent:1.14'
} }
apply plugin: 'com.google.gms.google-services' apply plugin: 'com.google.gms.google-services'
package chat.rocket.android; package chat.rocket.android;
import android.os.StrictMode; import android.os.StrictMode;
import com.facebook.stetho.Stetho; import com.facebook.stetho.Stetho;
import com.tspoon.traceur.Traceur; import com.tspoon.traceur.Traceur;
import com.uphyca.stetho_realm.RealmInspectorModulesProvider; import com.uphyca.stetho_realm.RealmInspectorModulesProvider;
......
...@@ -5,8 +5,8 @@ import chat.rocket.android.RocketChatCache ...@@ -5,8 +5,8 @@ import chat.rocket.android.RocketChatCache
import chat.rocket.android.api.rest.CookieInterceptor import chat.rocket.android.api.rest.CookieInterceptor
import chat.rocket.android.api.rest.DefaultCookieProvider import chat.rocket.android.api.rest.DefaultCookieProvider
import com.facebook.stetho.okhttp3.StethoInterceptor import com.facebook.stetho.okhttp3.StethoInterceptor
import java.util.concurrent.TimeUnit
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import java.util.concurrent.TimeUnit
object OkHttpHelper { object OkHttpHelper {
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/> <uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.VIBRATE"/>
<permission <permission
android:name="chat.rocket.android.permission.C2D_MESSAGE" android:name="chat.rocket.android.permission.C2D_MESSAGE"
...@@ -56,10 +57,32 @@ ...@@ -56,10 +57,32 @@
android:permission="com.google.android.c2dm.permission.SEND"> android:permission="com.google.android.c2dm.permission.SEND">
<intent-filter> <intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE"/> <action android:name="com.google.android.c2dm.intent.RECEIVE"/>
<category android:name="com.example.gcm"/> <action android:name="com.google.android.c2dm.intent.REGISTRATION"/>
<category android:name="chat.rocket.android"/>
</intent-filter> </intent-filter>
</receiver> </receiver>
<receiver
android:name="com.google.firebase.iid.FirebaseInstanceIdReceiver"
android:exported="true"
android:permission="com.google.android.c2dm.permission.SEND">
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE"/>
<category android:name="chat.rocket.android"/>
</intent-filter>
</receiver>
<receiver
android:name="com.google.firebase.iid.FirebaseInstanceIdInternalReceiver"
android:exported="false" />
<service android:name="com.google.firebase.iid.FirebaseInstanceIdService"
android:exported="true">
<intent-filter android:priority="-500">
<action android:name="com.google.firebase.INSTANCE_ID_EVENT" />
</intent-filter>
</service>
<service <service
android:name=".push.gcm.GCMIntentService" android:name=".push.gcm.GCMIntentService"
android:exported="false"> android:exported="false">
...@@ -76,6 +99,12 @@ ...@@ -76,6 +99,12 @@
</intent-filter> </intent-filter>
</service> </service>
<receiver android:name=".push.PushManager$DeleteReceiver"
android:exported="false" />
<receiver android:name=".push.PushManager$ReplyReceiver"
android:exported="false" />
<meta-data <meta-data
android:name="io.fabric.ApiKey" android:name="io.fabric.ApiKey"
android:value="12ac6e94f850aaffcdff52001af77ca415d06a43" /> android:value="12ac6e94f850aaffcdff52001af77ca415d06a43" />
......
...@@ -4,16 +4,21 @@ import android.os.Build; ...@@ -4,16 +4,21 @@ import android.os.Build;
import android.support.multidex.MultiDexApplication; import android.support.multidex.MultiDexApplication;
import android.support.v7.app.AppCompatDelegate; import android.support.v7.app.AppCompatDelegate;
import chat.rocket.android.helper.OkHttpHelper;
import com.crashlytics.android.Crashlytics; import com.crashlytics.android.Crashlytics;
import io.fabric.sdk.android.Fabric;
import java.util.List; import java.util.List;
import chat.rocket.persistence.realm.RealmStore;
import chat.rocket.android.helper.Logger;
import chat.rocket.android.helper.OkHttpHelper;
import chat.rocket.android.service.ConnectivityManager; import chat.rocket.android.service.ConnectivityManager;
import chat.rocket.core.models.ServerInfo;
import chat.rocket.android.widget.RocketChatWidgets; import chat.rocket.android.widget.RocketChatWidgets;
import chat.rocket.android_ddp.DDPClient;
import chat.rocket.core.models.ServerInfo;
import chat.rocket.persistence.realm.RealmStore;
import chat.rocket.persistence.realm.RocketChatPersistenceRealm; import chat.rocket.persistence.realm.RocketChatPersistenceRealm;
import io.fabric.sdk.android.Fabric;
import io.reactivex.exceptions.UndeliverableException;
import io.reactivex.plugins.RxJavaPlugins;
/** /**
* Customized Application-class for Rocket.Chat * Customized Application-class for Rocket.Chat
...@@ -29,6 +34,7 @@ public class RocketChatApplication extends MultiDexApplication { ...@@ -29,6 +34,7 @@ public class RocketChatApplication extends MultiDexApplication {
@Override @Override
public void onCreate() { public void onCreate() {
super.onCreate(); super.onCreate();
DDPClient.initialize(OkHttpHelper.INSTANCE.getClientForWebSocket());
Fabric.with(this, new Crashlytics()); Fabric.with(this, new Crashlytics());
RocketChatPersistenceRealm.init(this); RocketChatPersistenceRealm.init(this);
...@@ -44,6 +50,16 @@ public class RocketChatApplication extends MultiDexApplication { ...@@ -44,6 +50,16 @@ public class RocketChatApplication extends MultiDexApplication {
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true); AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
} }
RxJavaPlugins.setErrorHandler(e -> {
if (e instanceof UndeliverableException) {
e = e.getCause();
}
if (BuildConfig.DEBUG) {
e.printStackTrace();
}
Logger.report(e);
});
instance = this; instance = this;
} }
} }
\ No newline at end of file
...@@ -22,12 +22,15 @@ import io.reactivex.BackpressureStrategy; ...@@ -22,12 +22,15 @@ import io.reactivex.BackpressureStrategy;
import io.reactivex.Flowable; import io.reactivex.Flowable;
import io.reactivex.annotations.NonNull; import io.reactivex.annotations.NonNull;
import io.reactivex.annotations.Nullable; import io.reactivex.annotations.Nullable;
import okhttp3.HttpUrl;
/** /**
* sharedpreference-based cache. * sharedpreference-based cache.
*/ */
public class RocketChatCache { public class RocketChatCache {
private static final String KEY_SELECTED_SERVER_HOSTNAME = "KEY_SELECTED_SERVER_HOSTNAME"; private static final String KEY_SELECTED_SERVER_HOSTNAME = "KEY_SELECTED_SERVER_HOSTNAME";
private static final String KEY_SELECTED_SITE_URL = "KEY_SELECTED_SITE_URL";
private static final String KEY_SELECTED_SITE_NAME = "KEY_SELECTED_SITE_NAME";
private static final String KEY_SELECTED_ROOM_ID = "KEY_SELECTED_ROOM_ID"; private static final String KEY_SELECTED_ROOM_ID = "KEY_SELECTED_ROOM_ID";
private static final String KEY_PUSH_ID = "KEY_PUSH_ID"; private static final String KEY_PUSH_ID = "KEY_PUSH_ID";
private static final String KEY_HOSTNAME_LIST = "KEY_HOSTNAME_LIST"; private static final String KEY_HOSTNAME_LIST = "KEY_HOSTNAME_LIST";
...@@ -50,6 +53,75 @@ public class RocketChatCache { ...@@ -50,6 +53,75 @@ public class RocketChatCache {
setString(KEY_SELECTED_SERVER_HOSTNAME, newHostname); setString(KEY_SELECTED_SERVER_HOSTNAME, newHostname);
} }
public void addHostSiteName(@NonNull String currentHostname, @NonNull String siteName) {
try {
String hostSiteNamesJson = getHostSiteNamesJson();
JSONObject jsonObject = (hostSiteNamesJson == null) ?
new JSONObject() : new JSONObject(hostSiteNamesJson);
jsonObject.put(currentHostname, siteName);
setString(KEY_SELECTED_SITE_NAME, jsonObject.toString());
} catch (JSONException e) {
RCLog.e(e);
}
}
public @NonNull String getHostSiteName(@NonNull String host) {
if (host.startsWith("http")) {
HttpUrl url = HttpUrl.parse(host);
if (url != null) {
host = url.host();
}
}
try {
String hostSiteNamesJson = getHostSiteNamesJson();
JSONObject jsonObject = (hostSiteNamesJson == null) ?
new JSONObject() : new JSONObject(hostSiteNamesJson);
host = getSiteUrlFor(host);
return jsonObject.optString(host);
} catch (JSONException e) {
RCLog.e(e);
}
return "";
}
private @Nullable String getHostSiteNamesJson() {
return getString(KEY_SELECTED_SITE_NAME, null);
}
public void addHostnameSiteUrl(@Nullable String hostnameAlias, @NonNull String currentHostname) {
String alias = null;
if (hostnameAlias != null) {
alias = hostnameAlias.toLowerCase();
}
try {
String selectedHostnameAliasJson = getLoginHostnamesJson();
JSONObject jsonObject = selectedHostnameAliasJson == null ?
new JSONObject() : new JSONObject(selectedHostnameAliasJson);
jsonObject.put(alias, currentHostname);
setString(KEY_SELECTED_SITE_URL, jsonObject.toString());
} catch (JSONException e) {
RCLog.e(e);
}
}
public @Nullable String getSiteUrlFor(String hostname) {
try {
String selectedServerHostname = getSelectedServerHostname();
if (getLoginHostnamesJson() == null || getLoginHostnamesJson().isEmpty()) {
return null;
}
return new JSONObject(getLoginHostnamesJson())
.optString(hostname, selectedServerHostname);
} catch (JSONException e) {
RCLog.e(e);
}
return null;
}
private @Nullable String getLoginHostnamesJson() {
return getString(KEY_SELECTED_SITE_URL, null);
}
public void addHostname(@NonNull String hostname, @Nullable String hostnameAvatarUri, String siteName) { public void addHostname(@NonNull String hostname, @Nullable String hostnameAvatarUri, String siteName) {
String hostnameList = getString(KEY_HOSTNAME_LIST, null); String hostnameList = getString(KEY_HOSTNAME_LIST, null);
try { try {
......
...@@ -3,21 +3,25 @@ package chat.rocket.android.activity; ...@@ -3,21 +3,25 @@ package chat.rocket.android.activity;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import com.hadisatrio.optional.Optional; import com.hadisatrio.optional.Optional;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.schedulers.Schedulers;
import java.util.List; import java.util.List;
import chat.rocket.android.LaunchUtil; import chat.rocket.android.LaunchUtil;
import chat.rocket.android.R;
import chat.rocket.android.RocketChatCache; import chat.rocket.android.RocketChatCache;
import chat.rocket.android.helper.Logger; import chat.rocket.android.helper.Logger;
import chat.rocket.android.push.PushConstants; import chat.rocket.android.push.PushManager;
import chat.rocket.android.push.PushNotificationHandler;
import chat.rocket.android.service.ConnectivityManager; import chat.rocket.android.service.ConnectivityManager;
import chat.rocket.core.models.ServerInfo; import chat.rocket.core.models.ServerInfo;
import chat.rocket.persistence.realm.RealmStore; import chat.rocket.persistence.realm.RealmStore;
import chat.rocket.persistence.realm.models.ddp.RealmRoom; import chat.rocket.persistence.realm.models.ddp.RealmRoom;
import icepick.State; import icepick.State;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.schedulers.Schedulers;
import okhttp3.HttpUrl;
abstract class AbstractAuthedActivity extends AbstractFragmentActivity { abstract class AbstractAuthedActivity extends AbstractFragmentActivity {
@State protected String hostname; @State protected String hostname;
...@@ -39,7 +43,6 @@ abstract class AbstractAuthedActivity extends AbstractFragmentActivity { ...@@ -39,7 +43,6 @@ abstract class AbstractAuthedActivity extends AbstractFragmentActivity {
} }
updateHostnameIfNeeded(rocketChatCache.getSelectedServerHostname()); updateHostnameIfNeeded(rocketChatCache.getSelectedServerHostname());
updateRoomIdIfNeeded(rocketChatCache.getSelectedRoomId());
} }
@Override @Override
...@@ -53,20 +56,36 @@ abstract class AbstractAuthedActivity extends AbstractFragmentActivity { ...@@ -53,20 +56,36 @@ abstract class AbstractAuthedActivity extends AbstractFragmentActivity {
return; return;
} }
if (intent.hasExtra(PushConstants.HOSTNAME)) { if (intent.hasExtra(PushManager.EXTRA_HOSTNAME)) {
rocketChatCache.setSelectedServerHostname(intent.getStringExtra(PushConstants.HOSTNAME)); String hostname = intent.getStringExtra(PushManager.EXTRA_HOSTNAME);
HttpUrl url = HttpUrl.parse(hostname);
if (url != null) {
String hostnameFromPush = url.host();
String loginHostname = rocketChatCache.getSiteUrlFor(hostnameFromPush);
rocketChatCache.setSelectedServerHostname(loginHostname);
if (intent.hasExtra(PushConstants.ROOM_ID)) { if (intent.hasExtra(PushManager.EXTRA_ROOM_ID)) {
rocketChatCache.setSelectedRoomId(intent.getStringExtra(PushConstants.ROOM_ID)); rocketChatCache.setSelectedRoomId(intent.getStringExtra(PushManager.EXTRA_ROOM_ID));
}
} }
PushManager.INSTANCE.clearNotificationsByHost(hostname);
} else { } else {
updateHostnameIfNeeded(rocketChatCache.getSelectedServerHostname()); updateHostnameIfNeeded(rocketChatCache.getSelectedServerHostname());
} }
if (intent.hasExtra(PushConstants.NOT_ID)) { if (intent.hasExtra(PushManager.EXTRA_NOT_ID) && intent.hasExtra(PushManager.EXTRA_HOSTNAME)) {
isNotification = true; isNotification = true;
PushNotificationHandler int notificationId = intent.getIntExtra(PushManager.EXTRA_NOT_ID, 0);
.cleanUpNotificationStack(intent.getIntExtra(PushConstants.NOT_ID, 0)); String hostname = intent.getStringExtra(PushManager.EXTRA_HOSTNAME);
HttpUrl url = HttpUrl.parse(hostname);
if (url != null) {
String hostnameFromPush = url.host();
String loginHostname = rocketChatCache.getSiteUrlFor(hostnameFromPush);
PushManager.INSTANCE.clearNotificationsByHostAndNotificationId(loginHostname, notificationId);
} else {
PushManager.INSTANCE.clearNotificationsByNotificationId(notificationId);
}
} }
} }
...@@ -74,17 +93,22 @@ abstract class AbstractAuthedActivity extends AbstractFragmentActivity { ...@@ -74,17 +93,22 @@ abstract class AbstractAuthedActivity extends AbstractFragmentActivity {
if (hostname == null) { if (hostname == null) {
if (newHostname != null && assertServerRealmStoreExists(newHostname)) { if (newHostname != null && assertServerRealmStoreExists(newHostname)) {
updateHostname(newHostname); updateHostname(newHostname);
updateRoomIdIfNeeded(rocketChatCache.getSelectedRoomId());
} else { } else {
recoverFromHostnameError(); recoverFromHostnameError();
} }
} else { } else {
if (hostname.equals(newHostname)) { if (hostname.equals(newHostname)) {
updateHostname(newHostname); updateHostname(newHostname);
updateRoomIdIfNeeded(rocketChatCache.getSelectedRoomId());
return; return;
} }
if (assertServerRealmStoreExists(newHostname)) { if (assertServerRealmStoreExists(newHostname)) {
updateHostname(newHostname); Intent intent = new Intent(this, MainActivity.class);
startActivity(intent);
finish();
overridePendingTransition(R.anim.slide_in, R.anim.slide_out);
} else { } else {
recoverFromHostnameError(); recoverFromHostnameError();
} }
......
...@@ -8,8 +8,8 @@ import android.support.v4.app.Fragment; ...@@ -8,8 +8,8 @@ import android.support.v4.app.Fragment;
import chat.rocket.android.R; import chat.rocket.android.R;
import chat.rocket.android.fragment.server_config.LoginFragment; import chat.rocket.android.fragment.server_config.LoginFragment;
import chat.rocket.android.fragment.server_config.RetryLoginFragment; import chat.rocket.android.fragment.server_config.RetryLoginFragment;
import chat.rocket.core.interactors.SessionInteractor;
import chat.rocket.android.service.ConnectivityManager; import chat.rocket.android.service.ConnectivityManager;
import chat.rocket.core.interactors.SessionInteractor;
import chat.rocket.persistence.realm.repositories.RealmSessionRepository; import chat.rocket.persistence.realm.repositories.RealmSessionRepository;
/** /**
......
...@@ -2,14 +2,13 @@ package chat.rocket.android.activity; ...@@ -2,14 +2,13 @@ package chat.rocket.android.activity;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import chat.rocket.android.BackgroundLooper; import chat.rocket.android.BackgroundLooper;
import chat.rocket.android.helper.Logger; import chat.rocket.android.helper.Logger;
import chat.rocket.android.service.ConnectivityManagerApi; import chat.rocket.android.service.ConnectivityManagerApi;
import chat.rocket.android.shared.BasePresenter; import chat.rocket.android.shared.BasePresenter;
import chat.rocket.core.interactors.SessionInteractor; import chat.rocket.core.interactors.SessionInteractor;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
public class LoginPresenter extends BasePresenter<LoginContract.View> public class LoginPresenter extends BasePresenter<LoginContract.View>
implements LoginContract.Presenter { implements LoginContract.Presenter {
......
package chat.rocket.android.activity; package chat.rocket.android.activity;
import android.content.Intent; import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.design.widget.Snackbar; import android.support.design.widget.Snackbar;
import android.support.v4.app.Fragment; import android.support.v4.app.Fragment;
...@@ -26,6 +28,7 @@ import chat.rocket.android.fragment.chatroom.RoomFragment; ...@@ -26,6 +28,7 @@ import chat.rocket.android.fragment.chatroom.RoomFragment;
import chat.rocket.android.fragment.sidebar.SidebarMainFragment; import chat.rocket.android.fragment.sidebar.SidebarMainFragment;
import chat.rocket.android.helper.KeyboardHelper; import chat.rocket.android.helper.KeyboardHelper;
import chat.rocket.android.service.ConnectivityManager; import chat.rocket.android.service.ConnectivityManager;
import chat.rocket.android.service.ConnectivityManagerApi;
import chat.rocket.android.widget.RoomToolbar; import chat.rocket.android.widget.RoomToolbar;
import chat.rocket.android.widget.helper.FrescoHelper; import chat.rocket.android.widget.helper.FrescoHelper;
import chat.rocket.core.interactors.CanCreateRoomInteractor; import chat.rocket.core.interactors.CanCreateRoomInteractor;
...@@ -44,9 +47,9 @@ import hugo.weaving.DebugLog; ...@@ -44,9 +47,9 @@ import hugo.weaving.DebugLog;
*/ */
public class MainActivity extends AbstractAuthedActivity implements MainContract.View { public class MainActivity extends AbstractAuthedActivity implements MainContract.View {
private RoomToolbar toolbar; private RoomToolbar toolbar;
private StatusTicker statusTicker;
private SlidingPaneLayout pane; private SlidingPaneLayout pane;
private MainContract.Presenter presenter; private MainContract.Presenter presenter;
private volatile Snackbar statusTicker;
@Override @Override
public int getLayoutContainerForFragment() { public int getLayoutContainerForFragment() {
...@@ -57,25 +60,32 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract ...@@ -57,25 +60,32 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract
protected void onCreate(@Nullable Bundle savedInstanceState) { protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); setContentView(R.layout.activity_main);
toolbar = (RoomToolbar) findViewById(R.id.activity_main_toolbar); toolbar = findViewById(R.id.activity_main_toolbar);
statusTicker = new StatusTicker(); pane = findViewById(R.id.sliding_pane);
pane = (SlidingPaneLayout) findViewById(R.id.sliding_pane);
setupToolbar(); setupToolbar();
} }
@Override @Override
protected void onResume() { protected void onResume() {
super.onResume(); super.onResume();
ConnectivityManagerApi connectivityManager = ConnectivityManager.getInstance(getApplicationContext());
if (hostname == null || presenter == null) { if (hostname == null || presenter == null) {
String previousHostname = hostname;
hostname = new RocketChatCache(getApplicationContext()).getSelectedServerHostname(); hostname = new RocketChatCache(getApplicationContext()).getSelectedServerHostname();
if (hostname == null) { if (hostname == null) {
showAddServerScreen(); showAddServerScreen();
} else { } else {
onHostnameUpdated(); onHostnameUpdated();
if (!hostname.equalsIgnoreCase(previousHostname)) {
connectivityManager.resetConnectivityStateList();
connectivityManager.keepAliveServer();
}
} }
} else { } else {
presenter.bindViewOnly(this); connectivityManager.keepAliveServer();
presenter.bindView(this);
presenter.loadSignedInServers(hostname); presenter.loadSignedInServers(hostname);
roomId = new RocketChatCache(getApplicationContext()).getSelectedRoomId();
} }
} }
...@@ -84,6 +94,8 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract ...@@ -84,6 +94,8 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract
if (presenter != null) { if (presenter != null) {
presenter.release(); presenter.release();
} }
// Dismiss any status ticker
if (statusTicker != null) statusTicker.dismiss();
super.onPause(); super.onPause();
} }
...@@ -100,18 +112,18 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract ...@@ -100,18 +112,18 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract
if (pane != null) { if (pane != null) {
pane.setPanelSlideListener(new SlidingPaneLayout.PanelSlideListener() { pane.setPanelSlideListener(new SlidingPaneLayout.PanelSlideListener() {
@Override @Override
public void onPanelSlide(View view, float v) { public void onPanelSlide(@NonNull View view, float v) {
//Ref: ActionBarDrawerToggle#setProgress //Ref: ActionBarDrawerToggle#setProgress
toolbar.setNavigationIconProgress(v); toolbar.setNavigationIconProgress(v);
} }
@Override @Override
public void onPanelOpened(View view) { public void onPanelOpened(@NonNull View view) {
toolbar.setNavigationIconVerticalMirror(true); toolbar.setNavigationIconVerticalMirror(true);
} }
@Override @Override
public void onPanelClosed(View view) { public void onPanelClosed(@NonNull View view) {
toolbar.setNavigationIconVerticalMirror(false); toolbar.setNavigationIconVerticalMirror(false);
Fragment fragment = getSupportFragmentManager() Fragment fragment = getSupportFragmentManager()
.findFragmentById(R.id.sidebar_fragment_container); .findFragmentById(R.id.sidebar_fragment_container);
...@@ -131,6 +143,7 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract ...@@ -131,6 +143,7 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract
}); });
} }
} }
closeSidebarIfNeeded();
} }
private boolean closeSidebarIfNeeded() { private boolean closeSidebarIfNeeded() {
...@@ -233,33 +246,41 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract ...@@ -233,33 +246,41 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract
@Override @Override
public void showLoginScreen() { public void showLoginScreen() {
LaunchUtil.showLoginActivity(this, hostname); LaunchUtil.showLoginActivity(this, hostname);
statusTicker.updateStatus(StatusTicker.STATUS_DISMISS, null); showConnectionOk();
} }
@Override @Override
public void showConnectionError() { public synchronized void showConnectionError() {
statusTicker.updateStatus(StatusTicker.STATUS_CONNECTION_ERROR, dismissStatusTickerIfShowing();
Snackbar.make(findViewById(getLayoutContainerForFragment()), statusTicker = Snackbar.make(findViewById(getLayoutContainerForFragment()),
R.string.fragment_retry_login_error_title, Snackbar.LENGTH_INDEFINITE) R.string.fragment_retry_login_error_title, Snackbar.LENGTH_INDEFINITE)
.setAction(R.string.fragment_retry_login_retry_title, view -> .setAction(R.string.fragment_retry_login_retry_title, view ->
presenter.onRetryLogin())); ConnectivityManager.getInstance(getApplicationContext()).keepAliveServer());
statusTicker.show();
} }
@Override @Override
public void showConnecting() { public synchronized void showConnecting() {
statusTicker.updateStatus(StatusTicker.STATUS_TOKEN_LOGIN, dismissStatusTickerIfShowing();
Snackbar.make(findViewById(getLayoutContainerForFragment()), statusTicker = Snackbar.make(findViewById(getLayoutContainerForFragment()),
R.string.server_config_activity_authenticating, Snackbar.LENGTH_INDEFINITE)); R.string.server_config_activity_authenticating, Snackbar.LENGTH_INDEFINITE);
statusTicker.show();
} }
@Override @Override
public void showConnectionOk() { public synchronized void showConnectionOk() {
statusTicker.updateStatus(StatusTicker.STATUS_DISMISS, null); dismissStatusTickerIfShowing();
}
private void dismissStatusTickerIfShowing() {
if (statusTicker != null) {
statusTicker.dismiss();
}
} }
@Override @Override
public void showSignedInServers(List<Pair<String, Pair<String, String>>> serverList) { public void showSignedInServers(List<Pair<String, Pair<String, String>>> serverList) {
final SlidingPaneLayout subPane = (SlidingPaneLayout) findViewById(R.id.sub_sliding_pane); final SlidingPaneLayout subPane = findViewById(R.id.sub_sliding_pane);
if (subPane != null) { if (subPane != null) {
LinearLayout serverListContainer = subPane.findViewById(R.id.server_list_bar); LinearLayout serverListContainer = subPane.findViewById(R.id.server_list_bar);
View addServerButton = subPane.findViewById(R.id.btn_add_server); View addServerButton = subPane.findViewById(R.id.btn_add_server);
...@@ -293,7 +314,13 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract ...@@ -293,7 +314,13 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract
newServerView.setOnClickListener(view -> changeServerIfNeeded(serverHostname)); newServerView.setOnClickListener(view -> changeServerIfNeeded(serverHostname));
FrescoHelper.INSTANCE.loadImage(serverButton, logoUrl, ContextCompat.getDrawable(this, R.mipmap.ic_launcher)); Drawable drawable = ContextCompat.getDrawable(this, R.mipmap.ic_launcher);
if (drawable == null) {
int id = getResources().getIdentifier(
"rocket_chat_notification", "drawable", getPackageName());
drawable = ContextCompat.getDrawable(this, id);
}
FrescoHelper.INSTANCE.loadImage(serverButton, logoUrl, drawable);
serverListContainer.addView(newServerView); serverListContainer.addView(newServerView);
} }
...@@ -302,6 +329,15 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract ...@@ -302,6 +329,15 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract
} }
} }
@Override
public void refreshRoom() {
Fragment fragment = getSupportFragmentManager().findFragmentById(getLayoutContainerForFragment());
if (fragment != null && fragment instanceof RoomFragment) {
RoomFragment roomFragment = (RoomFragment) fragment;
roomFragment.loadMessages();
}
}
private void changeServerIfNeeded(String serverHostname) { private void changeServerIfNeeded(String serverHostname) {
if (!hostname.equalsIgnoreCase(serverHostname)) { if (!hostname.equalsIgnoreCase(serverHostname)) {
RocketChatCache rocketChatCache = new RocketChatCache(getApplicationContext()); RocketChatCache rocketChatCache = new RocketChatCache(getApplicationContext());
...@@ -322,35 +358,4 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract ...@@ -322,35 +358,4 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract
public void beforeLogoutCleanUp() { public void beforeLogoutCleanUp() {
presenter.beforeLogoutCleanUp(); presenter.beforeLogoutCleanUp();
} }
//TODO: consider this class to define in layouthelper for more complicated operation.
private static class StatusTicker {
public static final int STATUS_DISMISS = 0;
public static final int STATUS_CONNECTION_ERROR = 1;
public static final int STATUS_TOKEN_LOGIN = 2;
public static final int STATUS_LOGGING_OUT = 3;
private int status;
private Snackbar snackbar;
public StatusTicker() {
status = STATUS_DISMISS;
}
public void updateStatus(int status, Snackbar snackbar) {
if (status == this.status) {
return;
}
this.status = status;
if (this.snackbar != null) {
this.snackbar.dismiss();
}
if (status != STATUS_DISMISS) {
this.snackbar = snackbar;
if (this.snackbar != null) {
this.snackbar.show();
}
}
}
}
} }
...@@ -26,6 +26,8 @@ public interface MainContract { ...@@ -26,6 +26,8 @@ public interface MainContract {
void showConnectionOk(); void showConnectionOk();
void showSignedInServers(List<Pair<String, Pair<String, String>>> serverList); void showSignedInServers(List<Pair<String, Pair<String, String>>> serverList);
void refreshRoom();
} }
interface Presenter extends BaseContract.Presenter<View> { interface Presenter extends BaseContract.Presenter<View> {
......
...@@ -18,6 +18,7 @@ import chat.rocket.android.log.RCLog; ...@@ -18,6 +18,7 @@ import chat.rocket.android.log.RCLog;
import chat.rocket.android.service.ConnectivityManagerApi; import chat.rocket.android.service.ConnectivityManagerApi;
import chat.rocket.android.service.ServerConnectivity; import chat.rocket.android.service.ServerConnectivity;
import chat.rocket.android.shared.BasePresenter; import chat.rocket.android.shared.BasePresenter;
import chat.rocket.android_ddp.DDPClient;
import chat.rocket.core.PublicSettingsConstants; import chat.rocket.core.PublicSettingsConstants;
import chat.rocket.core.interactors.CanCreateRoomInteractor; import chat.rocket.core.interactors.CanCreateRoomInteractor;
import chat.rocket.core.interactors.RoomInteractor; import chat.rocket.core.interactors.RoomInteractor;
...@@ -27,7 +28,6 @@ import chat.rocket.core.models.Session; ...@@ -27,7 +28,6 @@ import chat.rocket.core.models.Session;
import chat.rocket.core.models.User; import chat.rocket.core.models.User;
import chat.rocket.core.repositories.PublicSettingRepository; import chat.rocket.core.repositories.PublicSettingRepository;
import chat.rocket.core.utils.Pair; import chat.rocket.core.utils.Pair;
import hu.akarnokd.rxjava.interop.RxJavaInterop;
import io.reactivex.Flowable; import io.reactivex.Flowable;
import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable; import io.reactivex.disposables.Disposable;
...@@ -219,15 +219,21 @@ public class MainPresenter extends BasePresenter<MainContract.View> ...@@ -219,15 +219,21 @@ public class MainPresenter extends BasePresenter<MainContract.View>
} }
private void subscribeToNetworkChanges() { private void subscribeToNetworkChanges() {
Disposable disposable = RxJavaInterop.toV2Flowable(connectivityManagerApi.getServerConnectivityAsObservable()) Disposable disposable = connectivityManagerApi.getServerConnectivityAsObservable()
.distinctUntilChanged()
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribe( .subscribe(
connectivity -> { connectivity -> {
if (connectivity.state == ServerConnectivity.STATE_CONNECTED) { if (connectivity.state == ServerConnectivity.STATE_CONNECTED) {
view.showConnectionOk(); view.showConnectionOk();
return; view.refreshRoom();
} else if (connectivity.state == ServerConnectivity.STATE_DISCONNECTED) {
if (connectivity.code == DDPClient.REASON_NETWORK_ERROR) {
view.showConnectionError();
} }
} else {
view.showConnecting(); view.showConnecting();
}
}, },
Logger::report Logger::report
); );
......
...@@ -5,6 +5,7 @@ import android.support.v4.app.Fragment ...@@ -5,6 +5,7 @@ import android.support.v4.app.Fragment
import android.support.v7.app.AppCompatActivity import android.support.v7.app.AppCompatActivity
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.fragment.chatroom.list.RoomListFragment import chat.rocket.android.fragment.chatroom.list.RoomListFragment
import chat.rocket.core.models.Room
import kotlinx.android.synthetic.main.activity_room.* import kotlinx.android.synthetic.main.activity_room.*
class RoomActivity : AppCompatActivity() { class RoomActivity : AppCompatActivity() {
...@@ -20,7 +21,7 @@ class RoomActivity : AppCompatActivity() { ...@@ -20,7 +21,7 @@ class RoomActivity : AppCompatActivity() {
val extras = intent.extras val extras = intent.extras
val roomListFragment = RoomListFragment.newInstance(extras.getInt("actionId"), val roomListFragment = RoomListFragment.newInstance(extras.getInt("actionId"),
extras.getString("roomId"), extras.getString("roomId"),
extras.getString("roomType"), extras.getString("roomType", Room.TYPE_GROUP),
extras.getString("hostname"), extras.getString("hostname"),
extras.getString("token"), extras.getString("token"),
extras.getString("userId")) extras.getString("userId"))
......
package chat.rocket.android.api;
import android.support.annotation.Nullable;
import chat.rocket.android.helper.OkHttpHelper;
import org.json.JSONArray;
import org.json.JSONException;
import java.util.UUID;
import bolts.Task;
import chat.rocket.android.helper.TextUtils;
import chat.rocket.android.log.RCLog;
import chat.rocket.android_ddp.DDPClient;
import chat.rocket.android_ddp.DDPClientCallback;
import chat.rocket.android_ddp.DDPSubscription;
import io.reactivex.Flowable;
import io.reactivex.Maybe;
/**
* DDP client wrapper.
*/
public class DDPClientWrapper {
private final DDPClient ddpClient;
private final String hostname;
private DDPClientWrapper(String hostname) {
ddpClient = new DDPClient(OkHttpHelper.INSTANCE.getClientForWebSocket());
this.hostname = hostname;
}
/**
* build new API client instance.
*/
public static DDPClientWrapper create(String hostname) {
return new DDPClientWrapper(hostname);
}
/**
* Connect to WebSocket server with DDP client.
*/
public Task<DDPClientCallback.Connect> connect(@Nullable String session,
boolean usesSecureConnection) {
final String protocol = usesSecureConnection ? "wss://" : "ws://";
return ddpClient.connect(protocol + hostname + "/websocket", session);
}
/**
* close connection.
*/
public void close() {
ddpClient.close();
}
/**
* Subscribe with DDP client.
*/
public Task<DDPSubscription.Ready> subscribe(final String name, JSONArray param) {
final String subscriptionId = UUID.randomUUID().toString();
RCLog.d("sub:[%s]> %s(%s)", subscriptionId, name, param);
return ddpClient.sub(subscriptionId, name, param);
}
/**
* Unsubscribe with DDP client.
*/
public Task<DDPSubscription.NoSub> unsubscribe(final String subscriptionId) {
RCLog.d("unsub:[%s]>", subscriptionId);
return ddpClient.unsub(subscriptionId);
}
/**
* Returns Observable for handling DDP subscription.
*/
public Flowable<DDPSubscription.Event> getSubscriptionCallback() {
return ddpClient.getSubscriptionCallback();
}
/**
* Execute raw RPC.
*/
public Task<DDPClientCallback.RPC> rpc(String methodCallId, String methodName, String params,
long timeoutMs) {
RCLog.d("rpc:[%s]> %s(%s) timeout=%d", methodCallId, methodName, params, timeoutMs);
if (TextUtils.isEmpty(params)) {
return ddpClient.rpc(methodName, null, methodCallId, timeoutMs).continueWithTask(task -> {
if (task.isFaulted()) {
RCLog.d("rpc:[%s]< error = %s", methodCallId, task.getError());
} else {
RCLog.d("rpc:[%s]< result = %s", methodCallId, task.getResult().result);
}
return task;
});
}
try {
return ddpClient.rpc(methodName, new JSONArray(params), methodCallId, timeoutMs)
.continueWithTask(task -> {
if (task.isFaulted()) {
RCLog.d("rpc:[%s]< error = %s", methodCallId, task.getError());
} else {
RCLog.d("rpc:[%s]< result = %s", methodCallId, task.getResult().result);
}
return task;
});
} catch (JSONException exception) {
return Task.forError(exception);
}
}
/**
* check WebSocket connectivity with ping.
*/
public Task<Void> ping() {
final String pingId = UUID.randomUUID().toString();
RCLog.d("ping[%s] >", pingId);
return ddpClient.ping(pingId)
.continueWithTask(task -> {
if (task.isFaulted()) {
RCLog.d(task.getError(), "ping[%s] xxx failed xxx", pingId);
return Task.forError(task.getError());
} else {
RCLog.d("pong[%s] <", pingId);
return Task.forResult(null);
}
});
}
/**
* check WebSocket connectivity with ping.
*/
public Maybe<DDPClientCallback.Base> doPing() {
final String pingId = UUID.randomUUID().toString();
RCLog.d("ping[%s] >", pingId);
return ddpClient.doPing(pingId);
}
}
package chat.rocket.android.api; package chat.rocket.android.api;
import android.content.Context; import android.content.Context;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONObject; import org.json.JSONObject;
import bolts.Task; import bolts.Task;
import chat.rocket.android.helper.TextUtils; import chat.rocket.android.helper.TextUtils;
import chat.rocket.persistence.realm.RealmHelper; import chat.rocket.persistence.realm.RealmHelper;
import chat.rocket.android.service.DDPClientRef;
/** /**
* MethodCall for uploading file. * MethodCall for uploading file.
...@@ -17,8 +17,8 @@ public class FileUploadingHelper extends MethodCallHelper { ...@@ -17,8 +17,8 @@ public class FileUploadingHelper extends MethodCallHelper {
super(context, hostname); super(context, hostname);
} }
public FileUploadingHelper(RealmHelper realmHelper, DDPClientRef ddpClientRef) { public FileUploadingHelper(RealmHelper realmHelper) {
super(realmHelper, ddpClientRef); super(realmHelper);
} }
public Task<JSONObject> uploadS3Request(String filename, long filesize, String mimeType, public Task<JSONObject> uploadS3Request(String filename, long filesize, String mimeType,
......
...@@ -11,11 +11,13 @@ import java.util.UUID; ...@@ -11,11 +11,13 @@ import java.util.UUID;
import bolts.Continuation; import bolts.Continuation;
import bolts.Task; import bolts.Task;
import chat.rocket.android.RocketChatCache;
import chat.rocket.android.helper.CheckSum; import chat.rocket.android.helper.CheckSum;
import chat.rocket.android.helper.TextUtils; import chat.rocket.android.helper.TextUtils;
import chat.rocket.android.service.ConnectivityManager; import chat.rocket.android.service.ConnectivityManager;
import chat.rocket.android.service.DDPClientRef; import chat.rocket.android_ddp.DDPClient;
import chat.rocket.android_ddp.DDPClientCallback; import chat.rocket.android_ddp.DDPClientCallback;
import chat.rocket.core.PublicSettingsConstants;
import chat.rocket.core.SyncState; import chat.rocket.core.SyncState;
import chat.rocket.persistence.realm.RealmHelper; import chat.rocket.persistence.realm.RealmHelper;
import chat.rocket.persistence.realm.RealmStore; import chat.rocket.persistence.realm.RealmStore;
...@@ -30,6 +32,7 @@ import chat.rocket.persistence.realm.models.ddp.RealmSpotlightUser; ...@@ -30,6 +32,7 @@ import chat.rocket.persistence.realm.models.ddp.RealmSpotlightUser;
import chat.rocket.persistence.realm.models.internal.MethodCall; import chat.rocket.persistence.realm.models.internal.MethodCall;
import chat.rocket.persistence.realm.models.internal.RealmSession; import chat.rocket.persistence.realm.models.internal.RealmSession;
import hugo.weaving.DebugLog; import hugo.weaving.DebugLog;
import okhttp3.HttpUrl;
/** /**
* Utility class for creating/handling MethodCall or RPC. * Utility class for creating/handling MethodCall or RPC.
...@@ -45,7 +48,6 @@ public class MethodCallHelper { ...@@ -45,7 +48,6 @@ public class MethodCallHelper {
task -> Task.forResult(new JSONArray(task.getResult())); task -> Task.forResult(new JSONArray(task.getResult()));
protected final Context context; protected final Context context;
protected final RealmHelper realmHelper; protected final RealmHelper realmHelper;
protected final DDPClientRef ddpClientRef;
/** /**
* initialize with Context and hostname. * initialize with Context and hostname.
...@@ -53,23 +55,32 @@ public class MethodCallHelper { ...@@ -53,23 +55,32 @@ public class MethodCallHelper {
public MethodCallHelper(Context context, String hostname) { public MethodCallHelper(Context context, String hostname) {
this.context = context.getApplicationContext(); this.context = context.getApplicationContext();
this.realmHelper = RealmStore.getOrCreate(hostname); this.realmHelper = RealmStore.getOrCreate(hostname);
ddpClientRef = null;
} }
/** /**
* initialize with RealmHelper and DDPClient. * initialize with RealmHelper and DDPClient.
*/ */
public MethodCallHelper(RealmHelper realmHelper, DDPClientRef ddpClientRef) { public MethodCallHelper(RealmHelper realmHelper) {
this.context = null; this.context = null;
this.realmHelper = realmHelper; this.realmHelper = realmHelper;
this.ddpClientRef = ddpClientRef; }
public MethodCallHelper(Context context, RealmHelper realmHelper) {
this.context = context.getApplicationContext();
this.realmHelper = realmHelper;
} }
@DebugLog @DebugLog
private Task<String> executeMethodCall(String methodName, String param, long timeout) { private Task<String> executeMethodCall(String methodName, String param, long timeout) {
if (ddpClientRef != null) { if (DDPClient.get() != null) {
return ddpClientRef.get().rpc(UUID.randomUUID().toString(), methodName, param, timeout) return DDPClient.get().rpc(UUID.randomUUID().toString(), methodName, param, timeout)
.onSuccessTask(task -> Task.forResult(task.getResult().result)); .onSuccessTask(task -> Task.forResult(task.getResult().result))
.continueWithTask(task_ -> {
if (task_.isFaulted()) {
return Task.forError(task_.getError());
}
return Task.forResult(task_.getResult());
});
} else { } else {
return MethodCall.execute(realmHelper, methodName, param, timeout) return MethodCall.execute(realmHelper, methodName, param, timeout)
.onSuccessTask(task -> { .onSuccessTask(task -> {
...@@ -84,8 +95,13 @@ public class MethodCallHelper { ...@@ -84,8 +95,13 @@ public class MethodCallHelper {
return task.continueWithTask(_task -> { return task.continueWithTask(_task -> {
if (_task.isFaulted()) { if (_task.isFaulted()) {
Exception exception = _task.getError(); Exception exception = _task.getError();
if (exception instanceof MethodCall.Error) { if (exception instanceof MethodCall.Error || exception instanceof DDPClientCallback.RPC.Error) {
String errMessageJson = exception.getMessage(); String errMessageJson;
if (exception instanceof DDPClientCallback.RPC.Error) {
errMessageJson = ((DDPClientCallback.RPC.Error) exception).error.toString();
} else {
errMessageJson = exception.getMessage();
}
if (TextUtils.isEmpty(errMessageJson)) { if (TextUtils.isEmpty(errMessageJson)) {
return Task.forError(exception); return Task.forError(exception);
} }
...@@ -96,11 +112,10 @@ public class MethodCallHelper { ...@@ -96,11 +112,10 @@ public class MethodCallHelper {
return Task.forError(new TwoStepAuthException(errMessage)); return Task.forError(new TwoStepAuthException(errMessage));
} }
return Task.forError(new Exception(errMessage)); return Task.forError(new Exception(errMessage));
} else if (exception instanceof DDPClientCallback.RPC.Error) {
String errMessage = ((DDPClientCallback.RPC.Error) exception).error.getString("message");
return Task.forError(new Exception(errMessage));
} else if (exception instanceof DDPClientCallback.RPC.Timeout) { } else if (exception instanceof DDPClientCallback.RPC.Timeout) {
return Task.forError(new MethodCall.Timeout()); return Task.forError(new MethodCall.Timeout());
} else if (exception instanceof DDPClientCallback.Closed) {
return Task.forError(new Exception("Oops, your connection seems off..."));
} else { } else {
return Task.forError(exception); return Task.forError(exception);
} }
...@@ -385,6 +400,17 @@ public class MethodCallHelper { ...@@ -385,6 +400,17 @@ public class MethodCallHelper {
} }
} }
public Task<Void> deleteMessage(String messageID) {
try {
JSONObject messageJson = new JSONObject()
.put("_id", messageID);
return deleteMessage(messageJson);
} catch(JSONException exception) {
return Task.forError(exception);
}
}
/** /**
* Send message object. * Send message object.
*/ */
...@@ -398,6 +424,11 @@ public class MethodCallHelper { ...@@ -398,6 +424,11 @@ public class MethodCallHelper {
.onSuccessTask(task -> Task.forResult(null)); .onSuccessTask(task -> Task.forResult(null));
} }
private Task<Void> deleteMessage(final JSONObject messageJson) {
return call("deleteMessage", TIMEOUT_MS, () -> new JSONArray().put(messageJson))
.onSuccessTask(task -> Task.forResult(null));
}
/** /**
* mark all messages are read in the room. * mark all messages are read in the room.
*/ */
...@@ -406,13 +437,31 @@ public class MethodCallHelper { ...@@ -406,13 +437,31 @@ public class MethodCallHelper {
.onSuccessTask(task -> Task.forResult(null)); .onSuccessTask(task -> Task.forResult(null));
} }
public Task<Void> getPublicSettings() { public Task<Void> getPublicSettings(String currentHostname) {
return call("public-settings/get", TIMEOUT_MS) return call("public-settings/get", TIMEOUT_MS)
.onSuccessTask(CONVERT_TO_JSON_ARRAY) .onSuccessTask(CONVERT_TO_JSON_ARRAY)
.onSuccessTask(task -> { .onSuccessTask(task -> {
final JSONArray settings = task.getResult(); final JSONArray settings = task.getResult();
String siteUrl = null;
String siteName = null;
for (int i = 0; i < settings.length(); i++) { for (int i = 0; i < settings.length(); i++) {
RealmPublicSetting.customizeJson(settings.getJSONObject(i)); JSONObject jsonObject = settings.getJSONObject(i);
RealmPublicSetting.customizeJson(jsonObject);
if (isPublicSetting(jsonObject, PublicSettingsConstants.General.SITE_URL)) {
siteUrl = jsonObject.getString(RealmPublicSetting.VALUE);
} else if (isPublicSetting(jsonObject, PublicSettingsConstants.General.SITE_NAME)) {
siteName = jsonObject.getString(RealmPublicSetting.VALUE);
}
}
if (siteName != null && siteUrl != null) {
HttpUrl httpSiteUrl = HttpUrl.parse(siteUrl);
if (httpSiteUrl != null) {
String host = httpSiteUrl.host();
RocketChatCache rocketChatCache = new RocketChatCache(context);
rocketChatCache.addHostnameSiteUrl(host, currentHostname);
rocketChatCache.addHostSiteName(currentHostname, siteName);
}
} }
return realmHelper.executeTransaction(realm -> { return realmHelper.executeTransaction(realm -> {
...@@ -423,6 +472,10 @@ public class MethodCallHelper { ...@@ -423,6 +472,10 @@ public class MethodCallHelper {
}); });
} }
private boolean isPublicSetting(JSONObject jsonObject, String id) {
return jsonObject.optString(RealmPublicSetting.ID).equalsIgnoreCase(id);
}
public Task<Void> getPermissions() { public Task<Void> getPermissions() {
return call("permissions/get", TIMEOUT_MS) return call("permissions/get", TIMEOUT_MS)
.onSuccessTask(CONVERT_TO_JSON_ARRAY) .onSuccessTask(CONVERT_TO_JSON_ARRAY)
......
...@@ -3,21 +3,20 @@ package chat.rocket.android.api; ...@@ -3,21 +3,20 @@ package chat.rocket.android.api;
import android.content.Context; import android.content.Context;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONObject; import org.json.JSONObject;
import bolts.Task; import bolts.Task;
import chat.rocket.persistence.realm.RealmHelper; import chat.rocket.persistence.realm.RealmHelper;
import chat.rocket.android.service.DDPClientRef;
public class RaixPushHelper extends MethodCallHelper { public class RaixPushHelper extends MethodCallHelper {
public RaixPushHelper(Context context, String hostname) { public RaixPushHelper(Context context, String hostname) {
super(context, hostname); super(context, hostname);
} }
public RaixPushHelper(RealmHelper realmHelper, public RaixPushHelper(RealmHelper realmHelper) {
DDPClientRef ddpClientRef) { super(realmHelper);
super(realmHelper, ddpClientRef);
} }
public Task<Void> pushUpdate(@NonNull String pushId, @NonNull String gcmToken, public Task<Void> pushUpdate(@NonNull String pushId, @NonNull String gcmToken,
......
package chat.rocket.android.api.rest; package chat.rocket.android.api.rest;
import java.io.IOException; import java.io.IOException;
import chat.rocket.android.helper.TextUtils; import chat.rocket.android.helper.TextUtils;
import okhttp3.Interceptor; import okhttp3.Interceptor;
import okhttp3.Request; import okhttp3.Request;
......
package chat.rocket.android.api.rest; package chat.rocket.android.api.rest;
import chat.rocket.android.RocketChatCache; import chat.rocket.android.RocketChatCache;
import chat.rocket.persistence.realm.models.ddp.RealmUser;
import chat.rocket.persistence.realm.models.internal.RealmSession;
import chat.rocket.persistence.realm.RealmHelper; import chat.rocket.persistence.realm.RealmHelper;
import chat.rocket.persistence.realm.RealmStore; import chat.rocket.persistence.realm.RealmStore;
import chat.rocket.persistence.realm.models.ddp.RealmUser;
import chat.rocket.persistence.realm.models.internal.RealmSession;
public class DefaultCookieProvider implements CookieProvider { public class DefaultCookieProvider implements CookieProvider {
......
...@@ -2,12 +2,13 @@ package chat.rocket.android.api.rest; ...@@ -2,12 +2,13 @@ package chat.rocket.android.api.rest;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import io.reactivex.BackpressureStrategy;
import io.reactivex.Flowable;
import io.reactivex.FlowableEmitter;
import org.json.JSONObject; import org.json.JSONObject;
import java.io.IOException; import java.io.IOException;
import io.reactivex.BackpressureStrategy;
import io.reactivex.Flowable;
import io.reactivex.FlowableEmitter;
import okhttp3.Call; import okhttp3.Call;
import okhttp3.OkHttpClient; import okhttp3.OkHttpClient;
import okhttp3.Request; import okhttp3.Request;
......
package chat.rocket.android.api.rest; package chat.rocket.android.api.rest;
import io.reactivex.Flowable;
import org.json.JSONObject; import org.json.JSONObject;
import io.reactivex.Flowable;
public interface ServerPolicyApi { public interface ServerPolicyApi {
String SECURE_PROTOCOL = "https://"; String SECURE_PROTOCOL = "https://";
......
...@@ -6,6 +6,7 @@ import android.support.annotation.Nullable; ...@@ -6,6 +6,7 @@ import android.support.annotation.Nullable;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import com.trello.rxlifecycle2.components.support.RxFragment; import com.trello.rxlifecycle2.components.support.RxFragment;
/** /**
......
...@@ -7,6 +7,7 @@ import android.support.constraint.ConstraintLayout; ...@@ -7,6 +7,7 @@ import android.support.constraint.ConstraintLayout;
import android.support.design.widget.Snackbar; import android.support.design.widget.Snackbar;
import android.view.View; import android.view.View;
import android.widget.TextView; import android.widget.TextView;
import chat.rocket.android.BuildConfig; import chat.rocket.android.BuildConfig;
import chat.rocket.android.LaunchUtil; import chat.rocket.android.LaunchUtil;
import chat.rocket.android.R; import chat.rocket.android.R;
......
package chat.rocket.android.fragment.add_server; package chat.rocket.android.fragment.add_server;
import chat.rocket.android.BackgroundLooper; import chat.rocket.android.BackgroundLooper;
import chat.rocket.android.helper.Logger;
import chat.rocket.android.helper.OkHttpHelper;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import chat.rocket.android.RocketChatCache; import chat.rocket.android.RocketChatCache;
import chat.rocket.android.api.rest.DefaultServerPolicyApi; import chat.rocket.android.api.rest.DefaultServerPolicyApi;
import chat.rocket.android.api.rest.ServerPolicyApi; import chat.rocket.android.api.rest.ServerPolicyApi;
import chat.rocket.android.helper.Logger;
import chat.rocket.android.helper.OkHttpHelper;
import chat.rocket.android.helper.ServerPolicyApiValidationHelper; import chat.rocket.android.helper.ServerPolicyApiValidationHelper;
import chat.rocket.android.helper.ServerPolicyHelper; import chat.rocket.android.helper.ServerPolicyHelper;
import chat.rocket.android.service.ConnectivityManagerApi; import chat.rocket.android.service.ConnectivityManagerApi;
import chat.rocket.android.shared.BasePresenter; import chat.rocket.android.shared.BasePresenter;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
public class InputHostnamePresenter extends BasePresenter<InputHostnameContract.View> implements InputHostnameContract.Presenter { public class InputHostnamePresenter extends BasePresenter<InputHostnameContract.View> implements InputHostnameContract.Presenter {
private final RocketChatCache rocketChatCache; private final RocketChatCache rocketChatCache;
......
...@@ -35,6 +35,8 @@ public interface RoomContract { ...@@ -35,6 +35,8 @@ public interface RoomContract {
void showMessageSendFailure(Message message); void showMessageSendFailure(Message message);
void showMessageDeleteFailure(Message message);
void autoloadImages(); void autoloadImages();
void manualLoadImages(); void manualLoadImages();
...@@ -71,5 +73,7 @@ public interface RoomContract { ...@@ -71,5 +73,7 @@ public interface RoomContract {
void refreshRoom(); void refreshRoom();
void replyMessage(@NonNull Message message, boolean justQuote); void replyMessage(@NonNull Message message, boolean justQuote);
void acceptMessageDeleteFailure(Message message);
} }
} }
...@@ -647,6 +647,15 @@ public class RoomFragment extends AbstractChatRoomFragment implements ...@@ -647,6 +647,15 @@ public class RoomFragment extends AbstractChatRoomFragment implements
.show(); .show();
} }
@Override
public void showMessageDeleteFailure(Message message) {
new AlertDialog.Builder(getContext())
.setTitle(getContext().getString(R.string.failed_to_delete))
.setMessage(getContext().getString(R.string.failed_to_delete_message))
.setPositiveButton(R.string.ok, (dialog, which) -> presenter.acceptMessageDeleteFailure(message))
.show();
}
@Override @Override
public void autoloadImages() { public void autoloadImages() {
messageListAdapter.setAutoloadImages(true); messageListAdapter.setAutoloadImages(true);
...@@ -679,6 +688,7 @@ public class RoomFragment extends AbstractChatRoomFragment implements ...@@ -679,6 +688,7 @@ public class RoomFragment extends AbstractChatRoomFragment implements
.setEditAction(this::onEditMessage) .setEditAction(this::onEditMessage)
.setCopyAction(msg -> onCopy(message.getMessage())) .setCopyAction(msg -> onCopy(message.getMessage()))
.setQuoteAction(msg -> presenter.replyMessage(message, true)) .setQuoteAction(msg -> presenter.replyMessage(message, true))
.setDeleteAction(this::onDeleteMessage)
.showWith(context); .showWith(context);
} }
} }
...@@ -688,6 +698,10 @@ public class RoomFragment extends AbstractChatRoomFragment implements ...@@ -688,6 +698,10 @@ public class RoomFragment extends AbstractChatRoomFragment implements
messageFormManager.setEditMessage(message.getMessage()); messageFormManager.setEditMessage(message.getMessage());
} }
public void onDeleteMessage(Message message) {
presenter.deleteMessage(message);
}
private void showRoomListFragment(int actionId) { private void showRoomListFragment(int actionId) {
//TODO: oddly sometimes getActivity() yields null. Investigate the situations this might happen //TODO: oddly sometimes getActivity() yields null. Investigate the situations this might happen
//and fix it, removing this null-check //and fix it, removing this null-check
...@@ -701,4 +715,8 @@ public class RoomFragment extends AbstractChatRoomFragment implements ...@@ -701,4 +715,8 @@ public class RoomFragment extends AbstractChatRoomFragment implements
startActivity(intent); startActivity(intent);
} }
} }
public void loadMessages() {
presenter.loadMessages();
}
} }
\ No newline at end of file
...@@ -112,7 +112,11 @@ public class RoomPresenter extends BasePresenter<RoomContract.View> ...@@ -112,7 +112,11 @@ public class RoomPresenter extends BasePresenter<RoomContract.View>
return; return;
} }
if (message.getType() == null && message.getSyncState() == SyncState.SYNCED) { if (message.getSyncState() == SyncState.DELETE_FAILED) {
view.showMessageDeleteFailure(message);
} else if (message.getSyncState() == SyncState.FAILED) {
view.showMessageSendFailure(message);
} else if (message.getType() == null && message.getSyncState() == SyncState.SYNCED) {
// If message is not a system message show applicable actions. // If message is not a system message show applicable actions.
view.showMessageActions(message); view.showMessageActions(message);
} }
...@@ -147,6 +151,15 @@ public class RoomPresenter extends BasePresenter<RoomContract.View> ...@@ -147,6 +151,15 @@ public class RoomPresenter extends BasePresenter<RoomContract.View>
); );
} }
public void acceptMessageDeleteFailure(Message message) {
final Disposable subscription = messageInteractor.acceptDeleteFailure(message)
.subscribeOn(AndroidSchedulers.from(BackgroundLooper.get()))
.observeOn(AndroidSchedulers.mainThread())
.subscribe();
addSubscription(subscription);
}
private String buildReplyOrQuoteMarkdown(String baseUrl, Message message, boolean justQuote) { private String buildReplyOrQuoteMarkdown(String baseUrl, Message message, boolean justQuote) {
if (currentRoom == null || message.getUser() == null) { if (currentRoom == null || message.getUser() == null) {
return ""; return "";
......
...@@ -10,11 +10,6 @@ import android.support.v4.util.Pair; ...@@ -10,11 +10,6 @@ import android.support.v4.util.Pair;
import android.view.View; import android.view.View;
import android.widget.TextView; import android.widget.TextView;
import io.reactivex.Single;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable;
import chat.rocket.android.BackgroundLooper; import chat.rocket.android.BackgroundLooper;
import chat.rocket.android.R; import chat.rocket.android.R;
import chat.rocket.android.RocketChatCache; import chat.rocket.android.RocketChatCache;
...@@ -34,6 +29,10 @@ import chat.rocket.persistence.realm.repositories.RealmPublicSettingRepository; ...@@ -34,6 +29,10 @@ import chat.rocket.persistence.realm.repositories.RealmPublicSettingRepository;
import chat.rocket.persistence.realm.repositories.RealmRoomRepository; import chat.rocket.persistence.realm.repositories.RealmRoomRepository;
import chat.rocket.persistence.realm.repositories.RealmRoomRoleRepository; import chat.rocket.persistence.realm.repositories.RealmRoomRoleRepository;
import chat.rocket.persistence.realm.repositories.RealmUserRepository; import chat.rocket.persistence.realm.repositories.RealmUserRepository;
import io.reactivex.Single;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable;
public class MessageOptionsDialogFragment extends BottomSheetDialogFragment { public class MessageOptionsDialogFragment extends BottomSheetDialogFragment {
......
...@@ -47,20 +47,23 @@ class RoomListFragment : Fragment(), RoomListContract.View { ...@@ -47,20 +47,23 @@ class RoomListFragment : Fragment(), RoomListContract.View {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
activity.title = "" activity?.title = ""
actionId = arguments.getInt("actionId") val args = arguments
roomId = arguments.getString("roomId") args?.let {
roomType = arguments.getString("roomType") actionId = args.getInt("actionId")
hostname = arguments.getString("hostname") roomId = args.getString("roomId")
token = arguments.getString("token") roomType = args.getString("roomType")
userId = arguments.getString("userId") hostname = args.getString("hostname")
token = args.getString("token")
userId = args.getString("userId")
}
} }
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? = inflater?.inflate(R.layout.fragment_room_list, container, false) override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? = inflater.inflate(R.layout.fragment_room_list, container, false)
override fun onViewCreated(view: View?, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
presenter = RoomListPresenter(context, this) presenter = RoomListPresenter(context!!, this)
} }
override fun onResume() { override fun onResume() {
...@@ -114,9 +117,9 @@ class RoomListFragment : Fragment(), RoomListContract.View { ...@@ -114,9 +117,9 @@ class RoomListFragment : Fragment(), RoomListContract.View {
} }
override fun showPinnedMessages(dataSet: ArrayList<Message>, total: String) { override fun showPinnedMessages(dataSet: ArrayList<Message>, total: String) {
activity.title = getString(R.string.fragment_room_list_pinned_message_title, total) activity?.title = getString(R.string.fragment_room_list_pinned_message_title, total)
if (recyclerView.adapter == null) { if (recyclerView.adapter == null) {
recyclerView.adapter = RoomMessagesAdapter(dataSet, hostname, context) recyclerView.adapter = RoomMessagesAdapter(dataSet, hostname, context!!)
val linearLayoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false) val linearLayoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
recyclerView.layoutManager = linearLayoutManager recyclerView.layoutManager = linearLayoutManager
if (dataSet.size >= 50) { if (dataSet.size >= 50) {
...@@ -132,9 +135,9 @@ class RoomListFragment : Fragment(), RoomListContract.View { ...@@ -132,9 +135,9 @@ class RoomListFragment : Fragment(), RoomListContract.View {
} }
override fun showFavoriteMessages(dataSet: ArrayList<Message>, total: String) { override fun showFavoriteMessages(dataSet: ArrayList<Message>, total: String) {
activity.title = getString(R.string.fragment_room_list_favorite_message_title, total) activity?.title = getString(R.string.fragment_room_list_favorite_message_title, total)
if (recyclerView.adapter == null) { if (recyclerView.adapter == null) {
recyclerView.adapter = RoomMessagesAdapter(dataSet, hostname, context) recyclerView.adapter = RoomMessagesAdapter(dataSet, hostname, context!!)
val linearLayoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false) val linearLayoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
recyclerView.layoutManager = linearLayoutManager recyclerView.layoutManager = linearLayoutManager
if (dataSet.size >= 50) { if (dataSet.size >= 50) {
...@@ -150,7 +153,7 @@ class RoomListFragment : Fragment(), RoomListContract.View { ...@@ -150,7 +153,7 @@ class RoomListFragment : Fragment(), RoomListContract.View {
} }
override fun showFileList(dataSet: ArrayList<Attachment>, total: String) { override fun showFileList(dataSet: ArrayList<Attachment>, total: String) {
activity.title = getString(R.string.fragment_room_list_file_list_title, total) activity?.title = getString(R.string.fragment_room_list_file_list_title, total)
if (recyclerView.adapter == null) { if (recyclerView.adapter == null) {
recyclerView.adapter = RoomFileListAdapter(dataSet) recyclerView.adapter = RoomFileListAdapter(dataSet)
val linearLayoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false) val linearLayoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
...@@ -168,9 +171,9 @@ class RoomListFragment : Fragment(), RoomListContract.View { ...@@ -168,9 +171,9 @@ class RoomListFragment : Fragment(), RoomListContract.View {
} }
override fun showMemberList(dataSet: ArrayList<User>, total: String) { override fun showMemberList(dataSet: ArrayList<User>, total: String) {
activity.title = getString(R.string.fragment_room_list_member_list_title, total) activity?.title = getString(R.string.fragment_room_list_member_list_title, total)
if (recyclerView.adapter == null) { if (recyclerView.adapter == null) {
recyclerView.adapter = RoomMemberListAdapter(dataSet, hostname, context) recyclerView.adapter = RoomMemberListAdapter(dataSet, hostname, context!!)
val linearLayoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false) val linearLayoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
recyclerView.layoutManager = linearLayoutManager recyclerView.layoutManager = linearLayoutManager
if (dataSet.size >= 50) { if (dataSet.size >= 50) {
......
package chat.rocket.android.fragment.oauth; package chat.rocket.android.fragment.oauth;
import io.reactivex.android.schedulers.AndroidSchedulers;
import org.json.JSONObject; import org.json.JSONObject;
import chat.rocket.android.BackgroundLooper; import chat.rocket.android.BackgroundLooper;
...@@ -10,6 +9,7 @@ import chat.rocket.android.helper.Logger; ...@@ -10,6 +9,7 @@ import chat.rocket.android.helper.Logger;
import chat.rocket.android.helper.TextUtils; import chat.rocket.android.helper.TextUtils;
import chat.rocket.android.shared.BasePresenter; import chat.rocket.android.shared.BasePresenter;
import chat.rocket.core.repositories.LoginServiceConfigurationRepository; import chat.rocket.core.repositories.LoginServiceConfigurationRepository;
import io.reactivex.android.schedulers.AndroidSchedulers;
public class OAuthPresenter extends BasePresenter<OAuthContract.View> public class OAuthPresenter extends BasePresenter<OAuthContract.View>
implements OAuthContract.Presenter { implements OAuthContract.Presenter {
......
package chat.rocket.android.fragment.server_config; package chat.rocket.android.fragment.server_config;
import java.util.List; import java.util.List;
import chat.rocket.android.shared.BaseContract; import chat.rocket.android.shared.BaseContract;
import chat.rocket.core.models.LoginServiceConfiguration; import chat.rocket.core.models.LoginServiceConfiguration;
......
...@@ -11,6 +11,7 @@ import android.widget.TextView; ...@@ -11,6 +11,7 @@ import android.widget.TextView;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import chat.rocket.android.R; import chat.rocket.android.R;
import chat.rocket.android.api.MethodCallHelper; import chat.rocket.android.api.MethodCallHelper;
import chat.rocket.android.layouthelper.oauth.OAuthProviderInfo; import chat.rocket.android.layouthelper.oauth.OAuthProviderInfo;
......
...@@ -3,7 +3,6 @@ package chat.rocket.android.fragment.server_config; ...@@ -3,7 +3,6 @@ package chat.rocket.android.fragment.server_config;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import com.hadisatrio.optional.Optional; import com.hadisatrio.optional.Optional;
import io.reactivex.android.schedulers.AndroidSchedulers;
import bolts.Task; import bolts.Task;
import chat.rocket.android.BackgroundLooper; import chat.rocket.android.BackgroundLooper;
...@@ -16,6 +15,7 @@ import chat.rocket.core.PublicSettingsConstants; ...@@ -16,6 +15,7 @@ import chat.rocket.core.PublicSettingsConstants;
import chat.rocket.core.models.PublicSetting; import chat.rocket.core.models.PublicSetting;
import chat.rocket.core.repositories.LoginServiceConfigurationRepository; import chat.rocket.core.repositories.LoginServiceConfigurationRepository;
import chat.rocket.core.repositories.PublicSettingRepository; import chat.rocket.core.repositories.PublicSettingRepository;
import io.reactivex.android.schedulers.AndroidSchedulers;
public class LoginPresenter extends BasePresenter<LoginContract.View> public class LoginPresenter extends BasePresenter<LoginContract.View>
implements LoginContract.Presenter { implements LoginContract.Presenter {
...@@ -85,7 +85,7 @@ public class LoginPresenter extends BasePresenter<LoginContract.View> ...@@ -85,7 +85,7 @@ public class LoginPresenter extends BasePresenter<LoginContract.View>
} }
} }
return null; return null;
}); }, Task.UI_THREAD_EXECUTOR);
} }
private Task<Void> call(String username, String password, Optional<PublicSetting> optional) { private Task<Void> call(String username, String password, Optional<PublicSetting> optional) {
......
...@@ -3,7 +3,6 @@ package chat.rocket.android.fragment.server_config; ...@@ -3,7 +3,6 @@ package chat.rocket.android.fragment.server_config;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import com.hadisatrio.optional.Optional; import com.hadisatrio.optional.Optional;
import io.reactivex.android.schedulers.AndroidSchedulers;
import chat.rocket.android.BackgroundLooper; import chat.rocket.android.BackgroundLooper;
import chat.rocket.android.api.MethodCallHelper; import chat.rocket.android.api.MethodCallHelper;
...@@ -12,6 +11,7 @@ import chat.rocket.android.helper.TextUtils; ...@@ -12,6 +11,7 @@ import chat.rocket.android.helper.TextUtils;
import chat.rocket.android.shared.BasePresenter; import chat.rocket.android.shared.BasePresenter;
import chat.rocket.core.interactors.SessionInteractor; import chat.rocket.core.interactors.SessionInteractor;
import chat.rocket.core.models.Session; import chat.rocket.core.models.Session;
import io.reactivex.android.schedulers.AndroidSchedulers;
public class RetryLoginPresenter extends BasePresenter<RetryLoginContract.View> public class RetryLoginPresenter extends BasePresenter<RetryLoginContract.View>
implements RetryLoginContract.Presenter { implements RetryLoginContract.Presenter {
......
package chat.rocket.android.fragment.server_config; package chat.rocket.android.fragment.server_config;
import bolts.Task;
import chat.rocket.android.api.MethodCallHelper; import chat.rocket.android.api.MethodCallHelper;
import chat.rocket.android.shared.BasePresenter; import chat.rocket.android.shared.BasePresenter;
...@@ -29,6 +30,6 @@ public class TwoStepAuthPresenter extends BasePresenter<TwoStepAuthContract.View ...@@ -29,6 +30,6 @@ public class TwoStepAuthPresenter extends BasePresenter<TwoStepAuthContract.View
view.showError(task.getError().getMessage()); view.showError(task.getError().getMessage());
} }
return null; return null;
}); }, Task.UI_THREAD_EXECUTOR);
} }
} }
...@@ -2,13 +2,14 @@ package chat.rocket.android.fragment.sidebar; ...@@ -2,13 +2,14 @@ package chat.rocket.android.fragment.sidebar;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import bolts.Continuation;
import chat.rocket.core.models.RoomSidebar;
import io.reactivex.Flowable;
import java.util.List; import java.util.List;
import bolts.Continuation;
import chat.rocket.android.shared.BaseContract; import chat.rocket.android.shared.BaseContract;
import chat.rocket.core.models.RoomSidebar;
import chat.rocket.core.models.Spotlight; import chat.rocket.core.models.Spotlight;
import chat.rocket.core.models.User; import chat.rocket.core.models.User;
import io.reactivex.Flowable;
public interface SidebarMainContract { public interface SidebarMainContract {
......
...@@ -151,6 +151,7 @@ public class SidebarMainFragment extends AbstractFragment implements SidebarMain ...@@ -151,6 +151,7 @@ public class SidebarMainFragment extends AbstractFragment implements SidebarMain
adapter.setOnItemClickListener(new RoomListAdapter.OnItemClickListener() { adapter.setOnItemClickListener(new RoomListAdapter.OnItemClickListener() {
@Override @Override
public void onItemClick(RoomSidebar roomSidebar) { public void onItemClick(RoomSidebar roomSidebar) {
searchView.setQuery(null, false);
searchView.clearFocus(); searchView.clearFocus();
presenter.onRoomSelected(roomSidebar); presenter.onRoomSelected(roomSidebar);
} }
......
...@@ -5,11 +5,9 @@ import android.os.Bundle; ...@@ -5,11 +5,9 @@ import android.os.Bundle;
import android.view.View; import android.view.View;
import android.widget.AutoCompleteTextView; import android.widget.AutoCompleteTextView;
import android.widget.TextView; import android.widget.TextView;
import com.hadisatrio.optional.Optional; import com.hadisatrio.optional.Optional;
import com.jakewharton.rxbinding2.widget.RxTextView; import com.jakewharton.rxbinding2.widget.RxTextView;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
import io.realm.Case;
import bolts.Task; import bolts.Task;
import chat.rocket.android.BackgroundLooper; import chat.rocket.android.BackgroundLooper;
...@@ -20,11 +18,14 @@ import chat.rocket.android.helper.Logger; ...@@ -20,11 +18,14 @@ import chat.rocket.android.helper.Logger;
import chat.rocket.android.helper.TextUtils; import chat.rocket.android.helper.TextUtils;
import chat.rocket.android.layouthelper.sidebar.dialog.SuggestUserAdapter; import chat.rocket.android.layouthelper.sidebar.dialog.SuggestUserAdapter;
import chat.rocket.core.interactors.SessionInteractor; import chat.rocket.core.interactors.SessionInteractor;
import chat.rocket.persistence.realm.models.ddp.RealmUser;
import chat.rocket.persistence.realm.RealmAutoCompleteAdapter; import chat.rocket.persistence.realm.RealmAutoCompleteAdapter;
import chat.rocket.persistence.realm.models.ddp.RealmUser;
import chat.rocket.persistence.realm.repositories.RealmServerInfoRepository; import chat.rocket.persistence.realm.repositories.RealmServerInfoRepository;
import chat.rocket.persistence.realm.repositories.RealmSessionRepository; import chat.rocket.persistence.realm.repositories.RealmSessionRepository;
import chat.rocket.persistence.realm.repositories.RealmUserRepository; import chat.rocket.persistence.realm.repositories.RealmUserRepository;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
import io.realm.Case;
/** /**
* add Direct RealmMessage. * add Direct RealmMessage.
......
package chat.rocket.android.helper; package chat.rocket.android.helper;
import com.hadisatrio.optional.Optional; import com.hadisatrio.optional.Optional;
import io.reactivex.Flowable;
import io.reactivex.Single;
import chat.rocket.android.fragment.chatroom.RocketChatAbsoluteUrl; import chat.rocket.android.fragment.chatroom.RocketChatAbsoluteUrl;
import chat.rocket.core.interactors.SessionInteractor; import chat.rocket.core.interactors.SessionInteractor;
import chat.rocket.core.repositories.ServerInfoRepository; import chat.rocket.core.repositories.ServerInfoRepository;
import chat.rocket.core.repositories.UserRepository; import chat.rocket.core.repositories.UserRepository;
import io.reactivex.Flowable;
import io.reactivex.Single;
public class AbsoluteUrlHelper { public class AbsoluteUrlHelper {
......
...@@ -6,9 +6,11 @@ import android.text.format.DateFormat; ...@@ -6,9 +6,11 @@ import android.text.format.DateFormat;
import java.text.ParseException; import java.text.ParseException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Calendar; import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar; import java.util.GregorianCalendar;
import java.util.Locale; import java.util.Locale;
import java.util.TimeZone; import java.util.TimeZone;
import chat.rocket.android.log.RCLog; import chat.rocket.android.log.RCLog;
/** /**
...@@ -55,7 +57,7 @@ public class DateTime { ...@@ -55,7 +57,7 @@ public class DateTime {
case DAY: case DAY:
return DAY_FORMAT.format(cal.getTime()); return DAY_FORMAT.format(cal.getTime());
case DATE: case DATE:
return DATE_FORMAT.format(cal.getTime()); return getDateFormat(cal.getTime());
case TIME: case TIME:
return TIME_FORMAT.format(cal.getTime()); return TIME_FORMAT.format(cal.getTime());
case DATE_TIME: case DATE_TIME:
...@@ -80,6 +82,22 @@ public class DateTime { ...@@ -80,6 +82,22 @@ public class DateTime {
} }
} }
private static String getDateFormat(Date dateTime) {
Calendar calendar = Calendar.getInstance();
calendar.setTime(dateTime);
Calendar today = Calendar.getInstance();
Calendar yesterday = Calendar.getInstance();
yesterday.add(Calendar.DATE, -1);
if (calendar.get(Calendar.YEAR) == today.get(Calendar.YEAR) && calendar.get(Calendar.DAY_OF_YEAR) == today.get(Calendar.DAY_OF_YEAR)) {
return "Today";
} else if (calendar.get(Calendar.YEAR) == yesterday.get(Calendar.YEAR) && calendar.get(Calendar.DAY_OF_YEAR) == yesterday.get(Calendar.DAY_OF_YEAR)) {
return "Yesterday";
} else {
return DATE_FORMAT.format(dateTime);
}
}
/** /**
* parse datetime string to ms. * parse datetime string to ms.
*/ */
......
...@@ -8,16 +8,18 @@ import android.os.ParcelFileDescriptor; ...@@ -8,16 +8,18 @@ import android.os.ParcelFileDescriptor;
import android.provider.OpenableColumns; import android.provider.OpenableColumns;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.webkit.MimeTypeMap; import android.webkit.MimeTypeMap;
import org.json.JSONObject; import org.json.JSONObject;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.util.UUID; import java.util.UUID;
import chat.rocket.android.log.RCLog; import chat.rocket.android.log.RCLog;
import chat.rocket.core.SyncState; import chat.rocket.core.SyncState;
import chat.rocket.persistence.realm.RealmHelper;
import chat.rocket.persistence.realm.models.ddp.RealmPublicSetting; import chat.rocket.persistence.realm.models.ddp.RealmPublicSetting;
import chat.rocket.persistence.realm.models.internal.FileUploading; import chat.rocket.persistence.realm.models.internal.FileUploading;
import chat.rocket.persistence.realm.RealmHelper;
/** /**
* utility class for uploading file. * utility class for uploading file.
......
package chat.rocket.android.helper; package chat.rocket.android.helper;
import io.realm.Realm;
import io.realm.RealmResults;
import java.util.List; import java.util.List;
import chat.rocket.persistence.realm.models.ddp.RealmPublicSetting;
import chat.rocket.core.PublicSettingsConstants; import chat.rocket.core.PublicSettingsConstants;
import chat.rocket.persistence.realm.models.ddp.RealmPublicSetting;
import io.realm.Realm;
import io.realm.RealmResults;
/** /**
* utility class for getting value comprehensibly from public settings list. * utility class for getting value comprehensibly from public settings list.
......
package chat.rocket.android.helper; package chat.rocket.android.helper;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import rx.Observable;
import rx.functions.Func1; import io.reactivex.Flowable;
import io.reactivex.functions.Function;
/** /**
* Rx operator and so on. * Rx operator and so on.
*/ */
public class RxHelper { public class RxHelper {
public static Func1<Observable<? extends Throwable>, Observable<?>> exponentialBackoff( public static Function<Flowable<? extends Throwable>, Flowable<?>> exponentialBackoff(
int maxRetryCount, long base, TimeUnit unit) { int maxRetryCount, long base, TimeUnit unit) {
// ref: https://github.com/ReactiveX/RxJava/blob/a8ba158839b67246a742b6f1531995ffd7545c08/src/main/java/io/reactivex/Observable.java#L9601 // ref: https://github.com/ReactiveX/RxJava/blob/a8ba158839b67246a742b6f1531995ffd7545c08/src/main/java/io/reactivex/Observable.java#L9601
return attempts -> attempts return attempts -> attempts
.zipWith(Observable.range(0, maxRetryCount), (error, retryCount) -> retryCount) .zipWith(Flowable.range(0, maxRetryCount), (error, retryCount) -> retryCount)
.flatMap(retryCount -> Observable.timer(base * (long) Math.pow(2, retryCount), unit)); .flatMap(retryCount -> Flowable.timer(base * (long) Math.pow(2, retryCount), unit));
} }
} }
...@@ -2,9 +2,8 @@ package chat.rocket.android.helper; ...@@ -2,9 +2,8 @@ package chat.rocket.android.helper;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import io.reactivex.Flowable;
import chat.rocket.android.api.rest.ServerPolicyApi; import chat.rocket.android.api.rest.ServerPolicyApi;
import io.reactivex.Flowable;
public class ServerPolicyApiValidationHelper { public class ServerPolicyApiValidationHelper {
......
...@@ -2,9 +2,10 @@ package chat.rocket.android.helper; ...@@ -2,9 +2,10 @@ package chat.rocket.android.helper;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import io.reactivex.Flowable;
import org.json.JSONObject; import org.json.JSONObject;
import io.reactivex.Flowable;
public class ServerPolicyHelper { public class ServerPolicyHelper {
private static final String DEFAULT_HOST = ".rocket.chat"; private static final String DEFAULT_HOST = ".rocket.chat";
...@@ -12,7 +13,7 @@ public class ServerPolicyHelper { ...@@ -12,7 +13,7 @@ public class ServerPolicyHelper {
public static String enforceHostname(String hostname) { public static String enforceHostname(String hostname) {
if (hostname == null) { if (hostname == null) {
return "demo.rocket.chat"; return "open.rocket.chat";
} }
return removeTrailingSlash(removeProtocol(enforceDefaultHost(hostname))); return removeTrailingSlash(removeProtocol(enforceDefaultHost(hostname)));
......
...@@ -4,6 +4,7 @@ import android.support.annotation.Nullable; ...@@ -4,6 +4,7 @@ import android.support.annotation.Nullable;
import android.view.View; import android.view.View;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import chat.rocket.android.R; import chat.rocket.android.R;
import chat.rocket.android.helper.DateTime; import chat.rocket.android.helper.DateTime;
import chat.rocket.android.helper.TextUtils; import chat.rocket.android.helper.TextUtils;
...@@ -44,7 +45,8 @@ public abstract class AbstractMessageViewHolder extends ModelViewHolder<PairedMe ...@@ -44,7 +45,8 @@ public abstract class AbstractMessageViewHolder extends ModelViewHolder<PairedMe
* bind the view model. * bind the view model.
*/ */
public final void bind(PairedMessage pairedMessage, boolean autoloadImages) { public final void bind(PairedMessage pairedMessage, boolean autoloadImages) {
if (pairedMessage.target.getSyncState() == SyncState.FAILED) { if (pairedMessage.target.getSyncState() == SyncState.FAILED ||
pairedMessage.target.getSyncState() == SyncState.DELETE_FAILED) {
errorImageView.setVisibility(View.VISIBLE); errorImageView.setVisibility(View.VISIBLE);
} else { } else {
errorImageView.setVisibility(View.GONE); errorImageView.setVisibility(View.GONE);
......
...@@ -7,6 +7,7 @@ import android.view.View; ...@@ -7,6 +7,7 @@ import android.view.View;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import chat.rocket.android.R; import chat.rocket.android.R;
import chat.rocket.android.helper.TextUtils; import chat.rocket.android.helper.TextUtils;
import chat.rocket.android.layouthelper.ExtModelListAdapter; import chat.rocket.android.layouthelper.ExtModelListAdapter;
......
package chat.rocket.android.layouthelper.chatroom; package chat.rocket.android.layouthelper.chatroom;
import android.view.View; import android.view.View;
import chat.rocket.android.R; import chat.rocket.android.R;
import chat.rocket.android.renderer.MessageRenderer; import chat.rocket.android.renderer.MessageRenderer;
import chat.rocket.android.widget.AbsoluteUrl; import chat.rocket.android.widget.AbsoluteUrl;
......
...@@ -37,7 +37,8 @@ public class MessagePopup { ...@@ -37,7 +37,8 @@ public class MessagePopup {
private static final Action QUOTE_ACTION_INFO = new Action("Quote", null, true); private static final Action QUOTE_ACTION_INFO = new Action("Quote", null, true);
private static final Action EDIT_ACTION_INFO = new Action("Edit", null, true); private static final Action EDIT_ACTION_INFO = new Action("Edit", null, true);
private static final Action COPY_ACTION_INFO = new Action("Copy", null, true); private static final Action COPY_ACTION_INFO = new Action("Copy", null, true);
private final List<Action> defaultActions = new ArrayList<>(4); private static final Action DELETE_ACTION_INFO = new Action("Delete", null, false);
private final List<Action> defaultActions = new ArrayList<>(5);
private final List<Action> otherActions = new ArrayList<>(); private final List<Action> otherActions = new ArrayList<>();
private Message message; private Message message;
private CompositeDisposable compositeDisposable = new CompositeDisposable(); private CompositeDisposable compositeDisposable = new CompositeDisposable();
...@@ -73,6 +74,7 @@ public class MessagePopup { ...@@ -73,6 +74,7 @@ public class MessagePopup {
.subscribe( .subscribe(
pair -> { pair -> {
EDIT_ACTION_INFO.allowed = pair.second; EDIT_ACTION_INFO.allowed = pair.second;
DELETE_ACTION_INFO.allowed = pair.second;
List<Action> allActions = singleton.defaultActions; List<Action> allActions = singleton.defaultActions;
List<Action> allowedActions = new ArrayList<>(3); List<Action> allowedActions = new ArrayList<>(3);
for (int i = 0; i < allActions.size(); i++) { for (int i = 0; i < allActions.size(); i++) {
...@@ -110,6 +112,7 @@ public class MessagePopup { ...@@ -110,6 +112,7 @@ public class MessagePopup {
singleton.defaultActions.add(QUOTE_ACTION_INFO); singleton.defaultActions.add(QUOTE_ACTION_INFO);
singleton.defaultActions.add(EDIT_ACTION_INFO); singleton.defaultActions.add(EDIT_ACTION_INFO);
singleton.defaultActions.add(COPY_ACTION_INFO); singleton.defaultActions.add(COPY_ACTION_INFO);
singleton.defaultActions.add(DELETE_ACTION_INFO);
} }
public static MessagePopup take(Message message) { public static MessagePopup take(Message message) {
...@@ -163,6 +166,11 @@ public class MessagePopup { ...@@ -163,6 +166,11 @@ public class MessagePopup {
return singleton; return singleton;
} }
public MessagePopup setDeleteAction(ActionListener actionListener) {
DELETE_ACTION_INFO.actionListener= actionListener;
return singleton;
}
public MessagePopup setQuoteAction(ActionListener actionListener) { public MessagePopup setQuoteAction(ActionListener actionListener) {
QUOTE_ACTION_INFO.actionListener = actionListener; QUOTE_ACTION_INFO.actionListener = actionListener;
return singleton; return singleton;
......
...@@ -7,10 +7,13 @@ import android.view.ViewGroup ...@@ -7,10 +7,13 @@ import android.view.ViewGroup
import android.widget.TextView import android.widget.TextView
import chat.rocket.android.R import chat.rocket.android.R
import chat.rocket.android.helper.DateTime import chat.rocket.android.helper.DateTime
import chat.rocket.android.helper.Logger
import chat.rocket.android.log.RCLog
import chat.rocket.android.widget.message.RocketChatMessageAttachmentsLayout import chat.rocket.android.widget.message.RocketChatMessageAttachmentsLayout
import chat.rocket.core.models.Attachment import chat.rocket.core.models.Attachment
import kotlinx.android.synthetic.main.day.view.* import kotlinx.android.synthetic.main.day.view.*
import kotlinx.android.synthetic.main.item_room_file.view.* import kotlinx.android.synthetic.main.item_room_file.view.*
import java.lang.IllegalArgumentException
import java.sql.Timestamp import java.sql.Timestamp
/** /**
...@@ -26,8 +29,16 @@ class RoomFileListAdapter(private var dataSet: List<Attachment>) : RecyclerView. ...@@ -26,8 +29,16 @@ class RoomFileListAdapter(private var dataSet: List<Attachment>) : RecyclerView.
override fun onBindViewHolder(holder: ViewHolder, position: Int) { override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val attachment = dataSet[position] val attachment = dataSet[position]
holder.newDay.text = DateTime.fromEpocMs(Timestamp.valueOf(attachment.timestamp).time, DateTime.Format.DATE) val timestamp: Timestamp?
try {
timestamp = Timestamp.valueOf(attachment.timestamp)
// If we don't have a timestamp we can parse let's be safe and stop here.
holder.newDay.text = DateTime.fromEpocMs(timestamp.time, DateTime.Format.DATE)
holder.attachment.appendAttachmentView(attachment, true, false) holder.attachment.appendAttachmentView(attachment, true, false)
} catch (e: IllegalArgumentException) {
RCLog.e(e)
Logger.report(e)
}
} }
override fun getItemCount(): Int = dataSet.size override fun getItemCount(): Int = dataSet.size
......
...@@ -7,13 +7,14 @@ import android.view.View; ...@@ -7,13 +7,14 @@ import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import java.util.List; import java.util.List;
import chat.rocket.android.R; import chat.rocket.android.R;
import chat.rocket.android.helper.TextUtils; import chat.rocket.android.helper.TextUtils;
import chat.rocket.android.renderer.UserRenderer;
import chat.rocket.android.widget.AbsoluteUrl; import chat.rocket.android.widget.AbsoluteUrl;
import chat.rocket.core.models.User; import chat.rocket.core.models.User;
import chat.rocket.persistence.realm.models.ddp.RealmUser;
import chat.rocket.persistence.realm.RealmHelper; import chat.rocket.persistence.realm.RealmHelper;
import chat.rocket.android.renderer.UserRenderer; import chat.rocket.persistence.realm.models.ddp.RealmUser;
/** /**
* RecyclerView adapter for UsersOfRooms. * RecyclerView adapter for UsersOfRooms.
......
...@@ -2,9 +2,10 @@ package chat.rocket.android.layouthelper.chatroom.roomlist; ...@@ -2,9 +2,10 @@ package chat.rocket.android.layouthelper.chatroom.roomlist;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import chat.rocket.core.models.RoomSidebar;
import java.util.List; import java.util.List;
import chat.rocket.core.models.Room; import chat.rocket.core.models.Room;
import chat.rocket.core.models.RoomSidebar;
public class ChannelRoomListHeader implements RoomListHeader { public class ChannelRoomListHeader implements RoomListHeader {
......
...@@ -2,9 +2,10 @@ package chat.rocket.android.layouthelper.chatroom.roomlist; ...@@ -2,9 +2,10 @@ package chat.rocket.android.layouthelper.chatroom.roomlist;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import chat.rocket.core.models.RoomSidebar;
import java.util.List; import java.util.List;
import chat.rocket.core.models.Room; import chat.rocket.core.models.Room;
import chat.rocket.core.models.RoomSidebar;
public class DirectMessageRoomListHeader implements RoomListHeader { public class DirectMessageRoomListHeader implements RoomListHeader {
......
...@@ -2,9 +2,10 @@ package chat.rocket.android.layouthelper.chatroom.roomlist; ...@@ -2,9 +2,10 @@ package chat.rocket.android.layouthelper.chatroom.roomlist;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import chat.rocket.core.models.RoomSidebar;
import java.util.List; import java.util.List;
import chat.rocket.core.models.RoomSidebar;
public class FavoriteRoomListHeader implements RoomListHeader { public class FavoriteRoomListHeader implements RoomListHeader {
private final String title; private final String title;
......
...@@ -5,14 +5,15 @@ import android.support.v7.widget.RecyclerView; ...@@ -5,14 +5,15 @@ import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.ViewGroup; import android.view.ViewGroup;
import chat.rocket.core.models.RoomSidebar;
import chat.rocket.core.models.Spotlight;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import chat.rocket.android.R; import chat.rocket.android.R;
import chat.rocket.android.widget.internal.RoomListItemView; import chat.rocket.android.widget.internal.RoomListItemView;
import chat.rocket.core.models.RoomSidebar;
import chat.rocket.core.models.Spotlight;
public class RoomListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { public class RoomListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
......
...@@ -2,9 +2,10 @@ package chat.rocket.android.layouthelper.chatroom.roomlist; ...@@ -2,9 +2,10 @@ package chat.rocket.android.layouthelper.chatroom.roomlist;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import chat.rocket.core.models.RoomSidebar;
import java.util.List; import java.util.List;
import chat.rocket.core.models.RoomSidebar;
public interface RoomListHeader { public interface RoomListHeader {
String getTitle(); String getTitle();
......
...@@ -2,9 +2,10 @@ package chat.rocket.android.layouthelper.chatroom.roomlist; ...@@ -2,9 +2,10 @@ package chat.rocket.android.layouthelper.chatroom.roomlist;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import chat.rocket.core.models.RoomSidebar;
import java.util.List; import java.util.List;
import chat.rocket.core.models.RoomSidebar;
public class UnreadRoomListHeader implements RoomListHeader { public class UnreadRoomListHeader implements RoomListHeader {
private final String title; private final String title;
......
...@@ -3,6 +3,7 @@ package chat.rocket.android.layouthelper.oauth; ...@@ -3,6 +3,7 @@ package chat.rocket.android.layouthelper.oauth;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import chat.rocket.android.R; import chat.rocket.android.R;
import chat.rocket.android.fragment.oauth.AbstractOAuthFragment; import chat.rocket.android.fragment.oauth.AbstractOAuthFragment;
import chat.rocket.android.fragment.oauth.FacebookOAuthFragment; import chat.rocket.android.fragment.oauth.FacebookOAuthFragment;
......
...@@ -5,11 +5,12 @@ import android.view.View; ...@@ -5,11 +5,12 @@ import android.view.View;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import chat.rocket.android.R; import chat.rocket.android.R;
import chat.rocket.android.renderer.UserRenderer;
import chat.rocket.android.widget.AbsoluteUrl; import chat.rocket.android.widget.AbsoluteUrl;
import chat.rocket.persistence.realm.models.ddp.RealmUser;
import chat.rocket.persistence.realm.RealmAutoCompleteAdapter; import chat.rocket.persistence.realm.RealmAutoCompleteAdapter;
import chat.rocket.android.renderer.UserRenderer; import chat.rocket.persistence.realm.models.ddp.RealmUser;
/** /**
* adapter to suggest user names. * adapter to suggest user names.
......
...@@ -5,8 +5,8 @@ import android.content.BroadcastReceiver; ...@@ -5,8 +5,8 @@ import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.util.Log;
import android.support.v4.app.RemoteInput; import android.support.v4.app.RemoteInput;
import android.util.Log;
import chat.rocket.android.push.gcm.GCMIntentService; import chat.rocket.android.push.gcm.GCMIntentService;
......
This diff is collapsed.
package chat.rocket.android.push.gcm; package chat.rocket.android.push.gcm;
import com.google.android.gms.gcm.GcmListenerService;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.content.Context; import android.content.Context;
import android.content.res.Resources; import android.content.res.Resources;
import android.os.Bundle; import android.os.Bundle;
import android.util.Log; import android.util.Log;
import com.google.android.gms.gcm.GcmListenerService;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Iterator; import java.util.Iterator;
import chat.rocket.android.push.PushConstants; import chat.rocket.android.push.PushConstants;
import chat.rocket.android.push.PushNotificationHandler; import chat.rocket.android.push.PushManager;
@SuppressLint("NewApi") @SuppressLint("NewApi")
public class GCMIntentService extends GcmListenerService implements PushConstants { public class GCMIntentService extends GcmListenerService implements PushConstants {
...@@ -33,9 +35,7 @@ public class GCMIntentService extends GcmListenerService implements PushConstant ...@@ -33,9 +35,7 @@ public class GCMIntentService extends GcmListenerService implements PushConstant
extras = normalizeExtras(applicationContext, extras); extras = normalizeExtras(applicationContext, extras);
PushNotificationHandler pushNotificationHandler = new PushNotificationHandler(); PushManager.INSTANCE.handle(applicationContext, extras);
pushNotificationHandler.showNotificationIfPossible(applicationContext, extras);
} }
/* /*
......
...@@ -3,13 +3,14 @@ package chat.rocket.android.push.gcm; ...@@ -3,13 +3,14 @@ package chat.rocket.android.push.gcm;
import com.google.android.gms.iid.InstanceIDListenerService; import com.google.android.gms.iid.InstanceIDListenerService;
import java.util.List; import java.util.List;
import chat.rocket.android.helper.GcmPushSettingHelper; import chat.rocket.android.helper.GcmPushSettingHelper;
import chat.rocket.persistence.realm.models.ddp.RealmPublicSetting;
import chat.rocket.persistence.realm.models.internal.GcmPushRegistration;
import chat.rocket.persistence.realm.RealmHelper;
import chat.rocket.persistence.realm.RealmStore;
import chat.rocket.android.service.ConnectivityManager; import chat.rocket.android.service.ConnectivityManager;
import chat.rocket.core.models.ServerInfo; import chat.rocket.core.models.ServerInfo;
import chat.rocket.persistence.realm.RealmHelper;
import chat.rocket.persistence.realm.RealmStore;
import chat.rocket.persistence.realm.models.ddp.RealmPublicSetting;
import chat.rocket.persistence.realm.models.internal.GcmPushRegistration;
public class GcmInstanceIDListenerService extends InstanceIDListenerService { public class GcmInstanceIDListenerService extends InstanceIDListenerService {
......
package chat.rocket.android.service; package chat.rocket.android.service;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import java.util.List; import java.util.List;
import chat.rocket.core.models.ServerInfo; import chat.rocket.core.models.ServerInfo;
import rx.Observable; import io.reactivex.Flowable;
import rx.Single; import io.reactivex.Single;
/** /**
* interfaces used for Activity/Fragment and other UI-related logic. * interfaces used for Activity/Fragment and other UI-related logic.
...@@ -21,5 +23,9 @@ public interface ConnectivityManagerApi { ...@@ -21,5 +23,9 @@ public interface ConnectivityManagerApi {
List<ServerInfo> getServerList(); List<ServerInfo> getServerList();
Observable<ServerConnectivity> getServerConnectivityAsObservable(); Flowable<ServerConnectivity> getServerConnectivityAsObservable();
int getConnectivityState(@NonNull String hostname);
void resetConnectivityStateList();
} }
package chat.rocket.android.service; package chat.rocket.android.service;
import java.util.List; import java.util.List;
import chat.rocket.core.models.ServerInfo; import chat.rocket.core.models.ServerInfo;
/** /**
* interfaces used for RocketChatService and RocketChatwebSocketThread. * interfaces used for RocketChatService and RocketChatwebSocketThread.
*/ */
/*package*/ interface ConnectivityManagerInternal { /*package*/ interface ConnectivityManagerInternal {
int REASON_CLOSED_BY_USER = 101;
int REASON_NETWORK_ERROR = 102;
int REASON_SERVER_ERROR = 103;
int REASON_UNKNOWN = 104;
void resetConnectivityStateList(); void resetConnectivityStateList();
......
package chat.rocket.android.service; package chat.rocket.android.service;
import rx.Single; import io.reactivex.Single;
public interface ConnectivityServiceInterface { public interface ConnectivityServiceInterface {
Single<Boolean> ensureConnectionToServer(String hostname); Single<Boolean> ensureConnectionToServer(String hostname);
......
package chat.rocket.android.service;
import chat.rocket.android.api.DDPClientWrapper;
/**
* reference to get fresh DDPClient instance.
*/
public interface DDPClientRef {
DDPClientWrapper get();
}
package chat.rocket.android.service; package chat.rocket.android.service;
import android.annotation.SuppressLint;
import android.content.ComponentName; import android.content.ComponentName;
import android.content.Context; import android.content.Context;
import android.content.ServiceConnection; import android.content.ServiceConnection;
import android.os.IBinder; import android.os.IBinder;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import java.util.ArrayList; import java.util.ArrayList;
...@@ -12,22 +14,26 @@ import java.util.Map; ...@@ -12,22 +14,26 @@ import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import chat.rocket.android.RocketChatCache;
import chat.rocket.android.helper.RxHelper; import chat.rocket.android.helper.RxHelper;
import chat.rocket.android.log.RCLog; import chat.rocket.android.log.RCLog;
import chat.rocket.android_ddp.DDPClient;
import chat.rocket.core.models.ServerInfo; import chat.rocket.core.models.ServerInfo;
import chat.rocket.persistence.realm.models.RealmBasedServerInfo; import chat.rocket.persistence.realm.models.RealmBasedServerInfo;
import hugo.weaving.DebugLog; import hugo.weaving.DebugLog;
import rx.Observable; import io.reactivex.BackpressureStrategy;
import rx.Single; import io.reactivex.Flowable;
import rx.subjects.PublishSubject; import io.reactivex.Single;
import io.reactivex.schedulers.Schedulers;
import io.reactivex.subjects.BehaviorSubject;
/** /**
* Connectivity management implementation. * Connectivity management implementation.
*/ */
/*package*/ class RealmBasedConnectivityManager /*package*/ class RealmBasedConnectivityManager
implements ConnectivityManagerApi, ConnectivityManagerInternal { implements ConnectivityManagerApi, ConnectivityManagerInternal {
private final ConcurrentHashMap<String, Integer> serverConnectivityList = new ConcurrentHashMap<>(); private volatile ConcurrentHashMap<String, Integer> serverConnectivityList = new ConcurrentHashMap<>();
private final PublishSubject<ServerConnectivity> connectivitySubject = PublishSubject.create(); private volatile BehaviorSubject<ServerConnectivity> connectivitySubject = BehaviorSubject.createDefault(ServerConnectivity.CONNECTED);
private Context appContext; private Context appContext;
private final ServiceConnection serviceConnection = new ServiceConnection() { private final ServiceConnection serviceConnection = new ServiceConnection() {
@Override @Override
...@@ -64,16 +70,27 @@ import rx.subjects.PublishSubject; ...@@ -64,16 +70,27 @@ import rx.subjects.PublishSubject;
} }
} }
@SuppressLint("RxLeakedSubscription")
@DebugLog @DebugLog
@Override @Override
public void ensureConnections() { public void ensureConnections() {
for (String hostname : serverConnectivityList.keySet()) { String hostname = new RocketChatCache(appContext).getSelectedServerHostname();
connectToServerIfNeeded(hostname, true/* force connect */) if (hostname == null) {
.subscribe(_val -> { return;
}, RCLog::e);
} }
connectToServerIfNeeded(hostname, true/* force connect */)
.subscribeOn(Schedulers.io())
.subscribe(connected -> {
if (!connected) {
notifyConnectionLost(hostname, DDPClient.REASON_NETWORK_ERROR);
}
}, error -> {
RCLog.e(error);
notifyConnectionLost(hostname, DDPClient.REASON_NETWORK_ERROR);
});
} }
@SuppressLint("RxLeakedSubscription")
@Override @Override
public void addOrUpdateServer(String hostname, @Nullable String name, boolean insecure) { public void addOrUpdateServer(String hostname, @Nullable String name, boolean insecure) {
RealmBasedServerInfo.addOrUpdate(hostname, name, insecure); RealmBasedServerInfo.addOrUpdate(hostname, name, insecure);
...@@ -81,10 +98,11 @@ import rx.subjects.PublishSubject; ...@@ -81,10 +98,11 @@ import rx.subjects.PublishSubject;
serverConnectivityList.put(hostname, ServerConnectivity.STATE_DISCONNECTED); serverConnectivityList.put(hostname, ServerConnectivity.STATE_DISCONNECTED);
} }
connectToServerIfNeeded(hostname, false) connectToServerIfNeeded(hostname, false)
.subscribe(_val -> { .subscribe(connected -> {
}, RCLog::e); }, RCLog::e);
} }
@SuppressLint("RxLeakedSubscription")
@Override @Override
public void removeServer(String hostname) { public void removeServer(String hostname) {
RealmBasedServerInfo.remove(hostname); RealmBasedServerInfo.remove(hostname);
...@@ -121,7 +139,9 @@ import rx.subjects.PublishSubject; ...@@ -121,7 +139,9 @@ import rx.subjects.PublishSubject;
@DebugLog @DebugLog
@Override @Override
public void notifyConnectionEstablished(String hostname, String session) { public void notifyConnectionEstablished(String hostname, String session) {
if (session != null) {
RealmBasedServerInfo.updateSession(hostname, session); RealmBasedServerInfo.updateSession(hostname, session);
}
serverConnectivityList.put(hostname, ServerConnectivity.STATE_CONNECTED); serverConnectivityList.put(hostname, ServerConnectivity.STATE_CONNECTED);
connectivitySubject.onNext( connectivitySubject.onNext(
new ServerConnectivity(hostname, ServerConnectivity.STATE_CONNECTED)); new ServerConnectivity(hostname, ServerConnectivity.STATE_CONNECTED));
...@@ -129,10 +149,10 @@ import rx.subjects.PublishSubject; ...@@ -129,10 +149,10 @@ import rx.subjects.PublishSubject;
@DebugLog @DebugLog
@Override @Override
public void notifyConnectionLost(String hostname, int reason) { public void notifyConnectionLost(String hostname, int code) {
serverConnectivityList.put(hostname, ServerConnectivity.STATE_DISCONNECTED); serverConnectivityList.put(hostname, ServerConnectivity.STATE_DISCONNECTED);
connectivitySubject.onNext( connectivitySubject.onNext(
new ServerConnectivity(hostname, ServerConnectivity.STATE_DISCONNECTED)); new ServerConnectivity(hostname, ServerConnectivity.STATE_DISCONNECTED, code));
} }
@DebugLog @DebugLog
...@@ -144,14 +164,23 @@ import rx.subjects.PublishSubject; ...@@ -144,14 +164,23 @@ import rx.subjects.PublishSubject;
} }
@Override @Override
public Observable<ServerConnectivity> getServerConnectivityAsObservable() { public Flowable<ServerConnectivity> getServerConnectivityAsObservable() {
return Observable.concat(Observable.from(getCurrentConnectivityList()), connectivitySubject); return connectivitySubject.toFlowable(BackpressureStrategy.LATEST);
}
@Override
public int getConnectivityState(@NonNull String hostname) {
return serverConnectivityList.get(hostname);
} }
@DebugLog @DebugLog
private Single<Boolean> connectToServerIfNeeded(String hostname, boolean forceConnect) { private Single<Boolean> connectToServerIfNeeded(String hostname, boolean forceConnect) {
return Single.defer(() -> { return Single.defer(() -> {
final int connectivity = serverConnectivityList.get(hostname); Integer state = serverConnectivityList.get(hostname);
if (state == null) {
state = ServerConnectivity.STATE_DISCONNECTED;
}
final int connectivity = state;
if (!forceConnect && connectivity == ServerConnectivity.STATE_CONNECTED) { if (!forceConnect && connectivity == ServerConnectivity.STATE_CONNECTED) {
return Single.just(true); return Single.just(true);
} }
...@@ -161,13 +190,13 @@ import rx.subjects.PublishSubject; ...@@ -161,13 +190,13 @@ import rx.subjects.PublishSubject;
.flatMap(_val -> connectToServerIfNeeded(hostname, forceConnect)); .flatMap(_val -> connectToServerIfNeeded(hostname, forceConnect));
} }
if (connectivity == ServerConnectivity.STATE_CONNECTING) { if (connectivity == ServerConnectivity.STATE_DISCONNECTED) {
return waitForConnected(hostname); // notifyConnecting(hostname);
} }
return connectToServer(hostname) return connectToServer(hostname)
.doOnError(RCLog::e) .retry(exception -> exception instanceof ThreadLooperNotPreparedException)
.retryWhen(RxHelper.exponentialBackoff(Integer.MAX_VALUE, 500, TimeUnit.MILLISECONDS)); .onErrorResumeNext(Single.just(false));
}); });
} }
...@@ -180,7 +209,7 @@ import rx.subjects.PublishSubject; ...@@ -180,7 +209,7 @@ import rx.subjects.PublishSubject;
if (connectivity == ServerConnectivity.STATE_CONNECTING) { if (connectivity == ServerConnectivity.STATE_CONNECTING) {
return waitForConnected(hostname) return waitForConnected(hostname)
.onErrorReturn(err -> true) .doOnError(err -> notifyConnectionLost(hostname, DDPClient.REASON_NETWORK_ERROR))
.flatMap(_val -> disconnectFromServerIfNeeded(hostname)); .flatMap(_val -> disconnectFromServerIfNeeded(hostname));
} }
...@@ -189,8 +218,7 @@ import rx.subjects.PublishSubject; ...@@ -189,8 +218,7 @@ import rx.subjects.PublishSubject;
} }
return disconnectFromServer(hostname) return disconnectFromServer(hostname)
//.doOnError(RCLog::e) .retryWhen(RxHelper.exponentialBackoff(1, 500, TimeUnit.MILLISECONDS));
.retryWhen(RxHelper.exponentialBackoff(3, 500, TimeUnit.MILLISECONDS));
}); });
} }
...@@ -202,7 +230,7 @@ import rx.subjects.PublishSubject; ...@@ -202,7 +230,7 @@ import rx.subjects.PublishSubject;
.filter(state -> .filter(state ->
state == ServerConnectivity.STATE_CONNECTED state == ServerConnectivity.STATE_CONNECTED
|| state == ServerConnectivity.STATE_DISCONNECTED) || state == ServerConnectivity.STATE_DISCONNECTED)
.first() .firstElement()
.toSingle() .toSingle()
.flatMap(state -> .flatMap(state ->
state == ServerConnectivity.STATE_CONNECTED state == ServerConnectivity.STATE_CONNECTED
...@@ -216,7 +244,7 @@ import rx.subjects.PublishSubject; ...@@ -216,7 +244,7 @@ import rx.subjects.PublishSubject;
.filter(serverConnectivity -> hostname.equals(serverConnectivity.hostname)) .filter(serverConnectivity -> hostname.equals(serverConnectivity.hostname))
.map(serverConnectivity -> serverConnectivity.state) .map(serverConnectivity -> serverConnectivity.state)
.filter(state -> state == ServerConnectivity.STATE_DISCONNECTED) .filter(state -> state == ServerConnectivity.STATE_DISCONNECTED)
.first() .firstElement()
.toSingle() .toSingle()
.map(state -> true); .map(state -> true);
} }
...@@ -231,13 +259,13 @@ import rx.subjects.PublishSubject; ...@@ -231,13 +259,13 @@ import rx.subjects.PublishSubject;
if (serverConnectivityList.get(hostname) != ServerConnectivity.STATE_CONNECTED) { if (serverConnectivityList.get(hostname) != ServerConnectivity.STATE_CONNECTED) {
// Mark as CONNECTING except for the case [forceConnect && connected] because // Mark as CONNECTING except for the case [forceConnect && connected] because
// ensureConnectionToServer doesn't notify ConnectionEstablished/Lost is already connected. // ensureConnectionToServer doesn't notify ConnectionEstablished/Lost is already connected.
serverConnectivityList.put(hostname, ServerConnectivity.STATE_CONNECTING); // serverConnectivityList.put(hostname, ServerConnectivity.STATE_CONNECTING);
} }
if (serviceInterface != null) { if (serviceInterface != null) {
return serviceInterface.ensureConnectionToServer(hostname); return serviceInterface.ensureConnectionToServer(hostname);
} else { } else {
return Single.error(new IllegalStateException("not prepared")); return Single.error(new ThreadLooperNotPreparedException("not prepared"));
} }
}); });
} }
...@@ -258,4 +286,10 @@ import rx.subjects.PublishSubject; ...@@ -258,4 +286,10 @@ import rx.subjects.PublishSubject;
} }
}); });
} }
private static class ThreadLooperNotPreparedException extends IllegalStateException {
ThreadLooperNotPreparedException(String message) {
super(message);
}
}
} }
\ No newline at end of file
...@@ -8,15 +8,15 @@ import android.os.Binder; ...@@ -8,15 +8,15 @@ import android.os.Binder;
import android.os.IBinder; import android.os.IBinder;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import java.util.HashMap;
import java.util.concurrent.Semaphore; import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import chat.rocket.android.helper.Logger; import chat.rocket.android.helper.Logger;
import chat.rocket.android.log.RCLog;
import chat.rocket.persistence.realm.RealmStore; import chat.rocket.persistence.realm.RealmStore;
import hugo.weaving.DebugLog; import hugo.weaving.DebugLog;
import rx.Observable; import io.reactivex.Observable;
import rx.Single; import io.reactivex.Single;
/** /**
* Background service for Rocket.Chat.Application class. * Background service for Rocket.Chat.Application class.
...@@ -24,8 +24,8 @@ import rx.Single; ...@@ -24,8 +24,8 @@ import rx.Single;
public class RocketChatService extends Service implements ConnectivityServiceInterface { public class RocketChatService extends Service implements ConnectivityServiceInterface {
private ConnectivityManagerInternal connectivityManager; private ConnectivityManagerInternal connectivityManager;
private HashMap<String, RocketChatWebSocketThread> webSocketThreads; private static volatile Semaphore webSocketThreadLock = new Semaphore(1);
private Semaphore webSocketThreadLock = new Semaphore(1); private static volatile RocketChatWebSocketThread currentWebSocketThread;
public class LocalBinder extends Binder { public class LocalBinder extends Binder {
ConnectivityServiceInterface getServiceInterface() { ConnectivityServiceInterface getServiceInterface() {
...@@ -38,7 +38,7 @@ public class RocketChatService extends Service implements ConnectivityServiceInt ...@@ -38,7 +38,7 @@ public class RocketChatService extends Service implements ConnectivityServiceInt
/** /**
* ensure RocketChatService alive. * ensure RocketChatService alive.
*/ */
/*package*/ static void keepAlive(Context context) { /*package*/static void keepAlive(Context context) {
context.startService(new Intent(context, RocketChatService.class)); context.startService(new Intent(context, RocketChatService.class));
} }
...@@ -57,7 +57,6 @@ public class RocketChatService extends Service implements ConnectivityServiceInt ...@@ -57,7 +57,6 @@ public class RocketChatService extends Service implements ConnectivityServiceInt
super.onCreate(); super.onCreate();
connectivityManager = ConnectivityManager.getInstanceForInternal(getApplicationContext()); connectivityManager = ConnectivityManager.getInstanceForInternal(getApplicationContext());
connectivityManager.resetConnectivityStateList(); connectivityManager.resetConnectivityStateList();
webSocketThreads = new HashMap<>();
} }
@DebugLog @DebugLog
...@@ -70,32 +69,26 @@ public class RocketChatService extends Service implements ConnectivityServiceInt ...@@ -70,32 +69,26 @@ public class RocketChatService extends Service implements ConnectivityServiceInt
@Override @Override
public Single<Boolean> ensureConnectionToServer(String hostname) { //called via binder. public Single<Boolean> ensureConnectionToServer(String hostname) { //called via binder.
return getOrCreateWebSocketThread(hostname) return getOrCreateWebSocketThread(hostname)
.doOnError(err -> { .flatMap(RocketChatWebSocketThread::keepAlive);
webSocketThreads.remove(hostname);
connectivityManager.notifyConnectionLost(hostname, ConnectivityManagerInternal.REASON_NETWORK_ERROR);
})
.flatMap(webSocketThreads -> webSocketThreads.keepAlive());
} }
@Override @Override
public Single<Boolean> disconnectFromServer(String hostname) { //called via binder. public Single<Boolean> disconnectFromServer(String hostname) { //called via binder.
return Single.defer(() -> { return Single.defer(() -> {
if (!webSocketThreads.containsKey(hostname)) { if (!existsThreadForHostname(hostname)) {
return Single.just(true); return Single.just(true);
} }
RocketChatWebSocketThread thread = webSocketThreads.get(hostname); if (currentWebSocketThread != null) {
if (thread != null) { return currentWebSocketThread.terminate()
return thread.terminate()
// after disconnection from server // after disconnection from server
.doAfterTerminate(() -> { .doAfterTerminate(() -> {
// remove RCWebSocket key from HashMap currentWebSocketThread = null;
webSocketThreads.remove(hostname);
// remove RealmConfiguration key from HashMap // remove RealmConfiguration key from HashMap
RealmStore.sStore.remove(hostname); RealmStore.sStore.remove(hostname);
}); });
} else { } else {
return Observable.timer(1, TimeUnit.SECONDS).toSingle() return Observable.timer(1, TimeUnit.SECONDS).singleOrError()
.flatMap(_val -> disconnectFromServer(hostname)); .flatMap(_val -> disconnectFromServer(hostname));
} }
}); });
...@@ -105,23 +98,52 @@ public class RocketChatService extends Service implements ConnectivityServiceInt ...@@ -105,23 +98,52 @@ public class RocketChatService extends Service implements ConnectivityServiceInt
private Single<RocketChatWebSocketThread> getOrCreateWebSocketThread(String hostname) { private Single<RocketChatWebSocketThread> getOrCreateWebSocketThread(String hostname) {
return Single.defer(() -> { return Single.defer(() -> {
webSocketThreadLock.acquire(); webSocketThreadLock.acquire();
if (webSocketThreads.containsKey(hostname)) { int connectivityState = ConnectivityManager.getInstance(getApplicationContext()).getConnectivityState(hostname);
RocketChatWebSocketThread thread = webSocketThreads.get(hostname); boolean isDisconnected = connectivityState != ServerConnectivity.STATE_CONNECTED;
if (currentWebSocketThread != null && existsThreadForHostname(hostname) && !isDisconnected) {
webSocketThreadLock.release();
return Single.just(currentWebSocketThread);
}
if (currentWebSocketThread != null) {
return currentWebSocketThread.terminate()
.doAfterTerminate(() -> currentWebSocketThread = null)
.flatMap(terminated ->
RocketChatWebSocketThread.getStarted(getApplicationContext(), hostname)
.doOnSuccess(thread -> {
currentWebSocketThread = thread;
webSocketThreadLock.release();
})
.doOnError(throwable -> {
currentWebSocketThread = null;
RCLog.e(throwable);
Logger.report(throwable);
webSocketThreadLock.release(); webSocketThreadLock.release();
return Single.just(thread); })
);
} }
return RocketChatWebSocketThread.getStarted(getApplicationContext(), hostname) return RocketChatWebSocketThread.getStarted(getApplicationContext(), hostname)
.doOnSuccess(thread -> { .doOnSuccess(thread -> {
webSocketThreads.put(hostname, thread); currentWebSocketThread = thread;
webSocketThreadLock.release(); webSocketThreadLock.release();
}) })
.doOnError(throwable -> { .doOnError(throwable -> {
currentWebSocketThread = null;
RCLog.e(throwable);
Logger.report(throwable); Logger.report(throwable);
webSocketThreadLock.release(); webSocketThreadLock.release();
}); });
}); });
} }
private boolean existsThreadForHostname(String hostname) {
if (hostname == null || currentWebSocketThread == null) {
return false;
}
return currentWebSocketThread.getName().equals("RC_thread_" + hostname);
}
@Nullable @Nullable
@Override @Override
public IBinder onBind(Intent intent) { public IBinder onBind(Intent intent) {
......
...@@ -4,17 +4,43 @@ package chat.rocket.android.service; ...@@ -4,17 +4,43 @@ package chat.rocket.android.service;
* pair with server's hostname and its connectivity state. * pair with server's hostname and its connectivity state.
*/ */
public class ServerConnectivity { public class ServerConnectivity {
public static final int STATE_CONNECTED = 1; public static final int STATE_CONNECTED = 1;
public static final int STATE_DISCONNECTED = 2; public static final int STATE_DISCONNECTED = 2;
public static final int STATE_CONNECTING = 3; public static final int STATE_CONNECTING = 3;
/*package*/ static final int STATE_DISCONNECTING = 4; /*package*/ static final int STATE_DISCONNECTING = 4;
public static final ServerConnectivity CONNECTED = new ServerConnectivity(null, STATE_CONNECTED);
public final String hostname; public final String hostname;
public final int state; public final int state;
public final int code;
ServerConnectivity(String hostname, int state) {
this.hostname = hostname;
this.state = state;
this.code = -1;
}
public ServerConnectivity(String hostname, int state) { ServerConnectivity(String hostname, int state, int code) {
this.hostname = hostname; this.hostname = hostname;
this.state = state; this.state = state;
this.code = code;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ServerConnectivity that = (ServerConnectivity) o;
return state == that.state;
}
@Override
public int hashCode() {
return state;
} }
/** /**
......
...@@ -3,38 +3,37 @@ package chat.rocket.android.service.ddp; ...@@ -3,38 +3,37 @@ package chat.rocket.android.service.ddp;
import android.content.Context; import android.content.Context;
import android.text.TextUtils; import android.text.TextUtils;
import chat.rocket.persistence.realm.models.ddp.RealmUser;
import io.reactivex.disposables.Disposable;
import io.realm.Realm;
import io.realm.RealmObject;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
import java.util.Iterator; import java.util.Iterator;
import chat.rocket.android.helper.LogIfError; import chat.rocket.android.helper.LogIfError;
import chat.rocket.android.log.RCLog; import chat.rocket.android.log.RCLog;
import chat.rocket.persistence.realm.RealmHelper;
import chat.rocket.android.service.DDPClientRef;
import chat.rocket.android.service.Registrable; import chat.rocket.android.service.Registrable;
import chat.rocket.android_ddp.DDPClient;
import chat.rocket.android_ddp.DDPSubscription; import chat.rocket.android_ddp.DDPSubscription;
import chat.rocket.persistence.realm.RealmHelper;
import chat.rocket.persistence.realm.models.ddp.RealmUser;
import io.reactivex.disposables.Disposable;
import io.realm.Realm;
import io.realm.RealmObject;
import io.realm.RealmResults; import io.realm.RealmResults;
public abstract class AbstractDDPDocEventSubscriber implements Registrable { public abstract class AbstractDDPDocEventSubscriber implements Registrable {
protected final Context context; protected final Context context;
protected final String hostname; protected final String hostname;
protected final RealmHelper realmHelper; protected final RealmHelper realmHelper;
protected final DDPClientRef ddpClientRef;
private boolean isUnsubscribed; private boolean isUnsubscribed;
private String subscriptionId; private String subscriptionId;
private Disposable rxSubscription; private Disposable rxSubscription;
protected AbstractDDPDocEventSubscriber(Context context, String hostname, protected AbstractDDPDocEventSubscriber(Context context, String hostname,
RealmHelper realmHelper, DDPClientRef ddpClientRef) { RealmHelper realmHelper) {
this.context = context; this.context = context;
this.hostname = hostname; this.hostname = hostname;
this.realmHelper = realmHelper; this.realmHelper = realmHelper;
this.ddpClientRef = ddpClientRef;
} }
protected abstract String getSubscriptionName(); protected abstract String getSubscriptionName();
...@@ -69,9 +68,9 @@ public abstract class AbstractDDPDocEventSubscriber implements Registrable { ...@@ -69,9 +68,9 @@ public abstract class AbstractDDPDocEventSubscriber implements Registrable {
// just ignore. // just ignore.
} }
ddpClientRef.get().subscribe(getSubscriptionName(), params).onSuccess(task -> { DDPClient.get().subscribe(getSubscriptionName(), params).onSuccess(task -> {
if (isUnsubscribed) { if (isUnsubscribed) {
ddpClientRef.get().unsubscribe(task.getResult().id).continueWith(new LogIfError()); DDPClient.get().unsubscribe(task.getResult().id).continueWith(new LogIfError());
} else { } else {
subscriptionId = task.getResult().id; subscriptionId = task.getResult().id;
} }
...@@ -98,7 +97,7 @@ public abstract class AbstractDDPDocEventSubscriber implements Registrable { ...@@ -98,7 +97,7 @@ public abstract class AbstractDDPDocEventSubscriber implements Registrable {
} }
protected Disposable subscribe() { protected Disposable subscribe() {
return ddpClientRef.get().getSubscriptionCallback() return DDPClient.get().getSubscriptionCallback()
.filter(event -> event instanceof DDPSubscription.DocEvent) .filter(event -> event instanceof DDPSubscription.DocEvent)
.cast(DDPSubscription.DocEvent.class) .cast(DDPSubscription.DocEvent.class)
.filter(event -> isTarget(event.collection)) .filter(event -> isTarget(event.collection))
...@@ -197,8 +196,8 @@ public abstract class AbstractDDPDocEventSubscriber implements Registrable { ...@@ -197,8 +196,8 @@ public abstract class AbstractDDPDocEventSubscriber implements Registrable {
if (rxSubscription != null) { if (rxSubscription != null) {
rxSubscription.dispose(); rxSubscription.dispose();
} }
if (!TextUtils.isEmpty(subscriptionId) && ddpClientRef.get() != null) { if (!TextUtils.isEmpty(subscriptionId) && DDPClient.get() != null) {
ddpClientRef.get().unsubscribe(subscriptionId).continueWith(new LogIfError()); DDPClient.get().unsubscribe(subscriptionId).continueWith(new LogIfError());
} }
} }
} }
package chat.rocket.android.service.ddp.base; package chat.rocket.android.service.ddp.base;
import android.content.Context; import android.content.Context;
import org.json.JSONArray; import org.json.JSONArray;
import chat.rocket.persistence.realm.RealmHelper;
import chat.rocket.android.service.DDPClientRef;
import chat.rocket.android.service.ddp.AbstractDDPDocEventSubscriber; import chat.rocket.android.service.ddp.AbstractDDPDocEventSubscriber;
import chat.rocket.persistence.realm.RealmHelper;
abstract class AbstractBaseSubscriber extends AbstractDDPDocEventSubscriber { abstract class AbstractBaseSubscriber extends AbstractDDPDocEventSubscriber {
protected AbstractBaseSubscriber(Context context, String hostname, RealmHelper realmHelper, protected AbstractBaseSubscriber(Context context, String hostname, RealmHelper realmHelper) {
DDPClientRef ddpClientRef) { super(context, hostname, realmHelper);
super(context, hostname, realmHelper, ddpClientRef);
} }
@Override @Override
......
package chat.rocket.android.service.ddp.base; package chat.rocket.android.service.ddp.base;
import android.content.Context; import android.content.Context;
import io.realm.RealmObject;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
import chat.rocket.persistence.realm.models.ddp.RealmUser;
import chat.rocket.persistence.realm.RealmHelper; import chat.rocket.persistence.realm.RealmHelper;
import chat.rocket.android.service.DDPClientRef; import chat.rocket.persistence.realm.models.ddp.RealmUser;
import io.realm.RealmObject;
/** /**
* "activeUsers" subscriber. * "activeUsers" subscriber.
*/ */
public class ActiveUsersSubscriber extends AbstractBaseSubscriber { public class ActiveUsersSubscriber extends AbstractBaseSubscriber {
public ActiveUsersSubscriber(Context context, String hostname, RealmHelper realmHelper, public ActiveUsersSubscriber(Context context, String hostname, RealmHelper realmHelper) {
DDPClientRef ddpClientRef) { super(context, hostname, realmHelper);
super(context, hostname, realmHelper, ddpClientRef);
} }
@Override @Override
......
package chat.rocket.android.service.ddp.base; package chat.rocket.android.service.ddp.base;
import android.content.Context; import android.content.Context;
import io.realm.RealmObject;
import chat.rocket.persistence.realm.models.ddp.RealmMeteorLoginServiceConfiguration;
import chat.rocket.persistence.realm.RealmHelper; import chat.rocket.persistence.realm.RealmHelper;
import chat.rocket.android.service.DDPClientRef; import chat.rocket.persistence.realm.models.ddp.RealmMeteorLoginServiceConfiguration;
import io.realm.RealmObject;
/** /**
* meteor.loginServiceConfiguration subscriber * meteor.loginServiceConfiguration subscriber
*/ */
public class LoginServiceConfigurationSubscriber extends AbstractBaseSubscriber { public class LoginServiceConfigurationSubscriber extends AbstractBaseSubscriber {
public LoginServiceConfigurationSubscriber(Context context, String hostname, public LoginServiceConfigurationSubscriber(Context context, String hostname,
RealmHelper realmHelper, DDPClientRef ddpClientRef) { RealmHelper realmHelper) {
super(context, hostname, realmHelper, ddpClientRef); super(context, hostname, realmHelper);
} }
@Override @Override
......
package chat.rocket.android.service.ddp.base; package chat.rocket.android.service.ddp.base;
import android.content.Context; import android.content.Context;
import io.realm.RealmObject;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
import chat.rocket.persistence.realm.models.ddp.RealmUser;
import chat.rocket.persistence.realm.RealmHelper; import chat.rocket.persistence.realm.RealmHelper;
import chat.rocket.android.service.DDPClientRef; import chat.rocket.persistence.realm.models.ddp.RealmUser;
import io.realm.RealmObject;
/** /**
* "userData" subscriber. * "userData" subscriber.
*/ */
public class UserDataSubscriber extends AbstractBaseSubscriber { public class UserDataSubscriber extends AbstractBaseSubscriber {
public UserDataSubscriber(Context context, String hostname, RealmHelper realmHelper, public UserDataSubscriber(Context context, String hostname, RealmHelper realmHelper) {
DDPClientRef ddpClientRef) { super(context, hostname, realmHelper);
super(context, hostname, realmHelper, ddpClientRef);
} }
@Override @Override
......
package chat.rocket.android.service.ddp.stream; package chat.rocket.android.service.ddp.stream;
import android.content.Context; import android.content.Context;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
import chat.rocket.android.helper.LogIfError; import chat.rocket.android.helper.LogIfError;
import chat.rocket.android.log.RCLog; import chat.rocket.android.log.RCLog;
import chat.rocket.persistence.realm.RealmHelper;
import chat.rocket.android.service.DDPClientRef;
import chat.rocket.android.service.ddp.AbstractDDPDocEventSubscriber; import chat.rocket.android.service.ddp.AbstractDDPDocEventSubscriber;
import chat.rocket.android_ddp.DDPSubscription; import chat.rocket.android_ddp.DDPSubscription;
import chat.rocket.persistence.realm.RealmHelper;
abstract class AbstractStreamNotifyEventSubscriber extends AbstractDDPDocEventSubscriber { abstract class AbstractStreamNotifyEventSubscriber extends AbstractDDPDocEventSubscriber {
protected AbstractStreamNotifyEventSubscriber(Context context, String hostname, protected AbstractStreamNotifyEventSubscriber(Context context, String hostname,
RealmHelper realmHelper, RealmHelper realmHelper) {
DDPClientRef ddpClientRef) { super(context, hostname, realmHelper);
super(context, hostname, realmHelper, ddpClientRef);
} }
@Override @Override
......
...@@ -3,15 +3,13 @@ package chat.rocket.android.service.ddp.stream; ...@@ -3,15 +3,13 @@ package chat.rocket.android.service.ddp.stream;
import android.content.Context; import android.content.Context;
import chat.rocket.persistence.realm.RealmHelper; import chat.rocket.persistence.realm.RealmHelper;
import chat.rocket.android.service.DDPClientRef;
abstract class AbstractStreamNotifyUserEventSubscriber extends AbstractStreamNotifyEventSubscriber { abstract class AbstractStreamNotifyUserEventSubscriber extends AbstractStreamNotifyEventSubscriber {
protected final String userId; protected final String userId;
protected AbstractStreamNotifyUserEventSubscriber(Context context, String hostname, protected AbstractStreamNotifyUserEventSubscriber(Context context, String hostname,
RealmHelper realmHelper, RealmHelper realmHelper, String userId) {
DDPClientRef ddpClientRef, String userId) { super(context, hostname, realmHelper);
super(context, hostname, realmHelper, ddpClientRef);
this.userId = userId; this.userId = userId;
} }
......
package chat.rocket.android.service.ddp.stream; package chat.rocket.android.service.ddp.stream;
import android.content.Context; import android.content.Context;
import io.realm.RealmObject;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
import chat.rocket.persistence.realm.models.ddp.RealmRoom;
import chat.rocket.persistence.realm.RealmHelper; import chat.rocket.persistence.realm.RealmHelper;
import chat.rocket.android.service.DDPClientRef; import chat.rocket.persistence.realm.models.ddp.RealmRoom;
import io.realm.RealmObject;
public class StreamNotifyUserSubscriptionsChanged extends AbstractStreamNotifyUserEventSubscriber { public class StreamNotifyUserSubscriptionsChanged extends AbstractStreamNotifyUserEventSubscriber {
public StreamNotifyUserSubscriptionsChanged(Context context, String hostname, public StreamNotifyUserSubscriptionsChanged(Context context, String hostname,
RealmHelper realmHelper, DDPClientRef ddpClientRef, RealmHelper realmHelper,
String userId) { String userId) {
super(context, hostname, realmHelper, ddpClientRef, userId); super(context, hostname, realmHelper, userId);
} }
@Override @Override
......
package chat.rocket.android.service.ddp.stream; package chat.rocket.android.service.ddp.stream;
import android.content.Context; import android.content.Context;
import io.realm.RealmObject;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
import chat.rocket.persistence.realm.models.ddp.RealmMessage;
import chat.rocket.persistence.realm.RealmHelper; import chat.rocket.persistence.realm.RealmHelper;
import chat.rocket.android.service.DDPClientRef; import chat.rocket.persistence.realm.models.ddp.RealmMessage;
import io.realm.RealmObject;
/** /**
* stream-room-message subscriber. * stream-room-message subscriber.
...@@ -16,8 +16,8 @@ public class StreamRoomMessage extends AbstractStreamNotifyEventSubscriber { ...@@ -16,8 +16,8 @@ public class StreamRoomMessage extends AbstractStreamNotifyEventSubscriber {
private String roomId; private String roomId;
public StreamRoomMessage(Context context, String hostname, public StreamRoomMessage(Context context, String hostname,
RealmHelper realmHelper, DDPClientRef ddpClientRef, String roomId) { RealmHelper realmHelper, String roomId) {
super(context, hostname, realmHelper, ddpClientRef); super(context, hostname, realmHelper);
this.roomId = roomId; this.roomId = roomId;
} }
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment