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
### 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: '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 {
compileSdkVersion rootProject.ext.compileSdkVersion
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;
import io.reactivex.Flowable;
import io.reactivex.Maybe;
import io.reactivex.annotations.Nullable;
import android.text.TextUtils;
import org.json.JSONArray;
import org.json.JSONException;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicReference;
import bolts.Task;
import bolts.TaskCompletionSource;
import chat.rocket.android.log.RCLog;
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;
public class DDPClient {
// reference: https://github.com/eddflrs/meteor-ddp/blob/master/meteor-ddp.js
private final DDPClientImpl impl;
public DDPClient(OkHttpClient client) {
impl = new DDPClientImpl(this, client);
}
public Task<DDPClientCallback.Connect> connect(String url) {
return connect(url, null);
}
public Task<DDPClientCallback.Connect> connect(String url, String session) {
TaskCompletionSource<DDPClientCallback.Connect> task = new TaskCompletionSource<>();
impl.connect(task, url, session);
return task.getTask();
}
public Task<DDPClientCallback.Ping> ping(@Nullable String id) {
TaskCompletionSource<DDPClientCallback.Ping> task = new TaskCompletionSource<>();
impl.ping(task, id);
return task.getTask();
}
public Maybe<DDPClientCallback.Base> doPing(@Nullable String id) {
return impl.ping(id);
}
public Task<DDPClientCallback.RPC> rpc(String method, JSONArray params, String id,
long timeoutMs) {
TaskCompletionSource<DDPClientCallback.RPC> task = new TaskCompletionSource<>();
impl.rpc(task, method, params, id, timeoutMs);
return task.getTask();
}
public Task<DDPSubscription.Ready> sub(String id, String name, JSONArray params) {
TaskCompletionSource<DDPSubscription.Ready> task = new TaskCompletionSource<>();
impl.sub(task, name, params, id);
return task.getTask();
}
public Task<DDPSubscription.NoSub> unsub(String id) {
TaskCompletionSource<DDPSubscription.NoSub> task = new TaskCompletionSource<>();
impl.unsub(task, id);
return task.getTask();
}
public Flowable<DDPSubscription.Event> getSubscriptionCallback() {
return impl.getDDPSubscription();
}
public Task<RxWebSocketCallback.Close> getOnCloseCallback() {
return impl.getOnCloseCallback();
}
public void close() {
impl.close(1000, "closed by DDPClient#close()");
}
// reference: https://github.com/eddflrs/meteor-ddp/blob/master/meteor-ddp.js
public static final int REASON_CLOSED_BY_USER = 1000;
public static final int REASON_NETWORK_ERROR = 1001;
private static volatile DDPClient singleton;
private static volatile OkHttpClient client;
private final DDPClientImpl impl;
private final AtomicReference<String> hostname = new AtomicReference<>();
public static void initialize(OkHttpClient okHttpClient) {
client = okHttpClient;
}
public static DDPClient get() {
DDPClient result = singleton;
if (result == null) {
synchronized (DDPClient.class) {
result = singleton;
if (result == null) {
singleton = result = new DDPClient(client);
}
}
}
return result;
}
private DDPClient(OkHttpClient client) {
impl = new DDPClientImpl(this, client);
}
private Task<DDPClientCallback.Connect> connect(String url, String session) {
hostname.set(url);
TaskCompletionSource<DDPClientCallback.Connect> task = new TaskCompletionSource<>();
impl.connect(task, url, session);
return task.getTask();
}
private Task<DDPClientCallback.Ping> ping(@Nullable String id) {
TaskCompletionSource<DDPClientCallback.Ping> task = new TaskCompletionSource<>();
impl.ping(task, id);
return task.getTask();
}
private Maybe<DDPClientCallback.Base> doPing(@Nullable String id) {
return impl.ping(id);
}
private Task<DDPSubscription.Ready> sub(String id, String name, JSONArray params) {
TaskCompletionSource<DDPSubscription.Ready> task = new TaskCompletionSource<>();
impl.sub(task, name, params, id);
return task.getTask();
}
private Task<DDPSubscription.NoSub> unsub(String id) {
TaskCompletionSource<DDPSubscription.NoSub> task = new TaskCompletionSource<>();
impl.unsub(task, id);
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;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import org.json.JSONObject;
public class DDPClientCallback {
......
......@@ -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) {
try {
flowable = websocket.connect(url).autoConnect(2);
CompositeDisposable disposables = new CompositeDisposable();
disposables.add(
flowable.retry().filter(callback -> callback instanceof RxWebSocketCallback.Open)
flowable.filter(callback -> callback instanceof RxWebSocketCallback.Open)
.subscribe(
callback ->
sendMessage("connect",
......@@ -115,6 +115,7 @@ public class DDPClientImpl {
if (requested) {
return flowable.filter(callback -> callback instanceof RxWebSocketCallback.Message)
.timeout(8, TimeUnit.SECONDS)
.map(callback -> ((RxWebSocketCallback.Message) callback).responseBodyString)
.map(DDPClientImpl::toJson)
.filter(response -> "pong".equalsIgnoreCase(extractMsg(response)))
......
package chat.rocket.android_ddp;
import android.support.annotation.NonNull;
import org.json.JSONArray;
import org.json.JSONObject;
......
package chat.rocket.android_ddp.rx;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import chat.rocket.android.log.RCLog;
import io.reactivex.BackpressureStrategy;
......
......@@ -8,35 +8,10 @@ repositories {
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'me.tatarka.retrolambda'
apply plugin: 'com.jakewharton.hugo'
apply plugin: 'com.github.triplet.play'
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 {
compileSdkVersion rootProject.ext.compileSdkVersion
buildToolsVersion rootProject.ext.buildToolsVersion
......@@ -45,8 +20,8 @@ android {
applicationId "chat.rocket.android"
minSdkVersion 16
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 44
versionName "1.0.22"
versionCode 54
versionName "1.0.31"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true
multiDexEnabled true
......@@ -108,6 +83,13 @@ android {
androidTest.java.srcDirs += 'src/androidTest/kotlin'
}
}
dexOptions {
if(System.getenv()["CIRCLECI"] as boolean) {
javaMaxHeapSize "1536M"
preDexLibraries false
}
}
}
play {
......@@ -115,7 +97,7 @@ play {
track = "${track}"
}
ext {
playLibVersion = '11.0.4'
playLibVersion = '11.6.0'
stethoVersion = '1.5.0'
stethoOkhttp3Version = '1.5.0'
stethoRealmVersion = '2.1.0'
......@@ -132,47 +114,37 @@ dependencies {
compile extraDependencies.okHTTP
compile extraDependencies.rxJava
compile extraDependencies.boltTask
compile supportDependencies.multidex
compile supportDependencies.designSupportLibrary
compile supportDependencies.annotation
compile supportDependencies.kotlin;
compile rxbindingDependencies.rxBinding
compile rxbindingDependencies.rxBindingSupport
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-crash:$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-android:$rxlifecycleVersion"
compile "com.trello.rxlifecycle2:rxlifecycle-components:$rxlifecycleVersion"
compile 'nl.littlerobots.rxlint:rxlint:1.2'
compile "frankiesardo:icepick:$icepickVersion"
provided "frankiesardo:icepick-processor:$icepickVersion"
annotationProcessor "frankiesardo:icepick-processor:$icepickVersion"
compile "com.github.hotchemi:permissionsdispatcher:$permissionsdispatcherVersion"
annotationProcessor "com.github.hotchemi:permissionsdispatcher-processor:$permissionsdispatcherVersion"
compile('com.crashlytics.sdk.android:crashlytics:2.6.8@aar') {
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"
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 "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-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'
package chat.rocket.android;
import android.os.StrictMode;
import com.facebook.stetho.Stetho;
import com.tspoon.traceur.Traceur;
import com.uphyca.stetho_realm.RealmInspectorModulesProvider;
......
......@@ -5,8 +5,8 @@ import chat.rocket.android.RocketChatCache
import chat.rocket.android.api.rest.CookieInterceptor
import chat.rocket.android.api.rest.DefaultCookieProvider
import com.facebook.stetho.okhttp3.StethoInterceptor
import java.util.concurrent.TimeUnit
import okhttp3.OkHttpClient
import java.util.concurrent.TimeUnit
object OkHttpHelper {
......
......@@ -5,6 +5,7 @@
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.VIBRATE"/>
<permission
android:name="chat.rocket.android.permission.C2D_MESSAGE"
......@@ -56,10 +57,32 @@
android:permission="com.google.android.c2dm.permission.SEND">
<intent-filter>
<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>
</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
android:name=".push.gcm.GCMIntentService"
android:exported="false">
......@@ -76,6 +99,12 @@
</intent-filter>
</service>
<receiver android:name=".push.PushManager$DeleteReceiver"
android:exported="false" />
<receiver android:name=".push.PushManager$ReplyReceiver"
android:exported="false" />
<meta-data
android:name="io.fabric.ApiKey"
android:value="12ac6e94f850aaffcdff52001af77ca415d06a43" />
......
......@@ -4,46 +4,62 @@ import android.os.Build;
import android.support.multidex.MultiDexApplication;
import android.support.v7.app.AppCompatDelegate;
import chat.rocket.android.helper.OkHttpHelper;
import com.crashlytics.android.Crashlytics;
import io.fabric.sdk.android.Fabric;
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.core.models.ServerInfo;
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 io.fabric.sdk.android.Fabric;
import io.reactivex.exceptions.UndeliverableException;
import io.reactivex.plugins.RxJavaPlugins;
/**
* Customized Application-class for Rocket.Chat
*/
public class RocketChatApplication extends MultiDexApplication {
private static RocketChatApplication instance;
private static RocketChatApplication instance;
public static RocketChatApplication getInstance() {
return instance;
}
public static RocketChatApplication getInstance() {
return instance;
}
@Override
public void onCreate() {
super.onCreate();
Fabric.with(this, new Crashlytics());
@Override
public void onCreate() {
super.onCreate();
DDPClient.initialize(OkHttpHelper.INSTANCE.getClientForWebSocket());
Fabric.with(this, new Crashlytics());
RocketChatPersistenceRealm.init(this);
RocketChatPersistenceRealm.init(this);
List<ServerInfo> serverInfoList = ConnectivityManager.getInstance(this).getServerList();
for (ServerInfo serverInfo : serverInfoList) {
RealmStore.put(serverInfo.getHostname());
}
List<ServerInfo> serverInfoList = ConnectivityManager.getInstance(this).getServerList();
for (ServerInfo serverInfo : serverInfoList) {
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) {
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
}
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
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;
import io.reactivex.Flowable;
import io.reactivex.annotations.NonNull;
import io.reactivex.annotations.Nullable;
import okhttp3.HttpUrl;
/**
* sharedpreference-based cache.
*/
public class RocketChatCache {
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_PUSH_ID = "KEY_PUSH_ID";
private static final String KEY_HOSTNAME_LIST = "KEY_HOSTNAME_LIST";
......@@ -50,6 +53,75 @@ public class RocketChatCache {
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) {
String hostnameList = getString(KEY_HOSTNAME_LIST, null);
try {
......
......@@ -3,21 +3,25 @@ package chat.rocket.android.activity;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.Nullable;
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 chat.rocket.android.LaunchUtil;
import chat.rocket.android.R;
import chat.rocket.android.RocketChatCache;
import chat.rocket.android.helper.Logger;
import chat.rocket.android.push.PushConstants;
import chat.rocket.android.push.PushNotificationHandler;
import chat.rocket.android.push.PushManager;
import chat.rocket.android.service.ConnectivityManager;
import chat.rocket.core.models.ServerInfo;
import chat.rocket.persistence.realm.RealmStore;
import chat.rocket.persistence.realm.models.ddp.RealmRoom;
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 {
@State protected String hostname;
......@@ -39,7 +43,6 @@ abstract class AbstractAuthedActivity extends AbstractFragmentActivity {
}
updateHostnameIfNeeded(rocketChatCache.getSelectedServerHostname());
updateRoomIdIfNeeded(rocketChatCache.getSelectedRoomId());
}
@Override
......@@ -53,20 +56,36 @@ abstract class AbstractAuthedActivity extends AbstractFragmentActivity {
return;
}
if (intent.hasExtra(PushConstants.HOSTNAME)) {
rocketChatCache.setSelectedServerHostname(intent.getStringExtra(PushConstants.HOSTNAME));
if (intent.hasExtra(PushConstants.ROOM_ID)) {
rocketChatCache.setSelectedRoomId(intent.getStringExtra(PushConstants.ROOM_ID));
if (intent.hasExtra(PushManager.EXTRA_HOSTNAME)) {
String hostname = intent.getStringExtra(PushManager.EXTRA_HOSTNAME);
HttpUrl url = HttpUrl.parse(hostname);
if (url != null) {
String hostnameFromPush = url.host();
String loginHostname = rocketChatCache.getSiteUrlFor(hostnameFromPush);
rocketChatCache.setSelectedServerHostname(loginHostname);
if (intent.hasExtra(PushManager.EXTRA_ROOM_ID)) {
rocketChatCache.setSelectedRoomId(intent.getStringExtra(PushManager.EXTRA_ROOM_ID));
}
}
PushManager.INSTANCE.clearNotificationsByHost(hostname);
} else {
updateHostnameIfNeeded(rocketChatCache.getSelectedServerHostname());
}
if (intent.hasExtra(PushConstants.NOT_ID)) {
if (intent.hasExtra(PushManager.EXTRA_NOT_ID) && intent.hasExtra(PushManager.EXTRA_HOSTNAME)) {
isNotification = true;
PushNotificationHandler
.cleanUpNotificationStack(intent.getIntExtra(PushConstants.NOT_ID, 0));
int notificationId = intent.getIntExtra(PushManager.EXTRA_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 {
if (hostname == null) {
if (newHostname != null && assertServerRealmStoreExists(newHostname)) {
updateHostname(newHostname);
updateRoomIdIfNeeded(rocketChatCache.getSelectedRoomId());
} else {
recoverFromHostnameError();
}
} else {
if (hostname.equals(newHostname)) {
updateHostname(newHostname);
updateRoomIdIfNeeded(rocketChatCache.getSelectedRoomId());
return;
}
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 {
recoverFromHostnameError();
}
......
......@@ -8,8 +8,8 @@ import android.support.v4.app.Fragment;
import chat.rocket.android.R;
import chat.rocket.android.fragment.server_config.LoginFragment;
import chat.rocket.android.fragment.server_config.RetryLoginFragment;
import chat.rocket.core.interactors.SessionInteractor;
import chat.rocket.android.service.ConnectivityManager;
import chat.rocket.core.interactors.SessionInteractor;
import chat.rocket.persistence.realm.repositories.RealmSessionRepository;
/**
......
......@@ -2,14 +2,13 @@ package chat.rocket.android.activity;
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.helper.Logger;
import chat.rocket.android.service.ConnectivityManagerApi;
import chat.rocket.android.shared.BasePresenter;
import chat.rocket.core.interactors.SessionInteractor;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
public class LoginPresenter extends BasePresenter<LoginContract.View>
implements LoginContract.Presenter {
......
......@@ -26,6 +26,8 @@ public interface MainContract {
void showConnectionOk();
void showSignedInServers(List<Pair<String, Pair<String, String>>> serverList);
void refreshRoom();
}
interface Presenter extends BaseContract.Presenter<View> {
......
......@@ -5,6 +5,7 @@ import android.support.v4.app.Fragment
import android.support.v7.app.AppCompatActivity
import chat.rocket.android.R
import chat.rocket.android.fragment.chatroom.list.RoomListFragment
import chat.rocket.core.models.Room
import kotlinx.android.synthetic.main.activity_room.*
class RoomActivity : AppCompatActivity() {
......@@ -20,7 +21,7 @@ class RoomActivity : AppCompatActivity() {
val extras = intent.extras
val roomListFragment = RoomListFragment.newInstance(extras.getInt("actionId"),
extras.getString("roomId"),
extras.getString("roomType"),
extras.getString("roomType", Room.TYPE_GROUP),
extras.getString("hostname"),
extras.getString("token"),
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;
import android.content.Context;
import org.json.JSONArray;
import org.json.JSONObject;
import bolts.Task;
import chat.rocket.android.helper.TextUtils;
import chat.rocket.persistence.realm.RealmHelper;
import chat.rocket.android.service.DDPClientRef;
/**
* MethodCall for uploading file.
......@@ -17,8 +17,8 @@ public class FileUploadingHelper extends MethodCallHelper {
super(context, hostname);
}
public FileUploadingHelper(RealmHelper realmHelper, DDPClientRef ddpClientRef) {
super(realmHelper, ddpClientRef);
public FileUploadingHelper(RealmHelper realmHelper) {
super(realmHelper);
}
public Task<JSONObject> uploadS3Request(String filename, long filesize, String mimeType,
......
......@@ -11,11 +11,13 @@ import java.util.UUID;
import bolts.Continuation;
import bolts.Task;
import chat.rocket.android.RocketChatCache;
import chat.rocket.android.helper.CheckSum;
import chat.rocket.android.helper.TextUtils;
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.core.PublicSettingsConstants;
import chat.rocket.core.SyncState;
import chat.rocket.persistence.realm.RealmHelper;
import chat.rocket.persistence.realm.RealmStore;
......@@ -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.RealmSession;
import hugo.weaving.DebugLog;
import okhttp3.HttpUrl;
/**
* Utility class for creating/handling MethodCall or RPC.
......@@ -45,7 +48,6 @@ public class MethodCallHelper {
task -> Task.forResult(new JSONArray(task.getResult()));
protected final Context context;
protected final RealmHelper realmHelper;
protected final DDPClientRef ddpClientRef;
/**
* initialize with Context and hostname.
......@@ -53,23 +55,32 @@ public class MethodCallHelper {
public MethodCallHelper(Context context, String hostname) {
this.context = context.getApplicationContext();
this.realmHelper = RealmStore.getOrCreate(hostname);
ddpClientRef = null;
}
/**
* initialize with RealmHelper and DDPClient.
*/
public MethodCallHelper(RealmHelper realmHelper, DDPClientRef ddpClientRef) {
public MethodCallHelper(RealmHelper realmHelper) {
this.context = null;
this.realmHelper = realmHelper;
this.ddpClientRef = ddpClientRef;
}
public MethodCallHelper(Context context, RealmHelper realmHelper) {
this.context = context.getApplicationContext();
this.realmHelper = realmHelper;
}
@DebugLog
private Task<String> executeMethodCall(String methodName, String param, long timeout) {
if (ddpClientRef != null) {
return ddpClientRef.get().rpc(UUID.randomUUID().toString(), methodName, param, timeout)
.onSuccessTask(task -> Task.forResult(task.getResult().result));
if (DDPClient.get() != null) {
return DDPClient.get().rpc(UUID.randomUUID().toString(), methodName, param, timeout)
.onSuccessTask(task -> Task.forResult(task.getResult().result))
.continueWithTask(task_ -> {
if (task_.isFaulted()) {
return Task.forError(task_.getError());
}
return Task.forResult(task_.getResult());
});
} else {
return MethodCall.execute(realmHelper, methodName, param, timeout)
.onSuccessTask(task -> {
......@@ -84,8 +95,13 @@ public class MethodCallHelper {
return task.continueWithTask(_task -> {
if (_task.isFaulted()) {
Exception exception = _task.getError();
if (exception instanceof MethodCall.Error) {
String errMessageJson = exception.getMessage();
if (exception instanceof MethodCall.Error || exception instanceof DDPClientCallback.RPC.Error) {
String errMessageJson;
if (exception instanceof DDPClientCallback.RPC.Error) {
errMessageJson = ((DDPClientCallback.RPC.Error) exception).error.toString();
} else {
errMessageJson = exception.getMessage();
}
if (TextUtils.isEmpty(errMessageJson)) {
return Task.forError(exception);
}
......@@ -96,11 +112,10 @@ public class MethodCallHelper {
return Task.forError(new TwoStepAuthException(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) {
return Task.forError(new MethodCall.Timeout());
} else if (exception instanceof DDPClientCallback.Closed) {
return Task.forError(new Exception("Oops, your connection seems off..."));
} else {
return Task.forError(exception);
}
......@@ -179,8 +194,8 @@ public class MethodCallHelper {
.put("algorithm", "sha-256"));
return new JSONArray().put(param);
}).onSuccessTask(CONVERT_TO_JSON_OBJECT)
.onSuccessTask(task -> Task.forResult(task.getResult().getString("token")))
.onSuccessTask(this::saveToken);
.onSuccessTask(task -> Task.forResult(task.getResult().getString("token")))
.onSuccessTask(this::saveToken);
}
public Task<Void> loginWithLdap(final String username, final String password) {
......@@ -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.
*/
......@@ -398,6 +424,11 @@ public class MethodCallHelper {
.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.
*/
......@@ -406,13 +437,31 @@ public class MethodCallHelper {
.onSuccessTask(task -> Task.forResult(null));
}
public Task<Void> getPublicSettings() {
public Task<Void> getPublicSettings(String currentHostname) {
return call("public-settings/get", TIMEOUT_MS)
.onSuccessTask(CONVERT_TO_JSON_ARRAY)
.onSuccessTask(task -> {
final JSONArray settings = task.getResult();
String siteUrl = null;
String siteName = null;
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 -> {
......@@ -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() {
return call("permissions/get", TIMEOUT_MS)
.onSuccessTask(CONVERT_TO_JSON_ARRAY)
......
......@@ -3,21 +3,20 @@ package chat.rocket.android.api;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import org.json.JSONArray;
import org.json.JSONObject;
import bolts.Task;
import chat.rocket.persistence.realm.RealmHelper;
import chat.rocket.android.service.DDPClientRef;
public class RaixPushHelper extends MethodCallHelper {
public RaixPushHelper(Context context, String hostname) {
super(context, hostname);
}
public RaixPushHelper(RealmHelper realmHelper,
DDPClientRef ddpClientRef) {
super(realmHelper, ddpClientRef);
public RaixPushHelper(RealmHelper realmHelper) {
super(realmHelper);
}
public Task<Void> pushUpdate(@NonNull String pushId, @NonNull String gcmToken,
......
package chat.rocket.android.api.rest;
import java.io.IOException;
import chat.rocket.android.helper.TextUtils;
import okhttp3.Interceptor;
import okhttp3.Request;
......
package chat.rocket.android.api.rest;
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.RealmStore;
import chat.rocket.persistence.realm.models.ddp.RealmUser;
import chat.rocket.persistence.realm.models.internal.RealmSession;
public class DefaultCookieProvider implements CookieProvider {
......
......@@ -2,12 +2,13 @@ package chat.rocket.android.api.rest;
import android.support.annotation.NonNull;
import io.reactivex.BackpressureStrategy;
import io.reactivex.Flowable;
import io.reactivex.FlowableEmitter;
import org.json.JSONObject;
import java.io.IOException;
import io.reactivex.BackpressureStrategy;
import io.reactivex.Flowable;
import io.reactivex.FlowableEmitter;
import okhttp3.Call;
import okhttp3.OkHttpClient;
import okhttp3.Request;
......
package chat.rocket.android.api.rest;
import io.reactivex.Flowable;
import org.json.JSONObject;
import io.reactivex.Flowable;
public interface ServerPolicyApi {
String SECURE_PROTOCOL = "https://";
......
......@@ -6,6 +6,7 @@ import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.trello.rxlifecycle2.components.support.RxFragment;
/**
......
......@@ -7,6 +7,7 @@ import android.support.constraint.ConstraintLayout;
import android.support.design.widget.Snackbar;
import android.view.View;
import android.widget.TextView;
import chat.rocket.android.BuildConfig;
import chat.rocket.android.LaunchUtil;
import chat.rocket.android.R;
......
package chat.rocket.android.fragment.add_server;
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.api.rest.DefaultServerPolicyApi;
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.ServerPolicyHelper;
import chat.rocket.android.service.ConnectivityManagerApi;
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 {
private final RocketChatCache rocketChatCache;
......
......@@ -35,6 +35,8 @@ public interface RoomContract {
void showMessageSendFailure(Message message);
void showMessageDeleteFailure(Message message);
void autoloadImages();
void manualLoadImages();
......@@ -71,5 +73,7 @@ public interface RoomContract {
void refreshRoom();
void replyMessage(@NonNull Message message, boolean justQuote);
void acceptMessageDeleteFailure(Message message);
}
}
......@@ -647,6 +647,15 @@ public class RoomFragment extends AbstractChatRoomFragment implements
.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
public void autoloadImages() {
messageListAdapter.setAutoloadImages(true);
......@@ -679,6 +688,7 @@ public class RoomFragment extends AbstractChatRoomFragment implements
.setEditAction(this::onEditMessage)
.setCopyAction(msg -> onCopy(message.getMessage()))
.setQuoteAction(msg -> presenter.replyMessage(message, true))
.setDeleteAction(this::onDeleteMessage)
.showWith(context);
}
}
......@@ -688,6 +698,10 @@ public class RoomFragment extends AbstractChatRoomFragment implements
messageFormManager.setEditMessage(message.getMessage());
}
public void onDeleteMessage(Message message) {
presenter.deleteMessage(message);
}
private void showRoomListFragment(int actionId) {
//TODO: oddly sometimes getActivity() yields null. Investigate the situations this might happen
//and fix it, removing this null-check
......@@ -701,4 +715,8 @@ public class RoomFragment extends AbstractChatRoomFragment implements
startActivity(intent);
}
}
public void loadMessages() {
presenter.loadMessages();
}
}
\ No newline at end of file
......@@ -112,7 +112,11 @@ public class RoomPresenter extends BasePresenter<RoomContract.View>
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.
view.showMessageActions(message);
}
......@@ -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) {
if (currentRoom == null || message.getUser() == null) {
return "";
......
......@@ -10,11 +10,6 @@ import android.support.v4.util.Pair;
import android.view.View;
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.R;
import chat.rocket.android.RocketChatCache;
......@@ -34,6 +29,10 @@ import chat.rocket.persistence.realm.repositories.RealmPublicSettingRepository;
import chat.rocket.persistence.realm.repositories.RealmRoomRepository;
import chat.rocket.persistence.realm.repositories.RealmRoomRoleRepository;
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 {
......
......@@ -47,20 +47,23 @@ class RoomListFragment : Fragment(), RoomListContract.View {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
activity.title = ""
actionId = arguments.getInt("actionId")
roomId = arguments.getString("roomId")
roomType = arguments.getString("roomType")
hostname = arguments.getString("hostname")
token = arguments.getString("token")
userId = arguments.getString("userId")
activity?.title = ""
val args = arguments
args?.let {
actionId = args.getInt("actionId")
roomId = args.getString("roomId")
roomType = args.getString("roomType")
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)
presenter = RoomListPresenter(context, this)
presenter = RoomListPresenter(context!!, this)
}
override fun onResume() {
......@@ -114,9 +117,9 @@ class RoomListFragment : Fragment(), RoomListContract.View {
}
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) {
recyclerView.adapter = RoomMessagesAdapter(dataSet, hostname, context)
recyclerView.adapter = RoomMessagesAdapter(dataSet, hostname, context!!)
val linearLayoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
recyclerView.layoutManager = linearLayoutManager
if (dataSet.size >= 50) {
......@@ -132,9 +135,9 @@ class RoomListFragment : Fragment(), RoomListContract.View {
}
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) {
recyclerView.adapter = RoomMessagesAdapter(dataSet, hostname, context)
recyclerView.adapter = RoomMessagesAdapter(dataSet, hostname, context!!)
val linearLayoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
recyclerView.layoutManager = linearLayoutManager
if (dataSet.size >= 50) {
......@@ -150,7 +153,7 @@ class RoomListFragment : Fragment(), RoomListContract.View {
}
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) {
recyclerView.adapter = RoomFileListAdapter(dataSet)
val linearLayoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
......@@ -168,9 +171,9 @@ class RoomListFragment : Fragment(), RoomListContract.View {
}
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) {
recyclerView.adapter = RoomMemberListAdapter(dataSet, hostname, context)
recyclerView.adapter = RoomMemberListAdapter(dataSet, hostname, context!!)
val linearLayoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
recyclerView.layoutManager = linearLayoutManager
if (dataSet.size >= 50) {
......
package chat.rocket.android.fragment.oauth;
import io.reactivex.android.schedulers.AndroidSchedulers;
import org.json.JSONObject;
import chat.rocket.android.BackgroundLooper;
......@@ -10,6 +9,7 @@ import chat.rocket.android.helper.Logger;
import chat.rocket.android.helper.TextUtils;
import chat.rocket.android.shared.BasePresenter;
import chat.rocket.core.repositories.LoginServiceConfigurationRepository;
import io.reactivex.android.schedulers.AndroidSchedulers;
public class OAuthPresenter extends BasePresenter<OAuthContract.View>
implements OAuthContract.Presenter {
......
package chat.rocket.android.fragment.server_config;
import java.util.List;
import chat.rocket.android.shared.BaseContract;
import chat.rocket.core.models.LoginServiceConfiguration;
......
......@@ -11,6 +11,7 @@ import android.widget.TextView;
import java.util.HashMap;
import java.util.List;
import chat.rocket.android.R;
import chat.rocket.android.api.MethodCallHelper;
import chat.rocket.android.layouthelper.oauth.OAuthProviderInfo;
......
......@@ -3,7 +3,6 @@ package chat.rocket.android.fragment.server_config;
import android.support.annotation.NonNull;
import com.hadisatrio.optional.Optional;
import io.reactivex.android.schedulers.AndroidSchedulers;
import bolts.Task;
import chat.rocket.android.BackgroundLooper;
......@@ -16,6 +15,7 @@ import chat.rocket.core.PublicSettingsConstants;
import chat.rocket.core.models.PublicSetting;
import chat.rocket.core.repositories.LoginServiceConfigurationRepository;
import chat.rocket.core.repositories.PublicSettingRepository;
import io.reactivex.android.schedulers.AndroidSchedulers;
public class LoginPresenter extends BasePresenter<LoginContract.View>
implements LoginContract.Presenter {
......@@ -85,7 +85,7 @@ public class LoginPresenter extends BasePresenter<LoginContract.View>
}
}
return null;
});
}, Task.UI_THREAD_EXECUTOR);
}
private Task<Void> call(String username, String password, Optional<PublicSetting> optional) {
......
......@@ -3,7 +3,6 @@ package chat.rocket.android.fragment.server_config;
import android.support.annotation.NonNull;
import com.hadisatrio.optional.Optional;
import io.reactivex.android.schedulers.AndroidSchedulers;
import chat.rocket.android.BackgroundLooper;
import chat.rocket.android.api.MethodCallHelper;
......@@ -12,6 +11,7 @@ import chat.rocket.android.helper.TextUtils;
import chat.rocket.android.shared.BasePresenter;
import chat.rocket.core.interactors.SessionInteractor;
import chat.rocket.core.models.Session;
import io.reactivex.android.schedulers.AndroidSchedulers;
public class RetryLoginPresenter extends BasePresenter<RetryLoginContract.View>
implements RetryLoginContract.Presenter {
......
package chat.rocket.android.fragment.server_config;
import bolts.Task;
import chat.rocket.android.api.MethodCallHelper;
import chat.rocket.android.shared.BasePresenter;
......@@ -29,6 +30,6 @@ public class TwoStepAuthPresenter extends BasePresenter<TwoStepAuthContract.View
view.showError(task.getError().getMessage());
}
return null;
});
}, Task.UI_THREAD_EXECUTOR);
}
}
......@@ -2,13 +2,14 @@ package chat.rocket.android.fragment.sidebar;
import android.support.annotation.NonNull;
import bolts.Continuation;
import chat.rocket.core.models.RoomSidebar;
import io.reactivex.Flowable;
import java.util.List;
import bolts.Continuation;
import chat.rocket.android.shared.BaseContract;
import chat.rocket.core.models.RoomSidebar;
import chat.rocket.core.models.Spotlight;
import chat.rocket.core.models.User;
import io.reactivex.Flowable;
public interface SidebarMainContract {
......
......@@ -151,6 +151,7 @@ public class SidebarMainFragment extends AbstractFragment implements SidebarMain
adapter.setOnItemClickListener(new RoomListAdapter.OnItemClickListener() {
@Override
public void onItemClick(RoomSidebar roomSidebar) {
searchView.setQuery(null, false);
searchView.clearFocus();
presenter.onRoomSelected(roomSidebar);
}
......
......@@ -5,11 +5,9 @@ import android.os.Bundle;
import android.view.View;
import android.widget.AutoCompleteTextView;
import android.widget.TextView;
import com.hadisatrio.optional.Optional;
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 chat.rocket.android.BackgroundLooper;
......@@ -20,11 +18,14 @@ import chat.rocket.android.helper.Logger;
import chat.rocket.android.helper.TextUtils;
import chat.rocket.android.layouthelper.sidebar.dialog.SuggestUserAdapter;
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.models.ddp.RealmUser;
import chat.rocket.persistence.realm.repositories.RealmServerInfoRepository;
import chat.rocket.persistence.realm.repositories.RealmSessionRepository;
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.
......
package chat.rocket.android.helper;
import com.hadisatrio.optional.Optional;
import io.reactivex.Flowable;
import io.reactivex.Single;
import chat.rocket.android.fragment.chatroom.RocketChatAbsoluteUrl;
import chat.rocket.core.interactors.SessionInteractor;
import chat.rocket.core.repositories.ServerInfoRepository;
import chat.rocket.core.repositories.UserRepository;
import io.reactivex.Flowable;
import io.reactivex.Single;
public class AbsoluteUrlHelper {
......
......@@ -6,9 +6,11 @@ import android.text.format.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Locale;
import java.util.TimeZone;
import chat.rocket.android.log.RCLog;
/**
......@@ -55,7 +57,7 @@ public class DateTime {
case DAY:
return DAY_FORMAT.format(cal.getTime());
case DATE:
return DATE_FORMAT.format(cal.getTime());
return getDateFormat(cal.getTime());
case TIME:
return TIME_FORMAT.format(cal.getTime());
case DATE_TIME:
......@@ -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.
*/
......
......@@ -8,16 +8,18 @@ import android.os.ParcelFileDescriptor;
import android.provider.OpenableColumns;
import android.support.annotation.Nullable;
import android.webkit.MimeTypeMap;
import org.json.JSONObject;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.UUID;
import chat.rocket.android.log.RCLog;
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.internal.FileUploading;
import chat.rocket.persistence.realm.RealmHelper;
/**
* utility class for uploading file.
......
package chat.rocket.android.helper;
import io.realm.Realm;
import io.realm.RealmResults;
import java.util.List;
import chat.rocket.persistence.realm.models.ddp.RealmPublicSetting;
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.
......
package chat.rocket.android.helper;
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.
*/
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) {
// ref: https://github.com/ReactiveX/RxJava/blob/a8ba158839b67246a742b6f1531995ffd7545c08/src/main/java/io/reactivex/Observable.java#L9601
return attempts -> attempts
.zipWith(Observable.range(0, maxRetryCount), (error, retryCount) -> retryCount)
.flatMap(retryCount -> Observable.timer(base * (long) Math.pow(2, retryCount), unit));
.zipWith(Flowable.range(0, maxRetryCount), (error, retryCount) -> retryCount)
.flatMap(retryCount -> Flowable.timer(base * (long) Math.pow(2, retryCount), unit));
}
}
......@@ -2,9 +2,8 @@ package chat.rocket.android.helper;
import android.support.annotation.NonNull;
import io.reactivex.Flowable;
import chat.rocket.android.api.rest.ServerPolicyApi;
import io.reactivex.Flowable;
public class ServerPolicyApiValidationHelper {
......
......@@ -2,9 +2,10 @@ package chat.rocket.android.helper;
import android.support.annotation.NonNull;
import io.reactivex.Flowable;
import org.json.JSONObject;
import io.reactivex.Flowable;
public class ServerPolicyHelper {
private static final String DEFAULT_HOST = ".rocket.chat";
......@@ -12,7 +13,7 @@ public class ServerPolicyHelper {
public static String enforceHostname(String hostname) {
if (hostname == null) {
return "demo.rocket.chat";
return "open.rocket.chat";
}
return removeTrailingSlash(removeProtocol(enforceDefaultHost(hostname)));
......
......@@ -4,6 +4,7 @@ import android.support.annotation.Nullable;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import chat.rocket.android.R;
import chat.rocket.android.helper.DateTime;
import chat.rocket.android.helper.TextUtils;
......@@ -44,7 +45,8 @@ public abstract class AbstractMessageViewHolder extends ModelViewHolder<PairedMe
* bind the view model.
*/
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);
} else {
errorImageView.setVisibility(View.GONE);
......
......@@ -7,6 +7,7 @@ import android.view.View;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import chat.rocket.android.R;
import chat.rocket.android.helper.TextUtils;
import chat.rocket.android.layouthelper.ExtModelListAdapter;
......
package chat.rocket.android.layouthelper.chatroom;
import android.view.View;
import chat.rocket.android.R;
import chat.rocket.android.renderer.MessageRenderer;
import chat.rocket.android.widget.AbsoluteUrl;
......
......@@ -37,7 +37,8 @@ public class MessagePopup {
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 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 Message message;
private CompositeDisposable compositeDisposable = new CompositeDisposable();
......@@ -73,6 +74,7 @@ public class MessagePopup {
.subscribe(
pair -> {
EDIT_ACTION_INFO.allowed = pair.second;
DELETE_ACTION_INFO.allowed = pair.second;
List<Action> allActions = singleton.defaultActions;
List<Action> allowedActions = new ArrayList<>(3);
for (int i = 0; i < allActions.size(); i++) {
......@@ -110,6 +112,7 @@ public class MessagePopup {
singleton.defaultActions.add(QUOTE_ACTION_INFO);
singleton.defaultActions.add(EDIT_ACTION_INFO);
singleton.defaultActions.add(COPY_ACTION_INFO);
singleton.defaultActions.add(DELETE_ACTION_INFO);
}
public static MessagePopup take(Message message) {
......@@ -163,6 +166,11 @@ public class MessagePopup {
return singleton;
}
public MessagePopup setDeleteAction(ActionListener actionListener) {
DELETE_ACTION_INFO.actionListener= actionListener;
return singleton;
}
public MessagePopup setQuoteAction(ActionListener actionListener) {
QUOTE_ACTION_INFO.actionListener = actionListener;
return singleton;
......
......@@ -7,10 +7,13 @@ import android.view.ViewGroup
import android.widget.TextView
import chat.rocket.android.R
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.core.models.Attachment
import kotlinx.android.synthetic.main.day.view.*
import kotlinx.android.synthetic.main.item_room_file.view.*
import java.lang.IllegalArgumentException
import java.sql.Timestamp
/**
......@@ -26,8 +29,16 @@ class RoomFileListAdapter(private var dataSet: List<Attachment>) : RecyclerView.
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val attachment = dataSet[position]
holder.newDay.text = DateTime.fromEpocMs(Timestamp.valueOf(attachment.timestamp).time, DateTime.Format.DATE)
holder.attachment.appendAttachmentView(attachment, true, false)
val timestamp: Timestamp?
try {
timestamp = Timestamp.valueOf(attachment.timestamp)
// If we don't have a timestamp we can parse let's be safe and stop here.
holder.newDay.text = DateTime.fromEpocMs(timestamp.time, DateTime.Format.DATE)
holder.attachment.appendAttachmentView(attachment, true, false)
} catch (e: IllegalArgumentException) {
RCLog.e(e)
Logger.report(e)
}
}
override fun getItemCount(): Int = dataSet.size
......
......@@ -7,13 +7,14 @@ import android.view.View;
import android.view.ViewGroup;
import java.util.List;
import chat.rocket.android.R;
import chat.rocket.android.helper.TextUtils;
import chat.rocket.android.renderer.UserRenderer;
import chat.rocket.android.widget.AbsoluteUrl;
import chat.rocket.core.models.User;
import chat.rocket.persistence.realm.models.ddp.RealmUser;
import chat.rocket.persistence.realm.RealmHelper;
import chat.rocket.android.renderer.UserRenderer;
import chat.rocket.persistence.realm.models.ddp.RealmUser;
/**
* RecyclerView adapter for UsersOfRooms.
......
......@@ -2,9 +2,10 @@ package chat.rocket.android.layouthelper.chatroom.roomlist;
import android.support.annotation.NonNull;
import chat.rocket.core.models.RoomSidebar;
import java.util.List;
import chat.rocket.core.models.Room;
import chat.rocket.core.models.RoomSidebar;
public class ChannelRoomListHeader implements RoomListHeader {
......
......@@ -2,9 +2,10 @@ package chat.rocket.android.layouthelper.chatroom.roomlist;
import android.support.annotation.NonNull;
import chat.rocket.core.models.RoomSidebar;
import java.util.List;
import chat.rocket.core.models.Room;
import chat.rocket.core.models.RoomSidebar;
public class DirectMessageRoomListHeader implements RoomListHeader {
......
......@@ -2,9 +2,10 @@ package chat.rocket.android.layouthelper.chatroom.roomlist;
import android.support.annotation.NonNull;
import chat.rocket.core.models.RoomSidebar;
import java.util.List;
import chat.rocket.core.models.RoomSidebar;
public class FavoriteRoomListHeader implements RoomListHeader {
private final String title;
......
......@@ -5,14 +5,15 @@ import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import chat.rocket.core.models.RoomSidebar;
import chat.rocket.core.models.Spotlight;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import chat.rocket.android.R;
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> {
......
......@@ -2,9 +2,10 @@ package chat.rocket.android.layouthelper.chatroom.roomlist;
import android.support.annotation.NonNull;
import chat.rocket.core.models.RoomSidebar;
import java.util.List;
import chat.rocket.core.models.RoomSidebar;
public interface RoomListHeader {
String getTitle();
......
......@@ -2,9 +2,10 @@ package chat.rocket.android.layouthelper.chatroom.roomlist;
import android.support.annotation.NonNull;
import chat.rocket.core.models.RoomSidebar;
import java.util.List;
import chat.rocket.core.models.RoomSidebar;
public class UnreadRoomListHeader implements RoomListHeader {
private final String title;
......
......@@ -3,6 +3,7 @@ package chat.rocket.android.layouthelper.oauth;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import chat.rocket.android.R;
import chat.rocket.android.fragment.oauth.AbstractOAuthFragment;
import chat.rocket.android.fragment.oauth.FacebookOAuthFragment;
......
......@@ -5,11 +5,12 @@ import android.view.View;
import java.util.Iterator;
import java.util.List;
import chat.rocket.android.R;
import chat.rocket.android.renderer.UserRenderer;
import chat.rocket.android.widget.AbsoluteUrl;
import chat.rocket.persistence.realm.models.ddp.RealmUser;
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.
......
......@@ -5,8 +5,8 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.support.v4.app.RemoteInput;
import android.util.Log;
import chat.rocket.android.push.gcm.GCMIntentService;
......
This diff is collapsed.
package chat.rocket.android.push.gcm;
import com.google.android.gms.gcm.GcmListenerService;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.Resources;
import android.os.Bundle;
import android.util.Log;
import com.google.android.gms.gcm.GcmListenerService;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.Iterator;
import chat.rocket.android.push.PushConstants;
import chat.rocket.android.push.PushNotificationHandler;
import chat.rocket.android.push.PushManager;
@SuppressLint("NewApi")
public class GCMIntentService extends GcmListenerService implements PushConstants {
......@@ -33,9 +35,7 @@ public class GCMIntentService extends GcmListenerService implements PushConstant
extras = normalizeExtras(applicationContext, extras);
PushNotificationHandler pushNotificationHandler = new PushNotificationHandler();
pushNotificationHandler.showNotificationIfPossible(applicationContext, extras);
PushManager.INSTANCE.handle(applicationContext, extras);
}
/*
......
......@@ -3,13 +3,14 @@ package chat.rocket.android.push.gcm;
import com.google.android.gms.iid.InstanceIDListenerService;
import java.util.List;
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.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 {
......
package chat.rocket.android.service;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import java.util.List;
import chat.rocket.core.models.ServerInfo;
import rx.Observable;
import rx.Single;
import io.reactivex.Flowable;
import io.reactivex.Single;
/**
* interfaces used for Activity/Fragment and other UI-related logic.
*/
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;
import java.util.List;
import chat.rocket.core.models.ServerInfo;
/**
* interfaces used for RocketChatService and RocketChatwebSocketThread.
*/
/*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();
......
package chat.rocket.android.service;
import rx.Single;
import io.reactivex.Single;
public interface ConnectivityServiceInterface {
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;
* pair with server's hostname and its connectivity state.
*/
public class ServerConnectivity {
public static final int STATE_CONNECTED = 1;
public static final int STATE_DISCONNECTED = 2;
public static final int STATE_CONNECTING = 3;
/*package*/ static final int STATE_DISCONNECTING = 4;
public static final ServerConnectivity CONNECTED = new ServerConnectivity(null, STATE_CONNECTED);
public final String hostname;
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.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;
import android.content.Context;
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.JSONException;
import org.json.JSONObject;
import java.util.Iterator;
import chat.rocket.android.helper.LogIfError;
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_ddp.DDPClient;
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;
public abstract class AbstractDDPDocEventSubscriber implements Registrable {
protected final Context context;
protected final String hostname;
protected final RealmHelper realmHelper;
protected final DDPClientRef ddpClientRef;
private boolean isUnsubscribed;
private String subscriptionId;
private Disposable rxSubscription;
protected AbstractDDPDocEventSubscriber(Context context, String hostname,
RealmHelper realmHelper, DDPClientRef ddpClientRef) {
RealmHelper realmHelper) {
this.context = context;
this.hostname = hostname;
this.realmHelper = realmHelper;
this.ddpClientRef = ddpClientRef;
}
protected abstract String getSubscriptionName();
......@@ -69,9 +68,9 @@ public abstract class AbstractDDPDocEventSubscriber implements Registrable {
// just ignore.
}
ddpClientRef.get().subscribe(getSubscriptionName(), params).onSuccess(task -> {
DDPClient.get().subscribe(getSubscriptionName(), params).onSuccess(task -> {
if (isUnsubscribed) {
ddpClientRef.get().unsubscribe(task.getResult().id).continueWith(new LogIfError());
DDPClient.get().unsubscribe(task.getResult().id).continueWith(new LogIfError());
} else {
subscriptionId = task.getResult().id;
}
......@@ -98,7 +97,7 @@ public abstract class AbstractDDPDocEventSubscriber implements Registrable {
}
protected Disposable subscribe() {
return ddpClientRef.get().getSubscriptionCallback()
return DDPClient.get().getSubscriptionCallback()
.filter(event -> event instanceof DDPSubscription.DocEvent)
.cast(DDPSubscription.DocEvent.class)
.filter(event -> isTarget(event.collection))
......@@ -197,8 +196,8 @@ public abstract class AbstractDDPDocEventSubscriber implements Registrable {
if (rxSubscription != null) {
rxSubscription.dispose();
}
if (!TextUtils.isEmpty(subscriptionId) && ddpClientRef.get() != null) {
ddpClientRef.get().unsubscribe(subscriptionId).continueWith(new LogIfError());
if (!TextUtils.isEmpty(subscriptionId) && DDPClient.get() != null) {
DDPClient.get().unsubscribe(subscriptionId).continueWith(new LogIfError());
}
}
}
......@@ -3,15 +3,13 @@ package chat.rocket.android.service.ddp.stream;
import android.content.Context;
import chat.rocket.persistence.realm.RealmHelper;
import chat.rocket.android.service.DDPClientRef;
abstract class AbstractStreamNotifyUserEventSubscriber extends AbstractStreamNotifyEventSubscriber {
protected final String userId;
protected AbstractStreamNotifyUserEventSubscriber(Context context, String hostname,
RealmHelper realmHelper,
DDPClientRef ddpClientRef, String userId) {
super(context, hostname, realmHelper, ddpClientRef);
RealmHelper realmHelper, String userId) {
super(context, hostname, realmHelper);
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