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;
private final DDPClientImpl impl; public static final int REASON_NETWORK_ERROR = 1001;
public DDPClient(OkHttpClient client) { private static volatile DDPClient singleton;
impl = new DDPClientImpl(this, client); private static volatile OkHttpClient client;
} private final DDPClientImpl impl;
private final AtomicReference<String> hostname = new AtomicReference<>();
public Task<DDPClientCallback.Connect> connect(String url) {
return connect(url, null); public static void initialize(OkHttpClient okHttpClient) {
} client = okHttpClient;
}
public Task<DDPClientCallback.Connect> connect(String url, String session) {
TaskCompletionSource<DDPClientCallback.Connect> task = new TaskCompletionSource<>(); public static DDPClient get() {
impl.connect(task, url, session); DDPClient result = singleton;
return task.getTask(); if (result == null) {
} synchronized (DDPClient.class) {
result = singleton;
public Task<DDPClientCallback.Ping> ping(@Nullable String id) { if (result == null) {
TaskCompletionSource<DDPClientCallback.Ping> task = new TaskCompletionSource<>(); singleton = result = new DDPClient(client);
impl.ping(task, id); }
return task.getTask(); }
} }
return result;
public Maybe<DDPClientCallback.Base> doPing(@Nullable String id) { }
return impl.ping(id);
} private DDPClient(OkHttpClient client) {
impl = new DDPClientImpl(this, client);
public Task<DDPClientCallback.RPC> rpc(String method, JSONArray params, String id, }
long timeoutMs) {
TaskCompletionSource<DDPClientCallback.RPC> task = new TaskCompletionSource<>(); private Task<DDPClientCallback.Connect> connect(String url, String session) {
impl.rpc(task, method, params, id, timeoutMs); hostname.set(url);
return task.getTask(); TaskCompletionSource<DDPClientCallback.Connect> task = new TaskCompletionSource<>();
} impl.connect(task, url, session);
return task.getTask();
public Task<DDPSubscription.Ready> sub(String id, String name, JSONArray params) { }
TaskCompletionSource<DDPSubscription.Ready> task = new TaskCompletionSource<>();
impl.sub(task, name, params, id); private Task<DDPClientCallback.Ping> ping(@Nullable String id) {
return task.getTask(); TaskCompletionSource<DDPClientCallback.Ping> task = new TaskCompletionSource<>();
} impl.ping(task, id);
return task.getTask();
public Task<DDPSubscription.NoSub> unsub(String id) { }
TaskCompletionSource<DDPSubscription.NoSub> task = new TaskCompletionSource<>();
impl.unsub(task, id); private Maybe<DDPClientCallback.Base> doPing(@Nullable String id) {
return task.getTask(); return impl.ping(id);
} }
public Flowable<DDPSubscription.Event> getSubscriptionCallback() { private Task<DDPSubscription.Ready> sub(String id, String name, JSONArray params) {
return impl.getDDPSubscription(); TaskCompletionSource<DDPSubscription.Ready> task = new TaskCompletionSource<>();
} impl.sub(task, name, params, id);
return task.getTask();
public Task<RxWebSocketCallback.Close> getOnCloseCallback() { }
return impl.getOnCloseCallback();
} private Task<DDPSubscription.NoSub> unsub(String id) {
TaskCompletionSource<DDPSubscription.NoSub> task = new TaskCompletionSource<>();
public void close() { impl.unsub(task, id);
impl.close(1000, "closed by DDPClient#close()"); 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() {
return impl.getDDPSubscription();
}
/**
* 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_;
});
}
try {
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,46 +4,62 @@ import android.os.Build; ...@@ -4,46 +4,62 @@ 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
*/ */
public class RocketChatApplication extends MultiDexApplication { public class RocketChatApplication extends MultiDexApplication {
private static RocketChatApplication instance; private static RocketChatApplication instance;
public static RocketChatApplication getInstance() { public static RocketChatApplication getInstance() {
return instance; return instance;
} }
@Override @Override
public void onCreate() { public void onCreate() {
super.onCreate(); super.onCreate();
Fabric.with(this, new Crashlytics()); DDPClient.initialize(OkHttpHelper.INSTANCE.getClientForWebSocket());
Fabric.with(this, new Crashlytics());
RocketChatPersistenceRealm.init(this); RocketChatPersistenceRealm.init(this);
List<ServerInfo> serverInfoList = ConnectivityManager.getInstance(this).getServerList(); List<ServerInfo> serverInfoList = ConnectivityManager.getInstance(this).getServerList();
for (ServerInfo serverInfo : serverInfoList) { for (ServerInfo serverInfo : serverInfoList) {
RealmStore.put(serverInfo.getHostname()); RealmStore.put(serverInfo.getHostname());
} }
RocketChatWidgets.initialize(this, OkHttpHelper.INSTANCE.getClientForDownloadFile(this)); RocketChatWidgets.initialize(this, OkHttpHelper.INSTANCE.getClientForDownloadFile(this));
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
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 (intent.hasExtra(PushConstants.ROOM_ID)) { if (url != null) {
rocketChatCache.setSelectedRoomId(intent.getStringExtra(PushConstants.ROOM_ID)); String hostnameFromPush = url.host();
String loginHostname = rocketChatCache.getSiteUrlFor(hostnameFromPush);
rocketChatCache.setSelectedServerHostname(loginHostname);
if (intent.hasExtra(PushManager.EXTRA_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 {
......
...@@ -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> {
......
...@@ -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);
} }
...@@ -179,8 +194,8 @@ public class MethodCallHelper { ...@@ -179,8 +194,8 @@ public class MethodCallHelper {
.put("algorithm", "sha-256")); .put("algorithm", "sha-256"));
return new JSONArray().put(param); return new JSONArray().put(param);
}).onSuccessTask(CONVERT_TO_JSON_OBJECT) }).onSuccessTask(CONVERT_TO_JSON_OBJECT)
.onSuccessTask(task -> Task.forResult(task.getResult().getString("token"))) .onSuccessTask(task -> Task.forResult(task.getResult().getString("token")))
.onSuccessTask(this::saveToken); .onSuccessTask(this::saveToken);
} }
public Task<Void> loginWithLdap(final String username, final String password) { public Task<Void> loginWithLdap(final String username, final String password) {
...@@ -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?
holder.attachment.appendAttachmentView(attachment, true, false) 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)
} 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.
*/ */
public interface ConnectivityManagerApi { public interface ConnectivityManagerApi {
void keepAliveServer(); void keepAliveServer();
void addOrUpdateServer(String hostname, @Nullable String name, boolean insecure);
void removeServer(String hostname);
void addOrUpdateServer(String hostname, @Nullable String name, boolean insecure); Single<Boolean> connect(String hostname);
void removeServer(String hostname); List<ServerInfo> getServerList();
Single<Boolean> connect(String hostname); Flowable<ServerConnectivity> getServerConnectivityAsObservable();
List<ServerInfo> getServerList(); int getConnectivityState(@NonNull String hostname);
Observable<ServerConnectivity> getServerConnectivityAsObservable(); 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();
}
...@@ -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());
} }
} }
} }
...@@ -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;
} }
......
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