Commit 8ea9e334 authored by Rafael Kellermann Streit's avatar Rafael Kellermann Streit Committed by GitHub

Merge branch 'develop' into ux

parents bff56d0d 8d2dfc0f
...@@ -3,26 +3,29 @@ package chat.rocket.android_ddp; ...@@ -3,26 +3,29 @@ 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 android.text.TextUtils; import android.text.TextUtils;
import io.reactivex.Flowable;
import io.reactivex.disposables.CompositeDisposable;
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.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
import bolts.Task; import bolts.Task;
import bolts.TaskCompletionSource; import bolts.TaskCompletionSource;
import chat.rocket.android.log.RCLog; import chat.rocket.android.log.RCLog;
import chat.rocket.android_ddp.rx.RxWebSocket; import chat.rocket.android_ddp.rx.RxWebSocket;
import chat.rocket.android_ddp.rx.RxWebSocketCallback; import chat.rocket.android_ddp.rx.RxWebSocketCallback;
import io.reactivex.Flowable;
import io.reactivex.disposables.CompositeDisposable;
import okhttp3.OkHttpClient; import okhttp3.OkHttpClient;
public class DDPClientImpl { public class DDPClientImpl {
private final DDPClient client; private final DDPClient client;
private final RxWebSocket websocket; private RxWebSocket websocket;
private Flowable<RxWebSocketCallback.Base> flowable; private Flowable<RxWebSocketCallback.Base> flowable;
private CompositeDisposable subscriptions; private CompositeDisposable disposables;
private String currentSession;
public DDPClientImpl(DDPClient self, OkHttpClient client) { public DDPClientImpl(DDPClient self, OkHttpClient client) {
websocket = new RxWebSocket(client); websocket = new RxWebSocket(client);
...@@ -51,26 +54,24 @@ public class DDPClientImpl { ...@@ -51,26 +54,24 @@ public class DDPClientImpl {
public void connect(final TaskCompletionSource<DDPClientCallback.Connect> task, final String url, public void connect(final TaskCompletionSource<DDPClientCallback.Connect> task, final String url,
String session) { String session) {
try { try {
flowable = websocket.connect(url).autoConnect(); flowable = websocket.connect(url).autoConnect(2);
CompositeDisposable subscriptions = new CompositeDisposable(); CompositeDisposable disposables = new CompositeDisposable();
subscriptions.add( disposables.add(
flowable.filter(callback -> callback instanceof RxWebSocketCallback.Open) flowable.retry().filter(callback -> callback instanceof RxWebSocketCallback.Open)
.subscribe( .subscribe(
callback -> { callback ->
sendMessage("connect", sendMessage("connect",
json -> (TextUtils.isEmpty(session) ? json : json.put("session", session)) json -> (TextUtils.isEmpty(session) ? json : json.put("session", DDPClientImpl.this.currentSession))
.put( .put(
"version", "pre2") "version", "pre2")
.put("support", new JSONArray().put("pre2").put("pre1")), .put("support", new JSONArray().put("pre2").put("pre1")),
task); task),
}, RCLog::e
err -> {
}
) )
); );
subscriptions.add( disposables.add(
flowable.filter( flowable.filter(
callback -> callback instanceof RxWebSocketCallback.Message) callback -> callback instanceof RxWebSocketCallback.Message)
.map(callback -> ((RxWebSocketCallback.Message) callback).responseBodyString) .map(callback -> ((RxWebSocketCallback.Message) callback).responseBodyString)
...@@ -79,24 +80,25 @@ public class DDPClientImpl { ...@@ -79,24 +80,25 @@ public class DDPClientImpl {
.subscribe(response -> { .subscribe(response -> {
String msg = extractMsg(response); String msg = extractMsg(response);
if ("connected".equals(msg) && !response.isNull("session")) { if ("connected".equals(msg) && !response.isNull("session")) {
currentSession = response.optString("session");
task.trySetResult( task.trySetResult(
new DDPClientCallback.Connect(client, response.optString("session"))); new DDPClientCallback.Connect(client, response.optString("session")));
subscriptions.dispose(); disposables.clear();
} else if ("error".equals(msg) && "Already connected".equals( } else if ("error".equals(msg) && "Already connected".equals(
response.optString("reason"))) { response.optString("reason"))) {
task.trySetResult(new DDPClientCallback.Connect(client, null)); task.trySetResult(new DDPClientCallback.Connect(client, null));
subscriptions.dispose(); disposables.clear();
} else if ("failed".equals(msg)) { } else if ("failed".equals(msg)) {
task.trySetError( task.trySetError(
new DDPClientCallback.Connect.Failed(client, response.optString("version"))); new DDPClientCallback.Connect.Failed(client, response.optString("version")));
subscriptions.dispose(); disposables.clear();
} }
}, },
err -> task.trySetError(new DDPClientCallback.Connect.Timeout(client)) err -> task.trySetError(new DDPClientCallback.Connect.Timeout(client))
) )
); );
addErrorCallback(subscriptions, task); addErrorCallback(disposables, task);
subscribeBaseListeners(); subscribeBaseListeners();
} catch (Exception e) { } catch (Exception e) {
...@@ -112,34 +114,35 @@ public class DDPClientImpl { ...@@ -112,34 +114,35 @@ public class DDPClientImpl {
sendMessage("ping", json -> json.put("id", id)); sendMessage("ping", json -> json.put("id", id));
if (requested) { if (requested) {
CompositeDisposable subscriptions = new CompositeDisposable(); CompositeDisposable disposables = new CompositeDisposable();
subscriptions.add( disposables.add(
flowable.filter(callback -> callback instanceof RxWebSocketCallback.Message) 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)
.timeout(4, TimeUnit.SECONDS)
.subscribe( .subscribe(
response -> { response -> {
String msg = extractMsg(response); String msg = extractMsg(response);
if ("pong".equals(msg)) { if ("pong".equals(msg)) {
if (response.isNull("id")) { if (response.isNull("id")) {
task.setResult(new DDPClientCallback.Ping(client, null)); task.setResult(new DDPClientCallback.Ping(client, null));
subscriptions.dispose(); disposables.clear();
} else { } else {
String _id = response.optString("id"); String _id = response.optString("id");
if (id.equals(_id)) { if (id.equals(_id)) {
task.setResult(new DDPClientCallback.Ping(client, id)); task.setResult(new DDPClientCallback.Ping(client, id));
subscriptions.dispose(); disposables.clear();
} }
} }
disposables.clear();
} }
}, },
err -> task.setError(new DDPClientCallback.Ping.Timeout(client)) err -> task.setError(new DDPClientCallback.Ping.Timeout(client))
) )
); );
addErrorCallback(subscriptions, task); addErrorCallback(disposables, task);
} else { } else {
task.trySetError(new DDPClientCallback.Closed(client)); task.trySetError(new DDPClientCallback.Closed(client));
} }
...@@ -151,9 +154,9 @@ public class DDPClientImpl { ...@@ -151,9 +154,9 @@ public class DDPClientImpl {
sendMessage("sub", json -> json.put("id", id).put("name", name).put("params", params)); sendMessage("sub", json -> json.put("id", id).put("name", name).put("params", params));
if (requested) { if (requested) {
CompositeDisposable subscriptions = new CompositeDisposable(); CompositeDisposable disposables = new CompositeDisposable();
subscriptions.add( disposables.add(
flowable.filter(callback -> callback instanceof RxWebSocketCallback.Message) flowable.filter(callback -> callback instanceof RxWebSocketCallback.Message)
.map(callback -> ((RxWebSocketCallback.Message) callback).responseBodyString) .map(callback -> ((RxWebSocketCallback.Message) callback).responseBodyString)
.map(DDPClientImpl::toJson) .map(DDPClientImpl::toJson)
...@@ -166,7 +169,7 @@ public class DDPClientImpl { ...@@ -166,7 +169,7 @@ public class DDPClientImpl {
String _id = ids.optString(i); String _id = ids.optString(i);
if (id.equals(_id)) { if (id.equals(_id)) {
task.setResult(new DDPSubscription.Ready(client, id)); task.setResult(new DDPSubscription.Ready(client, id));
subscriptions.dispose(); disposables.clear();
break; break;
} }
} }
...@@ -176,16 +179,15 @@ public class DDPClientImpl { ...@@ -176,16 +179,15 @@ public class DDPClientImpl {
if (id.equals(_id)) { if (id.equals(_id)) {
task.setError(new DDPSubscription.NoSub.Error(client, id, task.setError(new DDPSubscription.NoSub.Error(client, id,
response.optJSONObject("error"))); response.optJSONObject("error")));
subscriptions.dispose(); disposables.clear();
} }
} }
}, },
err -> { RCLog::e
}
) )
); );
addErrorCallback(subscriptions, task); addErrorCallback(disposables, task);
} else { } else {
task.trySetError(new DDPClientCallback.Closed(client)); task.trySetError(new DDPClientCallback.Closed(client));
} }
...@@ -197,9 +199,9 @@ public class DDPClientImpl { ...@@ -197,9 +199,9 @@ public class DDPClientImpl {
final boolean requested = sendMessage("unsub", json -> json.put("id", id)); final boolean requested = sendMessage("unsub", json -> json.put("id", id));
if (requested) { if (requested) {
CompositeDisposable subscriptions = new CompositeDisposable(); CompositeDisposable disposables = new CompositeDisposable();
subscriptions.add( disposables.add(
flowable.filter(callback -> callback instanceof RxWebSocketCallback.Message) flowable.filter(callback -> callback instanceof RxWebSocketCallback.Message)
.map(callback -> ((RxWebSocketCallback.Message) callback).responseBodyString) .map(callback -> ((RxWebSocketCallback.Message) callback).responseBodyString)
.map(DDPClientImpl::toJson) .map(DDPClientImpl::toJson)
...@@ -210,7 +212,7 @@ public class DDPClientImpl { ...@@ -210,7 +212,7 @@ public class DDPClientImpl {
String _id = response.optString("id"); String _id = response.optString("id");
if (id.equals(_id)) { if (id.equals(_id)) {
task.setResult(new DDPSubscription.NoSub(client, id)); task.setResult(new DDPSubscription.NoSub(client, id));
subscriptions.dispose(); disposables.clear();
} }
} }
}, },
...@@ -219,7 +221,7 @@ public class DDPClientImpl { ...@@ -219,7 +221,7 @@ public class DDPClientImpl {
) )
); );
addErrorCallback(subscriptions, task); addErrorCallback(disposables, task);
} else { } else {
task.trySetError(new DDPClientCallback.Closed(client)); task.trySetError(new DDPClientCallback.Closed(client));
} }
...@@ -232,9 +234,9 @@ public class DDPClientImpl { ...@@ -232,9 +234,9 @@ public class DDPClientImpl {
json -> json.put("method", method).put("params", params).put("id", id)); json -> json.put("method", method).put("params", params).put("id", id));
if (requested) { if (requested) {
CompositeDisposable subscriptions = new CompositeDisposable(); CompositeDisposable disposables = new CompositeDisposable();
subscriptions.add( disposables.add(
flowable.filter(callback -> callback instanceof RxWebSocketCallback.Message) flowable.filter(callback -> callback instanceof RxWebSocketCallback.Message)
.map(callback -> ((RxWebSocketCallback.Message) callback).responseBodyString) .map(callback -> ((RxWebSocketCallback.Message) callback).responseBodyString)
.map(DDPClientImpl::toJson) .map(DDPClientImpl::toJson)
...@@ -252,7 +254,7 @@ public class DDPClientImpl { ...@@ -252,7 +254,7 @@ public class DDPClientImpl {
String result = response.optString("result"); String result = response.optString("result");
task.setResult(new DDPClientCallback.RPC(client, id, result)); task.setResult(new DDPClientCallback.RPC(client, id, result));
} }
subscriptions.dispose(); disposables.clear();
} }
} }
}, },
...@@ -264,20 +266,20 @@ public class DDPClientImpl { ...@@ -264,20 +266,20 @@ public class DDPClientImpl {
) )
); );
addErrorCallback(subscriptions, task); addErrorCallback(disposables, task);
} else { } else {
task.trySetError(new DDPClientCallback.Closed(client)); task.trySetError(new DDPClientCallback.Closed(client));
} }
} }
private void subscribeBaseListeners() { private void subscribeBaseListeners() {
if (subscriptions != null && if (disposables != null &&
subscriptions.size() > 0 && !subscriptions.isDisposed()) { disposables.size() > 0 && !disposables.isDisposed()) {
return; return;
} }
subscriptions = new CompositeDisposable(); disposables = new CompositeDisposable();
subscriptions.add( disposables.add(
flowable.filter(callback -> callback instanceof RxWebSocketCallback.Message) flowable.filter(callback -> callback instanceof RxWebSocketCallback.Message)
.map(callback -> ((RxWebSocketCallback.Message) callback).responseBodyString) .map(callback -> ((RxWebSocketCallback.Message) callback).responseBodyString)
.map(DDPClientImpl::toJson) .map(DDPClientImpl::toJson)
...@@ -292,8 +294,7 @@ public class DDPClientImpl { ...@@ -292,8 +294,7 @@ public class DDPClientImpl {
} }
} }
}, },
err -> { RCLog::e
}
) )
); );
} }
...@@ -342,8 +343,8 @@ public class DDPClientImpl { ...@@ -342,8 +343,8 @@ public class DDPClientImpl {
} }
public void unsubscribeBaseListeners() { public void unsubscribeBaseListeners() {
if (subscriptions.size() > 0 || !subscriptions.isDisposed()) { if (disposables.size() > 0 || !disposables.isDisposed()) {
subscriptions.dispose(); disposables.clear();
} }
} }
...@@ -354,13 +355,7 @@ public class DDPClientImpl { ...@@ -354,13 +355,7 @@ public class DDPClientImpl {
.cast(RxWebSocketCallback.Close.class) .cast(RxWebSocketCallback.Close.class)
.subscribe( .subscribe(
task::setResult, task::setResult,
err -> { err -> setTaskError(task, err)
if (err instanceof Exception) {
task.setError((Exception) err);
} else {
task.setError(new Exception(err));
}
}
); );
return task.getTask().onSuccessTask(_task -> { return task.getTask().onSuccessTask(_task -> {
...@@ -373,28 +368,29 @@ public class DDPClientImpl { ...@@ -373,28 +368,29 @@ public class DDPClientImpl {
try { try {
JSONObject origJson = new JSONObject().put("msg", msg); JSONObject origJson = new JSONObject().put("msg", msg);
String msg2 = (json == null ? origJson : json.create(origJson)).toString(); String msg2 = (json == null ? origJson : json.create(origJson)).toString();
return websocket.sendText(msg2); websocket.sendText(msg2);
} catch (Exception e) { } catch (Exception e) {
RCLog.e(e); RCLog.e(e);
return false;
} }
return true; // ignore exception here. return true; // ignore exception here.
} }
private void sendMessage(String msg, @Nullable JSONBuilder json, private void sendMessage(String msg, @Nullable JSONBuilder json,
TaskCompletionSource<?> taskForSetError) { TaskCompletionSource<?> taskForSetError) {
if (!sendMessage(msg, json)) { if (!sendMessage(msg, json) && taskForSetError != null) {
taskForSetError.trySetError(new DDPClientCallback.Closed(client)); taskForSetError.trySetError(new DDPClientCallback.Closed(client));
} }
} }
private void addErrorCallback(CompositeDisposable subscriptions, TaskCompletionSource<?> task) { private void addErrorCallback(CompositeDisposable disposables, TaskCompletionSource<?> task) {
subscriptions.add( disposables.add(
flowable.subscribe( flowable.subscribe(
base -> { base -> {
}, },
err -> { err -> {
task.trySetError(new Exception(err)); task.trySetError(new Exception(err));
subscriptions.dispose(); disposables.clear();
} }
) )
); );
...@@ -408,6 +404,14 @@ public class DDPClientImpl { ...@@ -408,6 +404,14 @@ public class DDPClientImpl {
} }
} }
private void setTaskError(TaskCompletionSource<? extends RxWebSocketCallback.Base> task, Throwable throwable) {
if (throwable instanceof Exception) {
task.setError((Exception) throwable);
} else {
task.setError(new Exception(throwable));
}
}
private interface JSONBuilder { private interface JSONBuilder {
@NonNull @NonNull
JSONObject create(JSONObject root) throws JSONException; JSONObject create(JSONObject root) throws JSONException;
......
package chat.rocket.android_ddp.rx; 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; import io.reactivex.BackpressureStrategy;
import io.reactivex.Flowable; import io.reactivex.Flowable;
import io.reactivex.FlowableOnSubscribe; import io.reactivex.FlowableOnSubscribe;
import io.reactivex.exceptions.OnErrorNotImplementedException; import io.reactivex.exceptions.OnErrorNotImplementedException;
import io.reactivex.flowables.ConnectableFlowable; import io.reactivex.flowables.ConnectableFlowable;
import java.io.IOException;
import chat.rocket.android.log.RCLog;
import okhttp3.OkHttpClient; import okhttp3.OkHttpClient;
import okhttp3.Request; import okhttp3.Request;
import okhttp3.Response; import okhttp3.Response;
...@@ -15,8 +16,10 @@ import okhttp3.WebSocket; ...@@ -15,8 +16,10 @@ import okhttp3.WebSocket;
import okhttp3.WebSocketListener; import okhttp3.WebSocketListener;
public class RxWebSocket { public class RxWebSocket {
public static final int REASON_NETWORK_ERROR = 100;
private OkHttpClient httpClient; private OkHttpClient httpClient;
private WebSocket webSocket; private WebSocket webSocket;
private boolean hadErrorsBefore;
public RxWebSocket(OkHttpClient client) { public RxWebSocket(OkHttpClient client) {
httpClient = client; httpClient = client;
...@@ -30,6 +33,7 @@ public class RxWebSocket { ...@@ -30,6 +33,7 @@ public class RxWebSocket {
.newWebSocket(request, new WebSocketListener() { .newWebSocket(request, new WebSocketListener() {
@Override @Override
public void onOpen(WebSocket webSocket, Response response) { public void onOpen(WebSocket webSocket, Response response) {
hadErrorsBefore = false;
RxWebSocket.this.webSocket = webSocket; RxWebSocket.this.webSocket = webSocket;
emitter.onNext(new RxWebSocketCallback.Open(RxWebSocket.this.webSocket, response)); emitter.onNext(new RxWebSocketCallback.Open(RxWebSocket.this.webSocket, response));
} }
...@@ -37,7 +41,11 @@ public class RxWebSocket { ...@@ -37,7 +41,11 @@ public class RxWebSocket {
@Override @Override
public void onFailure(WebSocket webSocket, Throwable err, Response response) { public void onFailure(WebSocket webSocket, Throwable err, Response response) {
try { try {
emitter.onError(new RxWebSocketCallback.Failure(webSocket, err, response)); if (!hadErrorsBefore) {
hadErrorsBefore = true;
emitter.onNext(new RxWebSocketCallback.Close(webSocket, REASON_NETWORK_ERROR, err.getMessage()));
emitter.onComplete();
}
} catch (OnErrorNotImplementedException ex) { } catch (OnErrorNotImplementedException ex) {
RCLog.w(ex, "OnErrorNotImplementedException ignored"); RCLog.w(ex, "OnErrorNotImplementedException ignored");
} }
...@@ -51,11 +59,10 @@ public class RxWebSocket { ...@@ -51,11 +59,10 @@ public class RxWebSocket {
@Override @Override
public void onClosed(WebSocket webSocket, int code, String reason) { public void onClosed(WebSocket webSocket, int code, String reason) {
emitter.onNext(new RxWebSocketCallback.Close(webSocket, code, reason)); emitter.onNext(new RxWebSocketCallback.Close(webSocket, code, reason));
emitter.onComplete();
} }
}), }),
BackpressureStrategy.BUFFER BackpressureStrategy.BUFFER
).publish(); ).delay(4, TimeUnit.SECONDS).publish();
} }
public boolean sendText(String message) throws IOException { public boolean sendText(String message) throws IOException {
......
package chat.rocket.android_ddp.rx; package chat.rocket.android_ddp.rx;
import static android.R.attr.type;
import chat.rocket.android.log.RCLog; import chat.rocket.android.log.RCLog;
import okhttp3.Response; import okhttp3.Response;
import okhttp3.WebSocket; import okhttp3.WebSocket;
...@@ -28,25 +26,8 @@ public class RxWebSocketCallback { ...@@ -28,25 +26,8 @@ public class RxWebSocketCallback {
public Open(WebSocket websocket, Response response) { public Open(WebSocket websocket, Response response) {
super("Open", websocket); super("Open", websocket);
this.response = response; this.response = response;
} if (response != null && response.body() != null) {
} this.response.body().close();
public static class Failure extends Exception {
public WebSocket ws;
public Response response;
public Failure(WebSocket websocket, Throwable err, Response response) {
super(err);
this.ws = websocket;
this.response = response;
}
@Override
public String toString() {
if (response != null) {
return "[" + type + "] " + response.message();
} else {
return super.toString();
} }
} }
} }
......
...@@ -35,7 +35,7 @@ android { ...@@ -35,7 +35,7 @@ android {
applicationId "chat.rocket.android" applicationId "chat.rocket.android"
minSdkVersion 16 minSdkVersion 16
targetSdkVersion rootProject.ext.targetSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 26 versionCode 27
versionName "1.0.16" versionName "1.0.16"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
......
...@@ -16,11 +16,11 @@ import chat.rocket.android.fragment.chatroom.HomeFragment; ...@@ -16,11 +16,11 @@ import chat.rocket.android.fragment.chatroom.HomeFragment;
import chat.rocket.android.fragment.chatroom.RoomFragment; import chat.rocket.android.fragment.chatroom.RoomFragment;
import chat.rocket.android.fragment.sidebar.SidebarMainFragment; import chat.rocket.android.fragment.sidebar.SidebarMainFragment;
import chat.rocket.android.helper.KeyboardHelper; import chat.rocket.android.helper.KeyboardHelper;
import chat.rocket.android.service.ConnectivityManager;
import chat.rocket.android.widget.RoomToolbar;
import chat.rocket.core.interactors.CanCreateRoomInteractor; import chat.rocket.core.interactors.CanCreateRoomInteractor;
import chat.rocket.core.interactors.RoomInteractor; import chat.rocket.core.interactors.RoomInteractor;
import chat.rocket.core.interactors.SessionInteractor; import chat.rocket.core.interactors.SessionInteractor;
import chat.rocket.android.service.ConnectivityManager;
import chat.rocket.android.widget.RoomToolbar;
import chat.rocket.persistence.realm.repositories.RealmRoomRepository; import chat.rocket.persistence.realm.repositories.RealmRoomRepository;
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;
...@@ -34,6 +34,7 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract ...@@ -34,6 +34,7 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract
private StatusTicker statusTicker; private StatusTicker statusTicker;
private MainContract.Presenter presenter; private MainContract.Presenter presenter;
private RoomFragment roomFragment;
@Override @Override
protected int getLayoutContainerForFragment() { protected int getLayoutContainerForFragment() {
...@@ -179,7 +180,8 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract ...@@ -179,7 +180,8 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract
@Override @Override
public void showRoom(String hostname, String roomId) { public void showRoom(String hostname, String roomId) {
showFragment(RoomFragment.create(hostname, roomId)); roomFragment = RoomFragment.create(hostname, roomId);
showFragment(roomFragment);
closeSidebarIfNeeded(); closeSidebarIfNeeded();
KeyboardHelper.hideSoftKeyboard(this); KeyboardHelper.hideSoftKeyboard(this);
} }
...@@ -222,6 +224,9 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract ...@@ -222,6 +224,9 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract
@Override @Override
public void showConnectionOk() { public void showConnectionOk() {
statusTicker.updateStatus(StatusTicker.STATUS_DISMISS, null); statusTicker.updateStatus(StatusTicker.STATUS_DISMISS, null);
if (roomFragment != null) {
roomFragment.refreshRoom();
}
} }
//TODO: consider this class to define in layouthelper for more complicated operation. //TODO: consider this class to define in layouthelper for more complicated operation.
......
...@@ -3,22 +3,23 @@ package chat.rocket.android.activity; ...@@ -3,22 +3,23 @@ package chat.rocket.android.activity;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.v4.util.Pair; import android.support.v4.util.Pair;
import io.reactivex.Flowable;
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.RocketChatCache; import chat.rocket.android.RocketChatCache;
import chat.rocket.android.api.MethodCallHelper; import chat.rocket.android.api.MethodCallHelper;
import chat.rocket.android.helper.LogIfError; import chat.rocket.android.helper.LogIfError;
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.service.ServerConnectivity;
import chat.rocket.android.shared.BasePresenter; import chat.rocket.android.shared.BasePresenter;
import chat.rocket.core.interactors.CanCreateRoomInteractor; import chat.rocket.core.interactors.CanCreateRoomInteractor;
import chat.rocket.core.interactors.RoomInteractor; import chat.rocket.core.interactors.RoomInteractor;
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 chat.rocket.core.models.User; import chat.rocket.core.models.User;
import hu.akarnokd.rxjava.interop.RxJavaInterop;
import io.reactivex.Flowable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
public class MainPresenter extends BasePresenter<MainContract.View> public class MainPresenter extends BasePresenter<MainContract.View>
implements MainContract.Presenter { implements MainContract.Presenter {
...@@ -63,6 +64,7 @@ public class MainPresenter extends BasePresenter<MainContract.View> ...@@ -63,6 +64,7 @@ public class MainPresenter extends BasePresenter<MainContract.View>
openRoom(); openRoom();
subscribeToNetworkChanges();
subscribeToUnreadCount(); subscribeToUnreadCount();
subscribeToSession(); subscribeToSession();
setUserOnline(); setUserOnline();
...@@ -96,10 +98,8 @@ public class MainPresenter extends BasePresenter<MainContract.View> ...@@ -96,10 +98,8 @@ public class MainPresenter extends BasePresenter<MainContract.View>
@Override @Override
public void onRetryLogin() { public void onRetryLogin() {
final Disposable subscription = sessionInteractor.retryLogin() view.showConnecting();
.subscribe(); connectivityManagerApi.keepAliveServer();
addSubscription(subscription);
} }
private void openRoom() { private void openRoom() {
...@@ -161,6 +161,23 @@ public class MainPresenter extends BasePresenter<MainContract.View> ...@@ -161,6 +161,23 @@ public class MainPresenter extends BasePresenter<MainContract.View>
addSubscription(subscription); addSubscription(subscription);
} }
private void subscribeToNetworkChanges() {
Disposable disposable = RxJavaInterop.toV2Flowable(connectivityManagerApi.getServerConnectivityAsObservable())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
connectivity -> {
if (connectivity.state == ServerConnectivity.STATE_CONNECTED) {
view.showConnectionOk();
return;
}
view.showConnecting();
},
Logger::report
);
addSubscription(disposable);
}
private void setUserOnline() { private void setUserOnline() {
methodCallHelper.setUserPresence(User.STATUS_ONLINE) methodCallHelper.setUserPresence(User.STATUS_ONLINE)
.continueWith(new LogIfError()); .continueWith(new LogIfError());
......
package chat.rocket.android.api; package chat.rocket.android.api;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import io.reactivex.Flowable;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONException; import org.json.JSONException;
import java.util.UUID; import java.util.UUID;
import bolts.Task; import bolts.Task;
import chat.rocket.android.helper.OkHttpHelper; import chat.rocket.android.helper.OkHttpHelper;
import chat.rocket.android.helper.TextUtils; import chat.rocket.android.helper.TextUtils;
...@@ -13,6 +14,7 @@ import chat.rocket.android.log.RCLog; ...@@ -13,6 +14,7 @@ import chat.rocket.android.log.RCLog;
import chat.rocket.android_ddp.DDPClient; import chat.rocket.android_ddp.DDPClient;
import chat.rocket.android_ddp.DDPClientCallback; import chat.rocket.android_ddp.DDPClientCallback;
import chat.rocket.android_ddp.DDPSubscription; import chat.rocket.android_ddp.DDPSubscription;
import io.reactivex.Flowable;
/** /**
* DDP client wrapper. * DDP client wrapper.
......
...@@ -49,5 +49,7 @@ public interface RoomContract { ...@@ -49,5 +49,7 @@ public interface RoomContract {
void onUnreadCount(); void onUnreadCount();
void onMarkAsRead(); void onMarkAsRead();
void refreshRoom();
} }
} }
...@@ -21,6 +21,11 @@ import android.support.v7.widget.LinearLayoutManager; ...@@ -21,6 +21,11 @@ import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import chat.rocket.android.BackgroundLooper; import chat.rocket.android.BackgroundLooper;
import chat.rocket.android.R; import chat.rocket.android.R;
import chat.rocket.android.api.MethodCallHelper; import chat.rocket.android.api.MethodCallHelper;
...@@ -70,15 +75,13 @@ import chat.rocket.persistence.realm.repositories.RealmSessionRepository; ...@@ -70,15 +75,13 @@ import chat.rocket.persistence.realm.repositories.RealmSessionRepository;
import chat.rocket.persistence.realm.repositories.RealmSpotlightRoomRepository; import chat.rocket.persistence.realm.repositories.RealmSpotlightRoomRepository;
import chat.rocket.persistence.realm.repositories.RealmSpotlightUserRepository; import chat.rocket.persistence.realm.repositories.RealmSpotlightUserRepository;
import chat.rocket.persistence.realm.repositories.RealmUserRepository; import chat.rocket.persistence.realm.repositories.RealmUserRepository;
import com.fernandocejas.arrow.optional.Optional;
import com.hadisatrio.optional.Optional;
import com.jakewharton.rxbinding2.support.v4.widget.RxDrawerLayout; import com.jakewharton.rxbinding2.support.v4.widget.RxDrawerLayout;
import io.reactivex.Single; import io.reactivex.Single;
import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable; import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable; import io.reactivex.disposables.Disposable;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import permissions.dispatcher.NeedsPermission; import permissions.dispatcher.NeedsPermission;
import permissions.dispatcher.RuntimePermissions; import permissions.dispatcher.RuntimePermissions;
...@@ -188,7 +191,7 @@ public class RoomFragment extends AbstractChatRoomFragment implements ...@@ -188,7 +191,7 @@ public class RoomFragment extends AbstractChatRoomFragment implements
protected void onSetupView() { protected void onSetupView() {
messageRecyclerView = rootView.findViewById(R.id.messageRecyclerView); messageRecyclerView = rootView.findViewById(R.id.messageRecyclerView);
messageListAdapter = new MessageListAdapter(getContext()); messageListAdapter = new MessageListAdapter(getContext(), hostname);
messageRecyclerView.setAdapter(messageListAdapter); messageRecyclerView.setAdapter(messageListAdapter);
messageListAdapter.setOnItemClickListener(this); messageListAdapter.setOnItemClickListener(this);
messageListAdapter.setOnItemLongClickListener(this); messageListAdapter.setOnItemLongClickListener(this);
...@@ -251,8 +254,8 @@ public class RoomFragment extends AbstractChatRoomFragment implements ...@@ -251,8 +254,8 @@ public class RoomFragment extends AbstractChatRoomFragment implements
protected Snackbar getUnreadCountIndicatorView(int count) { protected Snackbar getUnreadCountIndicatorView(int count) {
// TODO: replace with another custom View widget, not to hide message composer. // TODO: replace with another custom View widget, not to hide message composer.
final String caption = getResources().getString( final String caption = getResources().getQuantityString(
R.string.fmt_dialog_view_latest_message_title, count); R.plurals.fmt_dialog_view_latest_message_title, count, count);
return Snackbar.make(rootView, caption, Snackbar.LENGTH_LONG) return Snackbar.make(rootView, caption, Snackbar.LENGTH_LONG)
.setAction(R.string.dialog_view_latest_message_action, view -> scrollToLatestMessage()); .setAction(R.string.dialog_view_latest_message_action, view -> scrollToLatestMessage());
...@@ -369,7 +372,7 @@ public class RoomFragment extends AbstractChatRoomFragment implements ...@@ -369,7 +372,7 @@ public class RoomFragment extends AbstractChatRoomFragment implements
new DefaultTempSpotlightUserCaller(methodCallHelper) new DefaultTempSpotlightUserCaller(methodCallHelper)
), ),
pair.first.get(), pair.first.get(),
RocketChatUserStatusProvider.getInstance(), RocketChatUserStatusProvider.INSTANCE,
AndroidSchedulers.from(BackgroundLooper.get()), AndroidSchedulers.from(BackgroundLooper.get()),
AndroidSchedulers.mainThread() AndroidSchedulers.mainThread()
) )
...@@ -599,4 +602,8 @@ public class RoomFragment extends AbstractChatRoomFragment implements ...@@ -599,4 +602,8 @@ public class RoomFragment extends AbstractChatRoomFragment implements
edittingMessage = message; edittingMessage = message;
messageFormManager.setEditMessage(message.getMessage()); messageFormManager.setEditMessage(message.getMessage());
} }
public void refreshRoom() {
presenter.loadMessages();
}
} }
\ No newline at end of file
...@@ -4,7 +4,7 @@ import android.support.annotation.NonNull; ...@@ -4,7 +4,7 @@ import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.v4.util.Pair; import android.support.v4.util.Pair;
import com.fernandocejas.arrow.optional.Optional; import com.hadisatrio.optional.Optional;
import io.reactivex.Single; import io.reactivex.Single;
import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable; import io.reactivex.disposables.Disposable;
...@@ -55,7 +55,11 @@ public class RoomPresenter extends BasePresenter<RoomContract.View> ...@@ -55,7 +55,11 @@ public class RoomPresenter extends BasePresenter<RoomContract.View>
@Override @Override
public void bindView(@NonNull RoomContract.View view) { public void bindView(@NonNull RoomContract.View view) {
super.bindView(view); super.bindView(view);
refreshRoom();
}
@Override
public void refreshRoom() {
getRoomRoles(); getRoomRoles();
getRoomInfo(); getRoomInfo();
getRoomHistoryStateInfo(); getRoomHistoryStateInfo();
......
...@@ -7,7 +7,7 @@ import android.support.v7.widget.GridLayoutManager; ...@@ -7,7 +7,7 @@ import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView;
import android.view.View; import android.view.View;
import android.widget.TextView; import android.widget.TextView;
import com.fernandocejas.arrow.optional.Optional; import com.hadisatrio.optional.Optional;
import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable; import io.reactivex.disposables.CompositeDisposable;
import org.json.JSONArray; import org.json.JSONArray;
...@@ -117,7 +117,7 @@ public class UsersOfRoomDialogFragment extends AbstractChatRoomDialogFragment { ...@@ -117,7 +117,7 @@ public class UsersOfRoomDialogFragment extends AbstractChatRoomDialogFragment {
RecyclerView recyclerView = (RecyclerView) getDialog().findViewById(R.id.recyclerview); RecyclerView recyclerView = (RecyclerView) getDialog().findViewById(R.id.recyclerview);
recyclerView.setLayoutManager(new GridLayoutManager(getContext(), 2)); recyclerView.setLayoutManager(new GridLayoutManager(getContext(), 2));
recyclerView.setAdapter( recyclerView.setAdapter(
new RoomUserAdapter(getContext(), realmHelper, rocketChatAbsoluteUrlOptional.get())); new RoomUserAdapter(getContext(), realmHelper, rocketChatAbsoluteUrlOptional.get(), hostname));
} }
private void requestGetUsersOfRoom() { private void requestGetUsersOfRoom() {
...@@ -192,7 +192,7 @@ public class UsersOfRoomDialogFragment extends AbstractChatRoomDialogFragment { ...@@ -192,7 +192,7 @@ public class UsersOfRoomDialogFragment extends AbstractChatRoomDialogFragment {
*/ */
private void onRenderTotalCount(long total) { private void onRenderTotalCount(long total) {
TextView userCount = (TextView) getDialog().findViewById(R.id.room_user_count); TextView userCount = (TextView) getDialog().findViewById(R.id.room_user_count);
userCount.setText(getString(R.string.fmt_room_user_count, total)); userCount.setText(getResources().getQuantityString(R.plurals.fmt_room_user_count, (int) total, total));
} }
/** /**
......
...@@ -2,7 +2,7 @@ package chat.rocket.android.fragment.server_config; ...@@ -2,7 +2,7 @@ package chat.rocket.android.fragment.server_config;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import com.fernandocejas.arrow.optional.Optional; import com.hadisatrio.optional.Optional;
import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.android.schedulers.AndroidSchedulers;
import bolts.Task; import bolts.Task;
......
...@@ -2,7 +2,7 @@ package chat.rocket.android.fragment.server_config; ...@@ -2,7 +2,7 @@ package chat.rocket.android.fragment.server_config;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import com.fernandocejas.arrow.optional.Optional; import com.hadisatrio.optional.Optional;
import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.android.schedulers.AndroidSchedulers;
import chat.rocket.android.BackgroundLooper; import chat.rocket.android.BackgroundLooper;
......
...@@ -224,10 +224,10 @@ public class SidebarMainFragment extends AbstractFragment implements SidebarMain ...@@ -224,10 +224,10 @@ public class SidebarMainFragment extends AbstractFragment implements SidebarMain
private void onRenderCurrentUser(User user, RocketChatAbsoluteUrl absoluteUrl) { private void onRenderCurrentUser(User user, RocketChatAbsoluteUrl absoluteUrl) {
if (user != null && absoluteUrl != null) { if (user != null && absoluteUrl != null) {
new UserRenderer(getContext(), user) UserRenderer userRenderer = new UserRenderer(user);
.avatarInto(rootView.findViewById(R.id.current_user_avatar), absoluteUrl) userRenderer.showAvatar(rootView.findViewById(R.id.current_user_avatar), hostname);
.usernameInto(rootView.findViewById(R.id.current_user_name)) userRenderer.showUsername(rootView.findViewById(R.id.current_user_name));
.statusColorInto(rootView.findViewById(R.id.current_user_status)); userRenderer.showStatusColor(rootView.findViewById(R.id.current_user_status));
} }
} }
......
...@@ -5,7 +5,7 @@ import android.os.Bundle; ...@@ -5,7 +5,7 @@ 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.fernandocejas.arrow.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.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable; import io.reactivex.disposables.CompositeDisposable;
...@@ -97,7 +97,7 @@ public class AddDirectMessageDialogFragment extends AbstractAddRoomDialogFragmen ...@@ -97,7 +97,7 @@ public class AddDirectMessageDialogFragment extends AbstractAddRoomDialogFragmen
(realm, text) -> realm.where(RealmUser.class) (realm, text) -> realm.where(RealmUser.class)
.contains(RealmUser.USERNAME, text, Case.INSENSITIVE) .contains(RealmUser.USERNAME, text, Case.INSENSITIVE)
.findAllSorted(RealmUser.USERNAME), .findAllSorted(RealmUser.USERNAME),
context -> new SuggestUserAdapter(context, rocketChatAbsoluteUrlOptional.get())); context -> new SuggestUserAdapter(context, rocketChatAbsoluteUrlOptional.get(), hostname));
autoCompleteTextView.setAdapter(adapter); autoCompleteTextView.setAdapter(adapter);
} }
......
package chat.rocket.android.helper; package chat.rocket.android.helper;
import com.fernandocejas.arrow.optional.Optional; import com.hadisatrio.optional.Optional;
import io.reactivex.Flowable; import io.reactivex.Flowable;
import io.reactivex.Single; import io.reactivex.Single;
......
package chat.rocket.android.helper
import chat.rocket.android.widget.AbsoluteUrl
import chat.rocket.android.widget.RocketChatAvatar
import java.net.URLEncoder
class Avatar(val absoluteUrl: AbsoluteUrl?, val username: String) {
val imageUrl: String
/** REMARK
* This is often a SVG image (see Rocket.Chat:server/startup/avatar.js)
*/
get() {
val avatarUrl = "/avatar/" + URLEncoder.encode(username, "UTF-8")
if (absoluteUrl == null) {
return avatarUrl
}
return absoluteUrl.from("/avatar/" + URLEncoder.encode(username, "UTF-8"))
}
/**
* render avatar into RocketChatAvatar.
*/
fun into(rocketChatAvatar: RocketChatAvatar) {
rocketChatAvatar.loadImage(imageUrl)
}
}
\ No newline at end of file
package chat.rocket.android.helper
import java.net.URLEncoder
class RocketChatUserAvatar(val hostname: String, val username: String) {
val imageUri: String
/** REMARK
* This is often a SVG image (see Rocket.Chat:server/startup/avatar.js).
*/
get() {
return "https://" +
hostname.replace("http://", "").replace("https://", "") +
"/avatar/" +
URLEncoder.encode(username, "UTF-8")
}
}
\ No newline at end of file
...@@ -13,21 +13,24 @@ import chat.rocket.core.SyncState; ...@@ -13,21 +13,24 @@ import chat.rocket.core.SyncState;
public abstract class AbstractMessageViewHolder extends ModelViewHolder<PairedMessage> { public abstract class AbstractMessageViewHolder extends ModelViewHolder<PairedMessage> {
protected final RocketChatAvatar avatar; protected final RocketChatAvatar avatar;
protected final ImageView userNotFoundAvatarImageView;
protected final ImageView errorImageView; protected final ImageView errorImageView;
protected final TextView username; protected final TextView username;
protected final TextView subUsername; protected final TextView subUsername;
protected final TextView timestamp; protected final TextView timestamp;
protected final View userAndTimeContainer; protected final View userAndTimeContainer;
protected final AbsoluteUrl absoluteUrl; protected final AbsoluteUrl absoluteUrl;
protected final String hostname;
protected final View newDayContainer; protected final View newDayContainer;
protected final TextView newDayText; protected final TextView newDayText;
/** /**
* constructor WITH hostname. * constructor WITH hostname.
*/ */
public AbstractMessageViewHolder(View itemView, AbsoluteUrl absoluteUrl) { public AbstractMessageViewHolder(View itemView, AbsoluteUrl absoluteUrl, String hostname) {
super(itemView); super(itemView);
avatar = itemView.findViewById(R.id.user_avatar); avatar = itemView.findViewById(R.id.user_avatar);
userNotFoundAvatarImageView = itemView.findViewById(R.id.userNotFoundAvatarImageView);
errorImageView = itemView.findViewById(R.id.errorImageView); errorImageView = itemView.findViewById(R.id.errorImageView);
username = itemView.findViewById(R.id.username); username = itemView.findViewById(R.id.username);
subUsername = itemView.findViewById(R.id.sub_username); subUsername = itemView.findViewById(R.id.sub_username);
...@@ -36,6 +39,7 @@ public abstract class AbstractMessageViewHolder extends ModelViewHolder<PairedMe ...@@ -36,6 +39,7 @@ public abstract class AbstractMessageViewHolder extends ModelViewHolder<PairedMe
newDayContainer = itemView.findViewById(R.id.newday_container); newDayContainer = itemView.findViewById(R.id.newday_container);
newDayText = itemView.findViewById(R.id.newday_text); newDayText = itemView.findViewById(R.id.newday_text);
this.absoluteUrl = absoluteUrl; this.absoluteUrl = absoluteUrl;
this.hostname = hostname;
} }
/** /**
......
...@@ -22,14 +22,16 @@ public class MessageListAdapter extends ExtModelListAdapter<Message, PairedMessa ...@@ -22,14 +22,16 @@ public class MessageListAdapter extends ExtModelListAdapter<Message, PairedMessa
private static final int VIEW_TYPE_NORMAL_MESSAGE = 1; private static final int VIEW_TYPE_NORMAL_MESSAGE = 1;
private static final int VIEW_TYPE_SYSTEM_MESSAGE = 2; private static final int VIEW_TYPE_SYSTEM_MESSAGE = 2;
private String hostname;
private AbsoluteUrl absoluteUrl; private AbsoluteUrl absoluteUrl;
private boolean autoloadImages = false; private boolean autoloadImages = false;
private boolean hasNext; private boolean hasNext;
private boolean isLoaded; private boolean isLoaded;
public MessageListAdapter(Context context) { public MessageListAdapter(Context context, String hostname) {
super(context); super(context);
this.hostname = hostname;
} }
public void setAbsoluteUrl(AbsoluteUrl absoluteUrl) { public void setAbsoluteUrl(AbsoluteUrl absoluteUrl) {
...@@ -92,11 +94,11 @@ public class MessageListAdapter extends ExtModelListAdapter<Message, PairedMessa ...@@ -92,11 +94,11 @@ public class MessageListAdapter extends ExtModelListAdapter<Message, PairedMessa
protected AbstractMessageViewHolder onCreateRealmModelViewHolder(int viewType, View itemView) { protected AbstractMessageViewHolder onCreateRealmModelViewHolder(int viewType, View itemView) {
switch (viewType) { switch (viewType) {
case VIEW_TYPE_NORMAL_MESSAGE: case VIEW_TYPE_NORMAL_MESSAGE:
return new MessageNormalViewHolder(itemView, absoluteUrl); return new MessageNormalViewHolder(itemView, absoluteUrl, hostname);
case VIEW_TYPE_SYSTEM_MESSAGE: case VIEW_TYPE_SYSTEM_MESSAGE:
return new MessageSystemViewHolder(itemView, absoluteUrl); return new MessageSystemViewHolder(itemView, absoluteUrl, hostname);
default: default:
return new AbstractMessageViewHolder(itemView, absoluteUrl) { return new AbstractMessageViewHolder(itemView, absoluteUrl, hostname) {
@Override @Override
protected void bindMessage(PairedMessage pairedMessage, boolean autoloadImages) {} protected void bindMessage(PairedMessage pairedMessage, boolean autoloadImages) {}
}; };
......
...@@ -19,8 +19,8 @@ public class MessageNormalViewHolder extends AbstractMessageViewHolder { ...@@ -19,8 +19,8 @@ public class MessageNormalViewHolder extends AbstractMessageViewHolder {
/** /**
* constructor WITH hostname. * constructor WITH hostname.
*/ */
public MessageNormalViewHolder(View itemView, AbsoluteUrl absoluteUrl) { public MessageNormalViewHolder(View itemView, AbsoluteUrl absoluteUrl, String hostname) {
super(itemView, absoluteUrl); super(itemView, absoluteUrl, hostname);
body = itemView.findViewById(R.id.message_body); body = itemView.findViewById(R.id.message_body);
urls = itemView.findViewById(R.id.message_urls); urls = itemView.findViewById(R.id.message_urls);
attachments = itemView.findViewById(R.id.message_attachments); attachments = itemView.findViewById(R.id.message_attachments);
...@@ -28,12 +28,12 @@ public class MessageNormalViewHolder extends AbstractMessageViewHolder { ...@@ -28,12 +28,12 @@ public class MessageNormalViewHolder extends AbstractMessageViewHolder {
@Override @Override
protected void bindMessage(PairedMessage pairedMessage, boolean autoloadImages) { protected void bindMessage(PairedMessage pairedMessage, boolean autoloadImages) {
new MessageRenderer(itemView.getContext(), pairedMessage.target, autoloadImages) MessageRenderer messageRenderer = new MessageRenderer(pairedMessage.target, autoloadImages);
.avatarInto(avatar, absoluteUrl) messageRenderer.showAvatar(avatar, hostname, userNotFoundAvatarImageView);
.usernameInto(username, subUsername) messageRenderer.showUsername(username, subUsername);
.timestampInto(timestamp) messageRenderer.showTimestampOrMessageState(timestamp);
.bodyInto(body) messageRenderer.showBody(body);
.urlsInto(urls) messageRenderer.showUrl(urls);
.attachmentsInto(attachments, absoluteUrl); messageRenderer.showAttachment(attachments, absoluteUrl);
} }
} }
...@@ -16,18 +16,17 @@ public class MessageSystemViewHolder extends AbstractMessageViewHolder { ...@@ -16,18 +16,17 @@ public class MessageSystemViewHolder extends AbstractMessageViewHolder {
/** /**
* constructor WITH hostname. * constructor WITH hostname.
*/ */
public MessageSystemViewHolder(View itemView, AbsoluteUrl absoluteUrl) { public MessageSystemViewHolder(View itemView, AbsoluteUrl absoluteUrl, String hostname) {
super(itemView, absoluteUrl); super(itemView, absoluteUrl, hostname);
body = itemView.findViewById(R.id.message_body); body = itemView.findViewById(R.id.message_body);
} }
@Override @Override
protected void bindMessage(PairedMessage pairedMessage, boolean autoloadImages) { protected void bindMessage(PairedMessage pairedMessage, boolean autoloadImages) {
new MessageRenderer(itemView.getContext(), pairedMessage.target, autoloadImages) MessageRenderer messageRenderer = new MessageRenderer(pairedMessage.target, autoloadImages);
.avatarInto(avatar, absoluteUrl) messageRenderer.showAvatar(avatar, hostname, userNotFoundAvatarImageView);
.usernameInto(username, subUsername) messageRenderer.showUsername(username, subUsername);
.timestampInto(timestamp); messageRenderer.showTimestampOrMessageState(timestamp);
if (pairedMessage.target != null) { if (pairedMessage.target != null) {
body.setText(MessageType.parse(pairedMessage.target.getType()) body.setText(MessageType.parse(pairedMessage.target.getType())
.getString(body.getContext(), pairedMessage.target)); .getString(body.getContext(), pairedMessage.target));
......
...@@ -24,16 +24,18 @@ public class RoomUserAdapter extends RecyclerView.Adapter<RoomUserViewHolder> { ...@@ -24,16 +24,18 @@ public class RoomUserAdapter extends RecyclerView.Adapter<RoomUserViewHolder> {
private final LayoutInflater inflater; private final LayoutInflater inflater;
private final RealmHelper realmHelper; private final RealmHelper realmHelper;
private final AbsoluteUrl absoluteUrl; private final AbsoluteUrl absoluteUrl;
private final String hostname;
private List<String> usernames; private List<String> usernames;
/** /**
* Constructor with required parameters. * Constructor with required parameters.
*/ */
public RoomUserAdapter(Context context, RealmHelper realmHelper, AbsoluteUrl absoluteUrl) { public RoomUserAdapter(Context context, RealmHelper realmHelper, AbsoluteUrl absoluteUrl, String hostname) {
this.context = context; this.context = context;
this.inflater = LayoutInflater.from(context); this.inflater = LayoutInflater.from(context);
this.realmHelper = realmHelper; this.realmHelper = realmHelper;
this.absoluteUrl = absoluteUrl; this.absoluteUrl = absoluteUrl;
this.hostname = hostname;
} }
@Override @Override
...@@ -57,14 +59,15 @@ public class RoomUserAdapter extends RecyclerView.Adapter<RoomUserViewHolder> { ...@@ -57,14 +59,15 @@ public class RoomUserAdapter extends RecyclerView.Adapter<RoomUserViewHolder> {
.setUsername(username) .setUsername(username)
.setUtcOffset(0) .setUtcOffset(0)
.build(); .build();
new UserRenderer(context, user)
.avatarInto(holder.avatar, absoluteUrl) UserRenderer userRenderer = new UserRenderer(user);
.usernameInto(holder.username); userRenderer.showAvatar(holder.avatar, hostname);
userRenderer.showUsername(holder.username);
} else { } else {
new UserRenderer(context, realmUser.asUser()) UserRenderer userRenderer = new UserRenderer(realmUser.asUser());
.statusColorInto(holder.status) userRenderer.showAvatar(holder.avatar, hostname);
.avatarInto(holder.avatar, absoluteUrl) userRenderer.showUsername(holder.username);
.usernameInto(holder.username); userRenderer.showStatusColor(holder.status);
} }
} }
......
...@@ -16,17 +16,19 @@ import chat.rocket.android.renderer.UserRenderer; ...@@ -16,17 +16,19 @@ import chat.rocket.android.renderer.UserRenderer;
*/ */
public class SuggestUserAdapter extends RealmAutoCompleteAdapter<RealmUser> { public class SuggestUserAdapter extends RealmAutoCompleteAdapter<RealmUser> {
private final AbsoluteUrl absoluteUrl; private final AbsoluteUrl absoluteUrl;
private final String hostname;
public SuggestUserAdapter(Context context, AbsoluteUrl absoluteUrl) { public SuggestUserAdapter(Context context, AbsoluteUrl absoluteUrl, String hostname) {
super(context, R.layout.listitem_room_user, R.id.room_user_name); super(context, R.layout.listitem_room_user, R.id.room_user_name);
this.absoluteUrl = absoluteUrl; this.absoluteUrl = absoluteUrl;
this.hostname = hostname;
} }
@Override @Override
protected void onBindItemView(View itemView, RealmUser user) { protected void onBindItemView(View itemView, RealmUser user) {
new UserRenderer(itemView.getContext(), user.asUser()) UserRenderer userRenderer = new UserRenderer(user.asUser());
.statusColorInto(itemView.findViewById(R.id.room_user_status)) userRenderer.showStatusColor(itemView.findViewById(R.id.room_user_status));
.avatarInto(itemView.findViewById(R.id.room_user_avatar), absoluteUrl); userRenderer.showAvatar(itemView.findViewById(R.id.room_user_avatar), hostname);
} }
@Override @Override
......
package chat.rocket.android.push; package chat.rocket.android.push;
import android.app.Notification; import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager; import android.app.NotificationManager;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.content.ContentResolver; import android.content.ContentResolver;
...@@ -13,13 +14,16 @@ import android.graphics.Bitmap; ...@@ -13,13 +14,16 @@ import android.graphics.Bitmap;
import android.graphics.BitmapFactory; import android.graphics.BitmapFactory;
import android.graphics.Color; import android.graphics.Color;
import android.net.Uri; import android.net.Uri;
import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.RequiresApi;
import android.support.v4.app.NotificationCompat; import android.support.v4.app.NotificationCompat;
import android.support.v4.app.RemoteInput; import android.support.v4.app.RemoteInput;
import android.support.v4.util.SparseArrayCompat; import android.support.v4.util.SparseArrayCompat;
import android.text.Html; import android.text.Html;
import android.text.Spanned; import android.text.Spanned;
import android.util.Log; import android.util.Log;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
...@@ -31,6 +35,7 @@ import java.net.URL; ...@@ -31,6 +35,7 @@ import java.net.URL;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Random; import java.util.Random;
import chat.rocket.android.activity.MainActivity; import chat.rocket.android.activity.MainActivity;
import chat.rocket.android.helper.ServerPolicyHelper; import chat.rocket.android.helper.ServerPolicyHelper;
import chat.rocket.android.service.ConnectivityManager; import chat.rocket.android.service.ConnectivityManager;
...@@ -93,7 +98,7 @@ public class PushNotificationHandler implements PushConstants { ...@@ -93,7 +98,7 @@ public class PushNotificationHandler implements PushConstants {
} }
public void createNotification(Context context, Bundle extras) { public void createNotification(Context context, Bundle extras) {
NotificationManager mNotificationManager = NotificationManager notificationManager =
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
String appName = getAppName(context); String appName = getAppName(context);
String packageName = context.getPackageName(); String packageName = context.getPackageName();
...@@ -117,15 +122,8 @@ public class PushNotificationHandler implements PushConstants { ...@@ -117,15 +122,8 @@ public class PushNotificationHandler implements PushConstants {
PendingIntent contentIntent = PendingIntent PendingIntent contentIntent = PendingIntent
.getActivity(context, requestCode, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT); .getActivity(context, requestCode, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(context)
.setWhen(System.currentTimeMillis())
.setContentTitle(fromHtml(extras.getString(TITLE)))
.setTicker(fromHtml(extras.getString(TITLE)))
.setContentIntent(contentIntent)
.setAutoCancel(true);
SharedPreferences prefs = context SharedPreferences prefs = context
.getSharedPreferences(PushConstants.COM_ADOBE_PHONEGAP_PUSH, Context.MODE_PRIVATE); .getSharedPreferences(PushConstants.COM_ADOBE_PHONEGAP_PUSH, Context.MODE_PRIVATE);
String localIcon = prefs.getString(ICON, null); String localIcon = prefs.getString(ICON, null);
String localIconColor = prefs.getString(ICON_COLOR, null); String localIconColor = prefs.getString(ICON_COLOR, null);
boolean soundOption = prefs.getBoolean(SOUND, true); boolean soundOption = prefs.getBoolean(SOUND, true);
...@@ -135,91 +133,62 @@ public class PushNotificationHandler implements PushConstants { ...@@ -135,91 +133,62 @@ public class PushNotificationHandler implements PushConstants {
Log.d(LOG_TAG, "stored sound=" + soundOption); Log.d(LOG_TAG, "stored sound=" + soundOption);
Log.d(LOG_TAG, "stored vibrate=" + vibrateOption); Log.d(LOG_TAG, "stored vibrate=" + vibrateOption);
/* if (Build.VERSION.SDK_INT >= 26) {
* Notification Vibration String channelId = "rocket-chat-channel";
*/ CharSequence name = "RocketChatMessage";
int importance = NotificationManager.IMPORTANCE_HIGH;
setNotificationVibration(extras, vibrateOption, notificationBuilder); NotificationChannel channel = new NotificationChannel(channelId, name, importance);
channel.enableLights(true);
/* notificationManager.createNotificationChannel(channel);
* Notification Icon Color
* Notification.Builder notificationBuilder = new Notification.Builder(context, channelId)
* Sets the small-icon background color of the notification. .setWhen(System.currentTimeMillis())
* To use, add the `iconColor` key to plugin android options .setContentTitle(fromHtml(extras.getString(TITLE)))
* .setTicker(fromHtml(extras.getString(TITLE)))
*/ .setContentIntent(contentIntent)
setNotificationIconColor(extras.getString("color"), notificationBuilder, localIconColor); .setChannelId(channelId)
.setAutoCancel(true);
/*
* Notification Icon setNotificationImportance(extras, channel);
* setNotificationVibration(extras, vibrateOption, channel);
* Sets the small-icon of the notification. setNotificationMessage(notId, extras, notificationBuilder);
* setNotificationCount(context, extras, notificationBuilder);
* - checks the plugin options for `icon` key setNotificationSmallIcon(extras, packageName, resources, notificationBuilder,
* - if none, uses the application icon localIcon);
* setNotificationLargeIcon(context, extras, packageName, resources, notificationBuilder);
* The icon value must be a string that maps to a drawable resource. setNotificationLedColor(extras, channel);
* If no resource is found, falls if (soundOption) {
* setNotificationSound(context, extras, channel);
*/ }
setNotificationSmallIcon(context, extras, packageName, resources, notificationBuilder, createActions(context, extras, notificationBuilder, resources, packageName, notId);
localIcon); notificationManager.notify(notId, notificationBuilder.build());
} else {
/* NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(context)
* Notification Large-Icon .setWhen(System.currentTimeMillis())
* .setContentTitle(fromHtml(extras.getString(TITLE)))
* Sets the large-icon of the notification .setTicker(fromHtml(extras.getString(TITLE)))
* .setContentIntent(contentIntent)
* - checks the gcm data for the `image` key .setAutoCancel(true);
* - checks to see if remote image, loads it.
* - checks to see if assets image, Loads It. setNotificationCount(context, extras, notificationBuilder);
* - checks to see if resource image, LOADS IT! setNotificationVibration(extras, vibrateOption, notificationBuilder);
* - if none, we don't set the large icon setNotificationIconColor(extras.getString("color"), notificationBuilder, localIconColor);
* setNotificationSmallIcon(extras, packageName, resources, notificationBuilder,
*/ localIcon);
setNotificationLargeIcon(context, extras, packageName, resources, notificationBuilder); setNotificationLargeIcon(context, extras, packageName, resources, notificationBuilder);
if (soundOption) {
/* setNotificationSound(context, extras, notificationBuilder);
* Notification Sound }
*/ setNotificationLedColor(extras, notificationBuilder);
if (soundOption) { setNotificationPriority(extras, notificationBuilder);
setNotificationSound(context, extras, notificationBuilder); setNotificationMessage(notId, extras, notificationBuilder);
} setVisibility(context, extras, notificationBuilder);
createActions(context, extras, notificationBuilder, resources, packageName, notId);
/* notificationManager.notify(appName, notId, notificationBuilder.build());
* LED Notification }
*/ }
setNotificationLedColor(extras, notificationBuilder);
private void createActions(Context context, Bundle extras, NotificationCompat.Builder builder,
/*
* Priority Notification
*/
setNotificationPriority(extras, notificationBuilder);
/*
* Notification message
*/
setNotificationMessage(notId, extras, notificationBuilder);
/*
* Notification count
*/
setNotificationCount(context, extras, notificationBuilder);
/*
* Notification count
*/
setVisibility(context, extras, notificationBuilder);
/*
* Notification add actions
*/
createActions(context, extras, notificationBuilder, resources, packageName, notId);
mNotificationManager.notify(appName, notId, notificationBuilder.build());
}
private void createActions(Context context, Bundle extras, NotificationCompat.Builder mBuilder,
Resources resources, String packageName, int notId) { Resources resources, String packageName, int notId) {
Log.d(LOG_TAG, "create actions: with in-line"); Log.d(LOG_TAG, "create actions: with in-line");
String actions = extras.getString(ACTIONS); String actions = extras.getString(ACTIONS);
...@@ -298,16 +267,112 @@ public class PushNotificationHandler implements PushConstants { ...@@ -298,16 +267,112 @@ public class PushNotificationHandler implements PushConstants {
wActions.add(actionBuilder.build()); wActions.add(actionBuilder.build());
if (inline) { if (inline) {
mBuilder.addAction(wAction); builder.addAction(wAction);
} else { } else {
mBuilder.addAction( builder.addAction(
resources.getIdentifier(action.optString(ICON, ""), DRAWABLE, packageName), resources.getIdentifier(action.optString(ICON, ""), DRAWABLE, packageName),
action.getString(TITLE), pIntent); action.getString(TITLE), pIntent);
} }
wAction = null; wAction = null;
pIntent = null; pIntent = null;
} }
mBuilder.extend(new NotificationCompat.WearableExtender().addActions(wActions)); builder.extend(new NotificationCompat.WearableExtender().addActions(wActions));
wActions.clear();
} catch (JSONException e) {
// nope
}
}
@RequiresApi(api = Build.VERSION_CODES.KITKAT_WATCH)
private void createActions(Context context, Bundle extras, Notification.Builder builder,
Resources resources, String packageName, int notId) {
Log.d(LOG_TAG, "create actions: with in-line");
String actions = extras.getString(ACTIONS);
if (actions == null) {
return;
}
try {
JSONArray actionsArray = new JSONArray(actions);
ArrayList<Notification.Action> wActions = new ArrayList<>();
for (int i = 0; i < actionsArray.length(); i++) {
int min = 1;
int max = 2000000000;
Random random = new Random();
int uniquePendingIntentRequestCode = random.nextInt((max - min) + 1) + min;
Log.d(LOG_TAG, "adding action");
JSONObject action = actionsArray.getJSONObject(i);
Log.d(LOG_TAG, "adding callback = " + action.getString(CALLBACK));
boolean foreground = action.optBoolean(FOREGROUND, true);
boolean inline = action.optBoolean("inline", false);
Intent intent;
PendingIntent pIntent;
if (inline) {
Log.d(LOG_TAG, "Version: " + android.os.Build.VERSION.SDK_INT + " = "
+ android.os.Build.VERSION_CODES.M);
if (android.os.Build.VERSION.SDK_INT <= android.os.Build.VERSION_CODES.M) {
Log.d(LOG_TAG, "push activity");
intent = new Intent(context, MainActivity.class);
} else {
Log.d(LOG_TAG, "push receiver");
intent = new Intent(context, BackgroundActionButtonHandler.class);
}
updateIntent(intent, action.getString(CALLBACK), extras, foreground, notId);
if (android.os.Build.VERSION.SDK_INT <= android.os.Build.VERSION_CODES.M) {
Log.d(LOG_TAG, "push activity for notId " + notId);
pIntent =
PendingIntent.getActivity(context, uniquePendingIntentRequestCode, intent,
PendingIntent.FLAG_ONE_SHOT);
} else {
Log.d(LOG_TAG, "push receiver for notId " + notId);
pIntent = PendingIntent
.getBroadcast(context, uniquePendingIntentRequestCode, intent,
PendingIntent.FLAG_ONE_SHOT);
}
} else if (foreground) {
intent = new Intent(context, MainActivity.class);
updateIntent(intent, action.getString(CALLBACK), extras, foreground, notId);
pIntent = PendingIntent
.getActivity(context, uniquePendingIntentRequestCode, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
} else {
intent = new Intent(context, BackgroundActionButtonHandler.class);
updateIntent(intent, action.getString(CALLBACK), extras, foreground, notId);
pIntent = PendingIntent
.getBroadcast(context, uniquePendingIntentRequestCode, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
}
Notification.Action.Builder actionBuilder = new Notification.Action.Builder(
resources.getIdentifier(action.optString(ICON, ""), DRAWABLE, packageName),
action.getString(TITLE), pIntent);
android.app.RemoteInput remoteInput;
if (inline) {
Log.d(LOG_TAG, "create remote input");
String replyLabel = "Enter your reply here";
remoteInput = new android.app.RemoteInput.Builder(INLINE_REPLY)
.setLabel(replyLabel)
.build();
actionBuilder.addRemoteInput(remoteInput);
}
Notification.Action wAction = actionBuilder.build();
wActions.add(actionBuilder.build());
if (inline) {
builder.addAction(wAction);
} else {
builder.addAction(
resources.getIdentifier(action.optString(ICON, ""), DRAWABLE, packageName),
action.getString(TITLE), pIntent);
}
wAction = null;
pIntent = null;
}
builder.extend(new Notification.WearableExtender().addActions(wActions));
wActions.clear(); wActions.clear();
} catch (JSONException e) { } catch (JSONException e) {
// nope // nope
...@@ -315,15 +380,24 @@ public class PushNotificationHandler implements PushConstants { ...@@ -315,15 +380,24 @@ public class PushNotificationHandler implements PushConstants {
} }
private void setNotificationCount(Context context, Bundle extras, private void setNotificationCount(Context context, Bundle extras,
NotificationCompat.Builder mBuilder) { NotificationCompat.Builder builder) {
int count = extractBadgeCount(extras); int count = extractBadgeCount(extras);
if (count >= 0) { if (count >= 0) {
Log.d(LOG_TAG, "count =[" + count + "]"); Log.d(LOG_TAG, "count =[" + count + "]");
mBuilder.setNumber(count); builder.setNumber(count);
} }
} }
private void setVisibility(Context context, Bundle extras, NotificationCompat.Builder mBuilder) { private void setNotificationCount(Context context, Bundle extras,
Notification.Builder builder) {
int count = extractBadgeCount(extras);
if (count >= 0) {
Log.d(LOG_TAG, "count =[" + count + "]");
builder.setNumber(count);
}
}
private void setVisibility(Context context, Bundle extras, NotificationCompat.Builder builder) {
String visibilityStr = extras.getString(VISIBILITY); String visibilityStr = extras.getString(VISIBILITY);
if (visibilityStr == null) { if (visibilityStr == null) {
return; return;
...@@ -332,8 +406,8 @@ public class PushNotificationHandler implements PushConstants { ...@@ -332,8 +406,8 @@ public class PushNotificationHandler implements PushConstants {
try { try {
Integer visibility = Integer.parseInt(visibilityStr); Integer visibility = Integer.parseInt(visibilityStr);
if (visibility >= NotificationCompat.VISIBILITY_SECRET if (visibility >= NotificationCompat.VISIBILITY_SECRET
&& visibility <= NotificationCompat.VISIBILITY_PUBLIC) { && visibility <= NotificationCompat.VISIBILITY_PUBLIC) {
mBuilder.setVisibility(visibility); builder.setVisibility(visibility);
} else { } else {
Log.e(LOG_TAG, "Visibility parameter must be between -1 and 1"); Log.e(LOG_TAG, "Visibility parameter must be between -1 and 1");
} }
...@@ -343,7 +417,7 @@ public class PushNotificationHandler implements PushConstants { ...@@ -343,7 +417,7 @@ public class PushNotificationHandler implements PushConstants {
} }
private void setNotificationVibration(Bundle extras, Boolean vibrateOption, private void setNotificationVibration(Bundle extras, Boolean vibrateOption,
NotificationCompat.Builder mBuilder) { NotificationCompat.Builder builder) {
String vibrationPattern = extras.getString(VIBRATION_PATTERN); String vibrationPattern = extras.getString(VIBRATION_PATTERN);
if (vibrationPattern != null) { if (vibrationPattern != null) {
String[] items = vibrationPattern.replaceAll("\\[", "").replaceAll("\\]", "").split(","); String[] items = vibrationPattern.replaceAll("\\[", "").replaceAll("\\]", "").split(",");
...@@ -354,23 +428,44 @@ public class PushNotificationHandler implements PushConstants { ...@@ -354,23 +428,44 @@ public class PushNotificationHandler implements PushConstants {
} catch (NumberFormatException nfe) { } catch (NumberFormatException nfe) {
} }
} }
mBuilder.setVibrate(results); builder.setVibrate(results);
} else { } else {
if (vibrateOption) { if (vibrateOption) {
mBuilder.setDefaults(Notification.DEFAULT_VIBRATE); builder.setDefaults(Notification.DEFAULT_VIBRATE);
}
}
}
@RequiresApi(api = 26)
private void setNotificationVibration(Bundle extras, Boolean vibrateOption,
NotificationChannel channel) {
String vibrationPattern = extras.getString(VIBRATION_PATTERN);
if (vibrationPattern != null) {
String[] items = vibrationPattern.replaceAll("\\[", "").replaceAll("\\]", "").split(",");
long[] results = new long[items.length];
for (int i = 0; i < items.length; i++) {
try {
results[i] = Long.parseLong(items[i].trim());
} catch (NumberFormatException nfe) {
}
}
channel.setVibrationPattern(results);
} else {
if (vibrateOption) {
channel.enableVibration(true);
} }
} }
} }
private void setNotificationMessage(int notId, Bundle extras, private void setNotificationMessage(int notId, Bundle extras,
NotificationCompat.Builder mBuilder) { NotificationCompat.Builder builder) {
String message = extras.getString(MESSAGE); String message = extras.getString(MESSAGE);
String style = extras.getString(STYLE, STYLE_TEXT); String style = extras.getString(STYLE, STYLE_TEXT);
if (STYLE_INBOX.equals(style)) { if (STYLE_INBOX.equals(style)) {
setNotification(notId, message); setNotification(notId, message);
mBuilder.setContentText(fromHtml(message)); builder.setContentText(fromHtml(message));
ArrayList<String> messageList = getMessageList(notId); ArrayList<String> messageList = getMessageList(notId);
Integer sizeList = messageList.size(); Integer sizeList = messageList.size();
...@@ -389,13 +484,13 @@ public class PushNotificationHandler implements PushConstants { ...@@ -389,13 +484,13 @@ public class PushNotificationHandler implements PushConstants {
notificationInbox.addLine(fromHtml(messageList.get(i))); notificationInbox.addLine(fromHtml(messageList.get(i)));
} }
mBuilder.setStyle(notificationInbox); builder.setStyle(notificationInbox);
} else { } else {
NotificationCompat.BigTextStyle bigText = new NotificationCompat.BigTextStyle(); NotificationCompat.BigTextStyle bigText = new NotificationCompat.BigTextStyle();
if (message != null) { if (message != null) {
bigText.bigText(fromHtml(message)); bigText.bigText(fromHtml(message));
bigText.setBigContentTitle(fromHtml(extras.getString(TITLE))); bigText.setBigContentTitle(fromHtml(extras.getString(TITLE)));
mBuilder.setStyle(bigText); builder.setStyle(bigText);
} }
} }
} else if (STYLE_PICTURE.equals(style)) { } else if (STYLE_PICTURE.equals(style)) {
...@@ -406,17 +501,17 @@ public class PushNotificationHandler implements PushConstants { ...@@ -406,17 +501,17 @@ public class PushNotificationHandler implements PushConstants {
bigPicture.setBigContentTitle(fromHtml(extras.getString(TITLE))); bigPicture.setBigContentTitle(fromHtml(extras.getString(TITLE)));
bigPicture.setSummaryText(fromHtml(extras.getString(SUMMARY_TEXT))); bigPicture.setSummaryText(fromHtml(extras.getString(SUMMARY_TEXT)));
mBuilder.setContentTitle(fromHtml(extras.getString(TITLE))); builder.setContentTitle(fromHtml(extras.getString(TITLE)));
mBuilder.setContentText(fromHtml(message)); builder.setContentText(fromHtml(message));
mBuilder.setStyle(bigPicture); builder.setStyle(bigPicture);
} else { } else {
setNotification(notId, ""); setNotification(notId, "");
NotificationCompat.BigTextStyle bigText = new NotificationCompat.BigTextStyle(); NotificationCompat.BigTextStyle bigText = new NotificationCompat.BigTextStyle();
if (message != null) { if (message != null) {
mBuilder.setContentText(fromHtml(message)); builder.setContentText(fromHtml(message));
bigText.bigText(fromHtml(message)); bigText.bigText(fromHtml(message));
bigText.setBigContentTitle(fromHtml(extras.getString(TITLE))); bigText.setBigContentTitle(fromHtml(extras.getString(TITLE)));
...@@ -426,30 +521,145 @@ public class PushNotificationHandler implements PushConstants { ...@@ -426,30 +521,145 @@ public class PushNotificationHandler implements PushConstants {
bigText.setSummaryText(fromHtml(summaryText)); bigText.setSummaryText(fromHtml(summaryText));
} }
mBuilder.setStyle(bigText); builder.setStyle(bigText);
}
}
}
private void setNotificationMessage(int notId, Bundle extras,
Notification.Builder builder) {
String message = extras.getString(MESSAGE);
String style = extras.getString(STYLE, STYLE_TEXT);
if (STYLE_INBOX.equals(style)) {
setNotification(notId, message);
builder.setContentText(fromHtml(message));
ArrayList<String> messageList = getMessageList(notId);
Integer sizeList = messageList.size();
if (sizeList > 1) {
String sizeListMessage = sizeList.toString();
String stacking = sizeList + " more";
if (extras.getString(SUMMARY_TEXT) != null) {
stacking = extras.getString(SUMMARY_TEXT);
stacking = stacking.replace("%n%", sizeListMessage);
}
Notification.InboxStyle notificationInbox = new Notification.InboxStyle()
.setBigContentTitle(fromHtml(extras.getString(TITLE)))
.setSummaryText(fromHtml(stacking));
for (int i = messageList.size() - 1; i >= 0; i--) {
notificationInbox.addLine(fromHtml(messageList.get(i)));
}
builder.setStyle(notificationInbox);
} else {
Notification.BigTextStyle bigText = new Notification.BigTextStyle();
if (message != null) {
bigText.bigText(fromHtml(message));
bigText.setBigContentTitle(fromHtml(extras.getString(TITLE)));
builder.setStyle(bigText);
}
}
} else if (STYLE_PICTURE.equals(style)) {
setNotification(notId, "");
Notification.BigPictureStyle bigPicture = new Notification.BigPictureStyle();
bigPicture.bigPicture(getBitmapFromURL(extras.getString(PICTURE)));
bigPicture.setBigContentTitle(fromHtml(extras.getString(TITLE)));
bigPicture.setSummaryText(fromHtml(extras.getString(SUMMARY_TEXT)));
builder.setContentTitle(fromHtml(extras.getString(TITLE)));
builder.setContentText(fromHtml(message));
builder.setStyle(bigPicture);
} else {
setNotification(notId, "");
Notification.BigTextStyle bigText = new Notification.BigTextStyle();
if (message != null) {
builder.setContentText(fromHtml(message));
bigText.bigText(fromHtml(message));
bigText.setBigContentTitle(fromHtml(extras.getString(TITLE)));
String summaryText = extras.getString(SUMMARY_TEXT);
if (summaryText != null) {
bigText.setSummaryText(fromHtml(summaryText));
}
builder.setStyle(bigText);
} }
} }
} }
private void setNotificationSound(Context context, Bundle extras, private void setNotificationSound(Context context, Bundle extras,
NotificationCompat.Builder mBuilder) { NotificationCompat.Builder builder) {
String soundname = extras.getString(SOUNDNAME); String soundname = extras.getString(SOUNDNAME);
if (soundname == null) { if (soundname == null) {
soundname = extras.getString(SOUND); soundname = extras.getString(SOUND);
} }
if (SOUND_RINGTONE.equals(soundname)) { if (SOUND_RINGTONE.equals(soundname)) {
mBuilder.setSound(android.provider.Settings.System.DEFAULT_RINGTONE_URI); builder.setSound(android.provider.Settings.System.DEFAULT_RINGTONE_URI);
} else if (soundname != null && !soundname.contentEquals(SOUND_DEFAULT)) { } else if (soundname != null && !soundname.contentEquals(SOUND_DEFAULT)) {
Uri sound = Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE Uri sound = Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE
+ "://" + context.getPackageName() + "/raw/" + soundname); + "://" + context.getPackageName() + "/raw/" + soundname);
Log.d(LOG_TAG, sound.toString()); Log.d(LOG_TAG, sound.toString());
mBuilder.setSound(sound); builder.setSound(sound);
} else {
builder.setSound(android.provider.Settings.System.DEFAULT_NOTIFICATION_URI);
}
}
@RequiresApi(api = 26)
private void setNotificationSound(Context context, Bundle extras,
NotificationChannel channel) {
String soundname = extras.getString(SOUNDNAME);
if (soundname == null) {
soundname = extras.getString(SOUND);
}
if (SOUND_RINGTONE.equals(soundname)) {
channel.setSound(android.provider.Settings.System.DEFAULT_RINGTONE_URI,
Notification.AUDIO_ATTRIBUTES_DEFAULT);
} else if (soundname != null && !soundname.contentEquals(SOUND_DEFAULT)) {
Uri sound = Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE
+ "://" + context.getPackageName() + "/raw/" + soundname);
Log.d(LOG_TAG, sound.toString());
channel.setSound(sound, Notification.AUDIO_ATTRIBUTES_DEFAULT);
} else {
channel.setSound(android.provider.Settings.System.DEFAULT_NOTIFICATION_URI,
Notification.AUDIO_ATTRIBUTES_DEFAULT);
}
}
private void setNotificationLedColor(Bundle extras, NotificationCompat.Builder builder) {
String ledColor = extras.getString(LED_COLOR);
if (ledColor == null) {
return;
}
// Converts parse Int Array from ledColor
String[] items = ledColor.replaceAll("\\[", "").replaceAll("\\]", "").split(",");
int[] results = new int[items.length];
for (int i = 0; i < items.length; i++) {
try {
results[i] = Integer.parseInt(items[i].trim());
} catch (NumberFormatException nfe) {
}
}
if (results.length == 4) {
builder.setLights(Color.argb(results[0], results[1], results[2], results[3]), 500, 500);
} else { } else {
mBuilder.setSound(android.provider.Settings.System.DEFAULT_NOTIFICATION_URI); Log.e(LOG_TAG, "ledColor parameter must be an array of length == 4 (ARGB)");
} }
} }
private void setNotificationLedColor(Bundle extras, NotificationCompat.Builder mBuilder) { @RequiresApi(api = 26)
private void setNotificationLedColor(Bundle extras, NotificationChannel channel) {
String ledColor = extras.getString(LED_COLOR); String ledColor = extras.getString(LED_COLOR);
if (ledColor == null) { if (ledColor == null) {
return; return;
...@@ -466,14 +676,14 @@ public class PushNotificationHandler implements PushConstants { ...@@ -466,14 +676,14 @@ public class PushNotificationHandler implements PushConstants {
} }
if (results.length == 4) { if (results.length == 4) {
mBuilder.setLights(Color.argb(results[0], results[1], results[2], results[3]), 500, 500); channel.setLightColor(Color.argb(results[0], results[1], results[2], results[3]));
} else { } else {
Log.e(LOG_TAG, "ledColor parameter must be an array of length == 4 (ARGB)"); Log.e(LOG_TAG, "ledColor parameter must be an array of length == 4 (ARGB)");
} }
} }
private void setNotificationPriority(Bundle extras, NotificationCompat.Builder mBuilder) { private void setNotificationPriority(Bundle extras, NotificationCompat.Builder builder) {
String priorityStr = extras.getString(PRIORITY); String priorityStr = extras.getString(PRIORITY);
if (priorityStr == null) { if (priorityStr == null) {
return; return;
...@@ -483,7 +693,28 @@ public class PushNotificationHandler implements PushConstants { ...@@ -483,7 +693,28 @@ public class PushNotificationHandler implements PushConstants {
Integer priority = Integer.parseInt(priorityStr); Integer priority = Integer.parseInt(priorityStr);
if (priority >= NotificationCompat.PRIORITY_MIN if (priority >= NotificationCompat.PRIORITY_MIN
&& priority <= NotificationCompat.PRIORITY_MAX) { && priority <= NotificationCompat.PRIORITY_MAX) {
mBuilder.setPriority(priority); builder.setPriority(priority);
} else {
Log.e(LOG_TAG, "Priority parameter must be between -2 and 2");
}
} catch (NumberFormatException e) {
e.printStackTrace();
}
}
@RequiresApi(api = 26)
private void setNotificationImportance(Bundle extras, NotificationChannel channel) {
String priorityStr = extras.getString(PRIORITY);
if (priorityStr == null) {
return;
}
try {
Integer priority = Integer.parseInt(priorityStr);
if (priority >= NotificationCompat.PRIORITY_MIN
&& priority <= NotificationCompat.PRIORITY_MAX) {
channel.setImportance(priority);
} else { } else {
Log.e(LOG_TAG, "Priority parameter must be between -2 and 2"); Log.e(LOG_TAG, "Priority parameter must be between -2 and 2");
} }
...@@ -494,14 +725,14 @@ public class PushNotificationHandler implements PushConstants { ...@@ -494,14 +725,14 @@ public class PushNotificationHandler implements PushConstants {
} }
private void setNotificationLargeIcon(Context context, Bundle extras, String packageName, private void setNotificationLargeIcon(Context context, Bundle extras, String packageName,
Resources resources, NotificationCompat.Builder mBuilder) { Resources resources, NotificationCompat.Builder builder) {
String gcmLargeIcon = extras.getString(IMAGE); // from gcm String gcmLargeIcon = extras.getString(IMAGE); // from gcm
if (gcmLargeIcon == null || "".equals(gcmLargeIcon)) { if (gcmLargeIcon == null || "".equals(gcmLargeIcon)) {
return; return;
} }
if (gcmLargeIcon.startsWith("http://") || gcmLargeIcon.startsWith("https://")) { if (gcmLargeIcon.startsWith("http://") || gcmLargeIcon.startsWith("https://")) {
mBuilder.setLargeIcon(getBitmapFromURL(gcmLargeIcon)); builder.setLargeIcon(getBitmapFromURL(gcmLargeIcon));
Log.d(LOG_TAG, "using remote large-icon from gcm"); Log.d(LOG_TAG, "using remote large-icon from gcm");
} else { } else {
AssetManager assetManager = context.getAssets(); AssetManager assetManager = context.getAssets();
...@@ -509,13 +740,13 @@ public class PushNotificationHandler implements PushConstants { ...@@ -509,13 +740,13 @@ public class PushNotificationHandler implements PushConstants {
try { try {
istr = assetManager.open(gcmLargeIcon); istr = assetManager.open(gcmLargeIcon);
Bitmap bitmap = BitmapFactory.decodeStream(istr); Bitmap bitmap = BitmapFactory.decodeStream(istr);
mBuilder.setLargeIcon(bitmap); builder.setLargeIcon(bitmap);
Log.d(LOG_TAG, "using assets large-icon from gcm"); Log.d(LOG_TAG, "using assets large-icon from gcm");
} catch (IOException e) { } catch (IOException e) {
int largeIconId = resources.getIdentifier(gcmLargeIcon, DRAWABLE, packageName); int largeIconId = resources.getIdentifier(gcmLargeIcon, DRAWABLE, packageName);
if (largeIconId != 0) { if (largeIconId != 0) {
Bitmap largeIconBitmap = BitmapFactory.decodeResource(resources, largeIconId); Bitmap largeIconBitmap = BitmapFactory.decodeResource(resources, largeIconId);
mBuilder.setLargeIcon(largeIconBitmap); builder.setLargeIcon(largeIconBitmap);
Log.d(LOG_TAG, "using resources large-icon from gcm"); Log.d(LOG_TAG, "using resources large-icon from gcm");
} else { } else {
Log.d(LOG_TAG, "Not setting large icon"); Log.d(LOG_TAG, "Not setting large icon");
...@@ -524,8 +755,59 @@ public class PushNotificationHandler implements PushConstants { ...@@ -524,8 +755,59 @@ public class PushNotificationHandler implements PushConstants {
} }
} }
private void setNotificationSmallIcon(Context context, Bundle extras, String packageName, private void setNotificationLargeIcon(Context context, Bundle extras, String packageName,
Resources resources, NotificationCompat.Builder mBuilder, Resources resources, Notification.Builder builder) {
String gcmLargeIcon = extras.getString(IMAGE); // from gcm
if (gcmLargeIcon == null || "".equals(gcmLargeIcon)) {
return;
}
if (gcmLargeIcon.startsWith("http://") || gcmLargeIcon.startsWith("https://")) {
builder.setLargeIcon(getBitmapFromURL(gcmLargeIcon));
Log.d(LOG_TAG, "using remote large-icon from gcm");
} else {
AssetManager assetManager = context.getAssets();
InputStream istr;
try {
istr = assetManager.open(gcmLargeIcon);
Bitmap bitmap = BitmapFactory.decodeStream(istr);
builder.setLargeIcon(bitmap);
Log.d(LOG_TAG, "using assets large-icon from gcm");
} catch (IOException e) {
int largeIconId = resources.getIdentifier(gcmLargeIcon, DRAWABLE, packageName);
if (largeIconId != 0) {
Bitmap largeIconBitmap = BitmapFactory.decodeResource(resources, largeIconId);
builder.setLargeIcon(largeIconBitmap);
Log.d(LOG_TAG, "using resources large-icon from gcm");
} else {
Log.d(LOG_TAG, "Not setting large icon");
}
}
}
}
private void setNotificationSmallIcon(Bundle extras, String packageName,
Resources resources, NotificationCompat.Builder builder,
String localIcon) {
int iconId = 0;
String icon = extras.getString(ICON);
if (icon != null && !"".equals(icon)) {
iconId = resources.getIdentifier(icon, DRAWABLE, packageName);
Log.d(LOG_TAG, "using icon from plugin options");
} else if (localIcon != null && !"".equals(localIcon)) {
iconId = resources.getIdentifier(localIcon, DRAWABLE, packageName);
Log.d(LOG_TAG, "using icon from plugin options");
}
if (iconId == 0) {
Log.d(LOG_TAG, "no icon resource found - using default icon");
iconId = resources.getIdentifier("rocket_chat_notification", DRAWABLE, packageName);
}
builder.setSmallIcon(iconId);
}
private void setNotificationSmallIcon(Bundle extras, String packageName,
Resources resources, Notification.Builder builder,
String localIcon) { String localIcon) {
int iconId = 0; int iconId = 0;
String icon = extras.getString(ICON); String icon = extras.getString(ICON);
...@@ -540,10 +822,10 @@ public class PushNotificationHandler implements PushConstants { ...@@ -540,10 +822,10 @@ public class PushNotificationHandler implements PushConstants {
Log.d(LOG_TAG, "no icon resource found - using default icon"); Log.d(LOG_TAG, "no icon resource found - using default icon");
iconId = resources.getIdentifier("rocket_chat_notification", DRAWABLE, packageName); iconId = resources.getIdentifier("rocket_chat_notification", DRAWABLE, packageName);
} }
mBuilder.setSmallIcon(iconId); builder.setSmallIcon(iconId);
} }
private void setNotificationIconColor(String color, NotificationCompat.Builder mBuilder, private void setNotificationIconColor(String color, NotificationCompat.Builder builder,
String localIconColor) { String localIconColor) {
int iconColor = 0; int iconColor = 0;
if (color != null && !"".equals(color)) { if (color != null && !"".equals(color)) {
...@@ -560,7 +842,7 @@ public class PushNotificationHandler implements PushConstants { ...@@ -560,7 +842,7 @@ public class PushNotificationHandler implements PushConstants {
} }
} }
if (iconColor != 0) { if (iconColor != 0) {
mBuilder.setColor(iconColor); builder.setColor(iconColor);
} }
} }
......
package chat.rocket.android.renderer;
import android.content.Context;
import android.view.View;
import android.widget.TextView;
import chat.rocket.android.R;
import chat.rocket.android.helper.DateTime;
import chat.rocket.android.helper.TextUtils;
import chat.rocket.android.widget.AbsoluteUrl;
import chat.rocket.android.widget.RocketChatAvatar;
import chat.rocket.android.widget.message.RocketChatMessageAttachmentsLayout;
import chat.rocket.android.widget.message.RocketChatMessageLayout;
import chat.rocket.android.widget.message.RocketChatMessageUrlsLayout;
import chat.rocket.core.SyncState;
import chat.rocket.core.models.Attachment;
import chat.rocket.core.models.Message;
import chat.rocket.core.models.WebContent;
import java.util.List;
/**
* Renderer for RealmMessage model.
*/
public class MessageRenderer extends AbstractRenderer<Message> {
private final UserRenderer userRenderer;
private final boolean autoloadImages;
public MessageRenderer(Context context, Message message, boolean autoloadImages) {
super(context, message);
userRenderer = new UserRenderer(context, message.getUser());
this.autoloadImages = autoloadImages;
}
/**
* show Avatar image.
*/
public MessageRenderer avatarInto(RocketChatAvatar rocketChatAvatar, AbsoluteUrl absoluteUrl) {
if (!shouldHandle(rocketChatAvatar)) {
return this;
}
if (TextUtils.isEmpty(object.getAvatar())) {
userRenderer.avatarInto(rocketChatAvatar, absoluteUrl);
// Avatar from oauth providers
} else {
rocketChatAvatar.loadImage(object.getAvatar());
}
return this;
}
/**
* show Username in textView.
*/
public MessageRenderer usernameInto(TextView usernameTextView, TextView subUsernameTextView) {
if (TextUtils.isEmpty(object.getAlias())) {
userRenderer.usernameInto(usernameTextView);
if (subUsernameTextView != null) {
subUsernameTextView.setVisibility(View.GONE);
}
} else {
aliasAndUsernameInto(usernameTextView, subUsernameTextView);
}
return this;
}
/**
* show timestamp in textView.
*/
public MessageRenderer timestampInto(TextView textView) {
if (!shouldHandle(textView)) {
return this;
}
switch (object.getSyncState()) {
case SyncState.SYNCING:
textView.setText(R.string.sending);
break;
case SyncState.NOT_SYNCED:
textView.setText(R.string.not_synced);
break;
case SyncState.FAILED:
textView.setText(R.string.failed_to_sync);
break;
default:
textView.setText(DateTime.fromEpocMs(object.getTimestamp(), DateTime.Format.TIME));
break;
}
return this;
}
/**
* show body in RocketChatMessageLayout.
*/
public MessageRenderer bodyInto(RocketChatMessageLayout rocketChatMessageLayout) {
if (!shouldHandle(rocketChatMessageLayout)) {
return this;
}
rocketChatMessageLayout.setText(object.getMessage());
return this;
}
/**
* show urls in RocketChatMessageUrlsLayout.
*/
public MessageRenderer urlsInto(RocketChatMessageUrlsLayout urlsLayout) {
if (!shouldHandle(urlsLayout)) {
return this;
}
List<WebContent> webContents = object.getWebContents();
if (webContents == null || webContents.size() == 0) {
urlsLayout.setVisibility(View.GONE);
} else {
urlsLayout.setVisibility(View.VISIBLE);
urlsLayout.setUrls(webContents, autoloadImages);
}
return this;
}
/**
* show urls in RocketChatMessageUrlsLayout.
*/
public MessageRenderer attachmentsInto(RocketChatMessageAttachmentsLayout attachmentsLayout, AbsoluteUrl absoluteUrl) {
if (!shouldHandle(attachmentsLayout)) {
return this;
}
List<Attachment> attachments = object.getAttachments();
if (attachments == null || attachments.size() == 0) {
attachmentsLayout.setVisibility(View.GONE);
} else {
attachmentsLayout.setVisibility(View.VISIBLE);
attachmentsLayout.setAbsoluteUrl(absoluteUrl);
attachmentsLayout.setAttachments(attachments, autoloadImages);
}
return this;
}
private void aliasAndUsernameInto(TextView aliasTextView, TextView usernameTextView) {
if (shouldHandle(aliasTextView)) {
aliasTextView.setText(object.getAlias());
}
if (shouldHandle(usernameTextView)) {
if (object.getUser() != null) {
usernameTextView.setText("@" + object.getUser().getUsername());
usernameTextView.setVisibility(View.VISIBLE);
} else {
usernameTextView.setVisibility(View.GONE);
}
}
}
}
\ No newline at end of file
package chat.rocket.android.renderer
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.RocketChatUserAvatar
import chat.rocket.android.widget.AbsoluteUrl
import chat.rocket.android.widget.RocketChatAvatar
import chat.rocket.android.widget.message.RocketChatMessageAttachmentsLayout
import chat.rocket.android.widget.message.RocketChatMessageLayout
import chat.rocket.android.widget.message.RocketChatMessageUrlsLayout
import chat.rocket.core.SyncState
import chat.rocket.core.models.Message
class MessageRenderer(val message: Message, val autoLoadImage: Boolean) {
/**
* Show user's avatar image in RocketChatAvatar widget.
*/
fun showAvatar(rocketChatAvatarWidget: RocketChatAvatar, hostname: String, userNotFoundAvatarImageView: ImageView) {
if (message.avatar != null) {
// Load user's avatar image from Oauth provider URI.
rocketChatAvatarWidget.loadImage(message.avatar)
} else {
val username: String? = message.user?.username
if (username != null) {
// Load user's avatar image from Rocket.Chat URI.
rocketChatAvatarWidget.loadImage(RocketChatUserAvatar(hostname, username).imageUri)
userNotFoundAvatarImageView.visibility = View.GONE
rocketChatAvatarWidget.visibility = View.VISIBLE
} else {
rocketChatAvatarWidget.visibility = View.GONE
userNotFoundAvatarImageView.visibility = View.VISIBLE
}
}
}
/**
* Show username in textView.
*/
fun showUsername(usernameTextView: TextView, subUsernameTextView: TextView?) {
if (message.alias == null) {
usernameTextView.text = message.user?.username ?: usernameTextView.context.getText(R.string.user_not_found)
} else {
usernameTextView.text = message.alias
val username: String? = message.user?.username
if (username != null && subUsernameTextView != null) {
subUsernameTextView.text = subUsernameTextView.context.getString(R.string.sub_username, username)
subUsernameTextView.visibility = View.VISIBLE
}
}
}
/**
* Show timestamp or message state in textView.
*/
fun showTimestampOrMessageState(textView: TextView) {
when (message.syncState) {
SyncState.SYNCING -> textView.text = textView.context.getText(R.string.sending)
SyncState.NOT_SYNCED -> textView.text = textView.context.getText(R.string.not_synced)
SyncState.FAILED -> textView.text = textView.context.getText(R.string.failed_to_sync)
else -> textView.text = DateTime.fromEpocMs(message.timestamp, DateTime.Format.TIME)
}
}
/**
* Show body in RocketChatMessageLayout widget.
*/
fun showBody(rocketChatMessageLayout: RocketChatMessageLayout) {
rocketChatMessageLayout.setText(message.message)
}
/**
* Show urls in RocketChatMessageUrlsLayout widget.
*/
fun showUrl(rocketChatMessageUrlsLayout: RocketChatMessageUrlsLayout) {
val webContents = message.webContents
if (webContents == null || webContents.isEmpty()) {
rocketChatMessageUrlsLayout.visibility = View.GONE
} else {
rocketChatMessageUrlsLayout.setUrls(webContents, autoLoadImage)
rocketChatMessageUrlsLayout.visibility = View.VISIBLE
}
}
/**
* show attachments in RocketChatMessageAttachmentsLayout widget.
*/
fun showAttachment(rocketChatMessageAttachmentsLayout: RocketChatMessageAttachmentsLayout, absoluteUrl: AbsoluteUrl?) {
val attachments = message.attachments
if (attachments == null || attachments.isEmpty()) {
rocketChatMessageAttachmentsLayout.visibility = View.GONE
} else {
rocketChatMessageAttachmentsLayout.setAbsoluteUrl(absoluteUrl)
rocketChatMessageAttachmentsLayout.setAttachments(attachments, autoLoadImage)
rocketChatMessageAttachmentsLayout.visibility = View.VISIBLE
}
}
}
\ No newline at end of file
package chat.rocket.android.renderer;
import android.support.annotation.DrawableRes;
import chat.rocket.android.R;
import chat.rocket.android.widget.helper.UserStatusProvider;
import chat.rocket.core.models.User;
public class RocketChatUserStatusProvider implements UserStatusProvider {
private static RocketChatUserStatusProvider instance;
private RocketChatUserStatusProvider() {
}
public static RocketChatUserStatusProvider getInstance() {
if (instance == null) {
instance = new RocketChatUserStatusProvider();
}
return instance;
}
@Override
@DrawableRes
public int getStatusResId(String status) {
if (User.STATUS_ONLINE.equals(status)) {
return R.drawable.userstatus_online;
} else if (User.STATUS_AWAY.equals(status)) {
return R.drawable.userstatus_away;
} else if (User.STATUS_BUSY.equals(status)) {
return R.drawable.userstatus_busy;
} else if (User.STATUS_OFFLINE.equals(status)) {
return R.drawable.userstatus_offline;
}
// unknown status is rendered as "offline" status.
return R.drawable.userstatus_offline;
}
}
package chat.rocket.android.renderer
import chat.rocket.android.R
import chat.rocket.android.widget.helper.UserStatusProvider
import chat.rocket.core.models.User
object RocketChatUserStatusProvider: UserStatusProvider {
override fun getStatusResId(status: String?): Int {
var userStatusDrawableId = R.drawable.userstatus_offline
when (status) {
User.STATUS_ONLINE -> {
userStatusDrawableId = R.drawable.userstatus_online
}
User.STATUS_AWAY -> {
userStatusDrawableId = R.drawable.userstatus_away
}
User.STATUS_BUSY -> {
userStatusDrawableId = R.drawable.userstatus_busy
}
}
return userStatusDrawableId
}
}
\ No newline at end of file
package chat.rocket.android.renderer;
import android.content.Context;
import android.widget.ImageView;
import android.widget.TextView;
import chat.rocket.android.helper.Avatar;
import chat.rocket.android.helper.TextUtils;
import chat.rocket.android.widget.AbsoluteUrl;
import chat.rocket.android.widget.RocketChatAvatar;
import chat.rocket.core.models.User;
/**
* Renderer for RealmUser model.
*/
public class UserRenderer extends AbstractRenderer<User> {
public UserRenderer(Context context, User user) {
super(context, user);
}
/**
* show Avatar image
*/
public UserRenderer avatarInto(RocketChatAvatar rocketChatAvatar, AbsoluteUrl absoluteUrl) {
if (!shouldHandle(rocketChatAvatar)) {
return this;
}
if (!TextUtils.isEmpty(object.getUsername())) {
new Avatar(absoluteUrl, object.getUsername())
.into(rocketChatAvatar);
}
return this;
}
/**
* show Username in textView
*/
public UserRenderer usernameInto(TextView textView) {
if (!shouldHandle(textView)) {
return this;
}
textView.setText(object.getUsername());
return this;
}
/**
* show user's status color into imageView.
*/
public UserRenderer statusColorInto(ImageView imageView) {
if (!shouldHandle(imageView)) {
return this;
}
String status = object.getStatus();
imageView.setImageResource(RocketChatUserStatusProvider.getInstance().getStatusResId(status));
return this;
}
}
package chat.rocket.android.renderer
import android.view.View
import android.widget.ImageView
import android.widget.TextView
import chat.rocket.android.R
import chat.rocket.android.helper.RocketChatUserAvatar
import chat.rocket.android.widget.RocketChatAvatar
import chat.rocket.core.models.User
class UserRenderer(val user: User) {
/**
* Show user's avatar image in RocketChatAvatar widget.
*/
fun showAvatar(rocketChatAvatarWidget: RocketChatAvatar, hostname: String) {
val username: String? = user.username
if (username != null) {
rocketChatAvatarWidget.loadImage(RocketChatUserAvatar(hostname, username).imageUri)
} else {
rocketChatAvatarWidget.visibility = View.GONE
}
}
/**
* Show username in textView.
*/
fun showUsername(textView: TextView) {
textView.text = user.username ?: textView.context.getText(R.string.user_not_found)
}
/**
* Show user's status color in imageView.
*/
fun showStatusColor(imageView: ImageView) {
val userStatus: String? = user.status
if (userStatus != null) {
imageView.setImageResource(RocketChatUserStatusProvider.getStatusResId(userStatus))
} else {
imageView.visibility = View.GONE
}
}
}
\ No newline at end of file
...@@ -23,4 +23,6 @@ import chat.rocket.core.models.ServerInfo; ...@@ -23,4 +23,6 @@ import chat.rocket.core.models.ServerInfo;
void notifyConnectionEstablished(String hostname, String session); void notifyConnectionEstablished(String hostname, String session);
void notifyConnectionLost(String hostname, int reason); void notifyConnectionLost(String hostname, int reason);
void notifyConnecting(String hostname);
} }
...@@ -133,6 +133,14 @@ import rx.subjects.PublishSubject; ...@@ -133,6 +133,14 @@ import rx.subjects.PublishSubject;
new ServerConnectivity(hostname, ServerConnectivity.STATE_DISCONNECTED)); new ServerConnectivity(hostname, ServerConnectivity.STATE_DISCONNECTED));
} }
@DebugLog
@Override
public void notifyConnecting(String hostname) {
serverConnectivityList.put(hostname, ServerConnectivity.STATE_CONNECTING);
connectivitySubject.onNext(
new ServerConnectivity(hostname, ServerConnectivity.STATE_CONNECTING));
}
@Override @Override
public Observable<ServerConnectivity> getServerConnectivityAsObservable() { public Observable<ServerConnectivity> getServerConnectivityAsObservable() {
return Observable.concat(Observable.from(getCurrentConnectivityList()), connectivitySubject); return Observable.concat(Observable.from(getCurrentConnectivityList()), connectivitySubject);
......
...@@ -9,6 +9,7 @@ import android.os.IBinder; ...@@ -9,6 +9,7 @@ import android.os.IBinder;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import java.util.HashMap; import java.util.HashMap;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import chat.rocket.android.activity.MainActivity; import chat.rocket.android.activity.MainActivity;
...@@ -24,6 +25,7 @@ public class RocketChatService extends Service implements ConnectivityServiceInt ...@@ -24,6 +25,7 @@ public class RocketChatService extends Service implements ConnectivityServiceInt
private ConnectivityManagerInternal connectivityManager; private ConnectivityManagerInternal connectivityManager;
private HashMap<String, RocketChatWebSocketThread> webSocketThreads; private HashMap<String, RocketChatWebSocketThread> webSocketThreads;
private Semaphore webSocketThreadLock = new Semaphore(1);
public class LocalBinder extends Binder { public class LocalBinder extends Binder {
ConnectivityServiceInterface getServiceInterface() { ConnectivityServiceInterface getServiceInterface() {
...@@ -49,6 +51,7 @@ public class RocketChatService extends Service implements ConnectivityServiceInt ...@@ -49,6 +51,7 @@ public class RocketChatService extends Service implements ConnectivityServiceInt
context.unbindService(serviceConnection); context.unbindService(serviceConnection);
} }
@DebugLog
@Override @Override
public void onCreate() { public void onCreate() {
super.onCreate(); super.onCreate();
...@@ -57,6 +60,7 @@ public class RocketChatService extends Service implements ConnectivityServiceInt ...@@ -57,6 +60,7 @@ public class RocketChatService extends Service implements ConnectivityServiceInt
webSocketThreads = new HashMap<>(); webSocketThreads = new HashMap<>();
} }
@DebugLog
@Override @Override
public int onStartCommand(Intent intent, int flags, int startId) { public int onStartCommand(Intent intent, int flags, int startId) {
connectivityManager.ensureConnections(); connectivityManager.ensureConnections();
...@@ -106,12 +110,17 @@ public class RocketChatService extends Service implements ConnectivityServiceInt ...@@ -106,12 +110,17 @@ public class RocketChatService extends Service implements ConnectivityServiceInt
@DebugLog @DebugLog
private Single<RocketChatWebSocketThread> getOrCreateWebSocketThread(String hostname) { private Single<RocketChatWebSocketThread> getOrCreateWebSocketThread(String hostname) {
return Single.defer(() -> { return Single.defer(() -> {
webSocketThreadLock.acquire();
if (webSocketThreads.containsKey(hostname)) { if (webSocketThreads.containsKey(hostname)) {
RocketChatWebSocketThread thread = webSocketThreads.get(hostname); RocketChatWebSocketThread thread = webSocketThreads.get(hostname);
webSocketThreadLock.release();
return Single.just(thread); return Single.just(thread);
} }
return RocketChatWebSocketThread.getStarted(getApplicationContext(), hostname) return RocketChatWebSocketThread.getStarted(getApplicationContext(), hostname)
.doOnSuccess(thread -> webSocketThreads.put(hostname, thread)); .doOnSuccess(thread -> {
webSocketThreads.put(hostname, thread);
webSocketThreadLock.release();
});
}); });
} }
......
...@@ -3,24 +3,26 @@ package chat.rocket.android.service; ...@@ -3,24 +3,26 @@ package chat.rocket.android.service;
import android.content.Context; import android.content.Context;
import android.os.Handler; import android.os.Handler;
import android.os.HandlerThread; import android.os.HandlerThread;
import org.json.JSONObject; import org.json.JSONObject;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Iterator; import java.util.Iterator;
import java.util.concurrent.TimeUnit;
import bolts.Task; import bolts.Task;
import chat.rocket.android.RocketChatCache;
import chat.rocket.android.api.DDPClientWrapper; import chat.rocket.android.api.DDPClientWrapper;
import chat.rocket.android.api.MethodCallHelper; import chat.rocket.android.api.MethodCallHelper;
import chat.rocket.android.helper.LogIfError; import chat.rocket.android.helper.LogIfError;
import chat.rocket.android.helper.RxHelper;
import chat.rocket.android.helper.TextUtils; import chat.rocket.android.helper.TextUtils;
import chat.rocket.android.log.RCLog; import chat.rocket.android.log.RCLog;
import chat.rocket.core.models.ServerInfo;
import chat.rocket.persistence.realm.models.internal.RealmSession;
import chat.rocket.persistence.realm.RealmHelper;
import chat.rocket.persistence.realm.RealmStore;
import chat.rocket.android.service.ddp.base.ActiveUsersSubscriber; import chat.rocket.android.service.ddp.base.ActiveUsersSubscriber;
import chat.rocket.android.service.ddp.base.LoginServiceConfigurationSubscriber; import chat.rocket.android.service.ddp.base.LoginServiceConfigurationSubscriber;
import chat.rocket.android.service.ddp.base.UserDataSubscriber; import chat.rocket.android.service.ddp.base.UserDataSubscriber;
import chat.rocket.android.service.ddp.stream.StreamRoomMessage;
import chat.rocket.android.service.observer.CurrentUserObserver; import chat.rocket.android.service.observer.CurrentUserObserver;
import chat.rocket.android.service.observer.FileUploadingToUrlObserver; import chat.rocket.android.service.observer.FileUploadingToUrlObserver;
import chat.rocket.android.service.observer.FileUploadingWithUfsObserver; import chat.rocket.android.service.observer.FileUploadingWithUfsObserver;
...@@ -32,8 +34,13 @@ import chat.rocket.android.service.observer.NewMessageObserver; ...@@ -32,8 +34,13 @@ import chat.rocket.android.service.observer.NewMessageObserver;
import chat.rocket.android.service.observer.PushSettingsObserver; import chat.rocket.android.service.observer.PushSettingsObserver;
import chat.rocket.android.service.observer.SessionObserver; import chat.rocket.android.service.observer.SessionObserver;
import chat.rocket.android.service.observer.TokenLoginObserver; import chat.rocket.android.service.observer.TokenLoginObserver;
import chat.rocket.core.models.ServerInfo;
import chat.rocket.persistence.realm.RealmHelper;
import chat.rocket.persistence.realm.RealmStore;
import chat.rocket.persistence.realm.models.internal.RealmSession;
import hugo.weaving.DebugLog; import hugo.weaving.DebugLog;
import rx.Single; import rx.Single;
import rx.subscriptions.CompositeSubscription;
/** /**
* Thread for handling WebSocket connection. * Thread for handling WebSocket connection.
...@@ -62,6 +69,7 @@ public class RocketChatWebSocketThread extends HandlerThread { ...@@ -62,6 +69,7 @@ public class RocketChatWebSocketThread extends HandlerThread {
private final ArrayList<Registrable> listeners = new ArrayList<>(); private final ArrayList<Registrable> listeners = new ArrayList<>();
private DDPClientWrapper ddpClient; private DDPClientWrapper ddpClient;
private boolean listenersRegistered; private boolean listenersRegistered;
private RocketChatCache rocketChatCache;
private final DDPClientRef ddpClientRef = new DDPClientRef() { private final DDPClientRef ddpClientRef = new DDPClientRef() {
@Override @Override
public DDPClientWrapper get() { public DDPClientWrapper get() {
...@@ -69,7 +77,6 @@ public class RocketChatWebSocketThread extends HandlerThread { ...@@ -69,7 +77,6 @@ public class RocketChatWebSocketThread extends HandlerThread {
} }
}; };
private static class KeepAliveTimer { private static class KeepAliveTimer {
private long lastTime; private long lastTime;
private final long thresholdMs; private final long thresholdMs;
...@@ -96,6 +103,7 @@ public class RocketChatWebSocketThread extends HandlerThread { ...@@ -96,6 +103,7 @@ public class RocketChatWebSocketThread extends HandlerThread {
this.hostname = hostname; this.hostname = hostname;
this.realmHelper = RealmStore.getOrCreate(hostname); this.realmHelper = RealmStore.getOrCreate(hostname);
this.connectivityManager = ConnectivityManager.getInstanceForInternal(appContext); this.connectivityManager = ConnectivityManager.getInstanceForInternal(appContext);
this.rocketChatCache = new RocketChatCache(appContext);
} }
/** /**
...@@ -147,7 +155,7 @@ public class RocketChatWebSocketThread extends HandlerThread { ...@@ -147,7 +155,7 @@ public class RocketChatWebSocketThread extends HandlerThread {
return Single.fromEmitter(emitter -> { return Single.fromEmitter(emitter -> {
new Handler(getLooper()).post(() -> { new Handler(getLooper()).post(() -> {
RCLog.d("thread %s: terminated()", Thread.currentThread().getId()); RCLog.d("thread %s: terminated()", Thread.currentThread().getId());
unregisterListeners(); unregisterListenersAndClose();
connectivityManager.notifyConnectionLost(hostname, connectivityManager.notifyConnectionLost(hostname,
ConnectivityManagerInternal.REASON_CLOSED_BY_USER); ConnectivityManagerInternal.REASON_CLOSED_BY_USER);
RocketChatWebSocketThread.super.quit(); RocketChatWebSocketThread.super.quit();
...@@ -196,9 +204,9 @@ public class RocketChatWebSocketThread extends HandlerThread { ...@@ -196,9 +204,9 @@ public class RocketChatWebSocketThread extends HandlerThread {
public void run() { public void run() {
ddpClient.ping().continueWith(task -> { ddpClient.ping().continueWith(task -> {
if (task.isFaulted()) { if (task.isFaulted()) {
RCLog.e(task.getError()); Exception error = task.getError();
RCLog.e(error);
emitter.onSuccess(false); emitter.onSuccess(false);
ddpClient.close();
} else { } else {
keepAliveTimer.update(); keepAliveTimer.update();
emitter.onSuccess(true); emitter.onSuccess(true);
...@@ -232,9 +240,26 @@ public class RocketChatWebSocketThread extends HandlerThread { ...@@ -232,9 +240,26 @@ public class RocketChatWebSocketThread extends HandlerThread {
// handling WebSocket#onClose() callback. // handling WebSocket#onClose() callback.
task.getResult().client.getOnCloseCallback().onSuccess(_task -> { task.getResult().client.getOnCloseCallback().onSuccess(_task -> {
if (listenersRegistered) { ddpClient.close();
terminate(); forceInvalidateTokens();
} connectivityManager.notifyConnecting(hostname);
// Needed to use subscriptions because of legacy code.
// TODO: Should update to RxJava 2
final CompositeSubscription subscriptions = new CompositeSubscription();
subscriptions.add(
connect().retryWhen(RxHelper.exponentialBackoff(3, 500, TimeUnit.MILLISECONDS))
.subscribe(
connected -> {
if (!connected) {
connectivityManager.notifyConnectionLost(
hostname, ConnectivityManagerInternal.REASON_NETWORK_ERROR
);
}
subscriptions.clear();
},
err -> logErrorAndUnsubscribe(subscriptions, err)
)
);
return null; return null;
}); });
...@@ -265,6 +290,11 @@ public class RocketChatWebSocketThread extends HandlerThread { ...@@ -265,6 +290,11 @@ public class RocketChatWebSocketThread extends HandlerThread {
})); }));
} }
private void logErrorAndUnsubscribe(CompositeSubscription subscriptions, Throwable err) {
RCLog.e(err);
subscriptions.clear();
}
@DebugLog @DebugLog
private Single<Boolean> connect() { private Single<Boolean> connect() {
return connectDDPClient() return connectDDPClient()
...@@ -284,7 +314,7 @@ public class RocketChatWebSocketThread extends HandlerThread { ...@@ -284,7 +314,7 @@ public class RocketChatWebSocketThread extends HandlerThread {
return new MethodCallHelper(realmHelper, ddpClientRef).getPermissions(); return new MethodCallHelper(realmHelper, ddpClientRef).getPermissions();
} }
//@DebugLog @DebugLog
private void registerListeners() { private void registerListeners() {
if (!Thread.currentThread().getName().equals("RC_thread_" + hostname)) { if (!Thread.currentThread().getName().equals("RC_thread_" + hostname)) {
// execute in Looper. // execute in Looper.
...@@ -293,7 +323,7 @@ public class RocketChatWebSocketThread extends HandlerThread { ...@@ -293,7 +323,7 @@ public class RocketChatWebSocketThread extends HandlerThread {
} }
if (listenersRegistered) { if (listenersRegistered) {
return; unregisterListeners();
} }
listenersRegistered = true; listenersRegistered = true;
...@@ -308,12 +338,30 @@ public class RocketChatWebSocketThread extends HandlerThread { ...@@ -308,12 +338,30 @@ public class RocketChatWebSocketThread extends HandlerThread {
registrable.register(); registrable.register();
listeners.add(registrable); listeners.add(registrable);
} }
// Register for room stream messages
String roomId = rocketChatCache.getSelectedRoomId();
if (roomId != null && !roomId.isEmpty()) {
StreamRoomMessage streamRoomMessage = new StreamRoomMessage(
appContext, hostname, realmHelper, ddpClientRef, roomId
);
streamRoomMessage.register();
listeners.add(streamRoomMessage);
}
} catch (Exception exception) { } catch (Exception exception) {
RCLog.w(exception, "Failed to register listeners!!"); RCLog.w(exception, "Failed to register listeners!!");
} }
} }
} }
@DebugLog
private void unregisterListenersAndClose() {
unregisterListeners();
if (ddpClient != null) {
ddpClient.close();
ddpClient = null;
}
}
@DebugLog @DebugLog
private void unregisterListeners() { private void unregisterListeners() {
Iterator<Registrable> iterator = listeners.iterator(); Iterator<Registrable> iterator = listeners.iterator();
...@@ -323,9 +371,5 @@ public class RocketChatWebSocketThread extends HandlerThread { ...@@ -323,9 +371,5 @@ public class RocketChatWebSocketThread extends HandlerThread {
iterator.remove(); iterator.remove();
} }
listenersRegistered = false; listenersRegistered = false;
if (ddpClient != null) {
ddpClient.close();
ddpClient = null;
}
} }
} }
...@@ -6,7 +6,7 @@ package chat.rocket.android.service; ...@@ -6,7 +6,7 @@ package chat.rocket.android.service;
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;
/*package*/ 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 final String hostname; public final String hostname;
......
...@@ -185,7 +185,7 @@ public abstract class AbstractDDPDocEventSubscriber implements Registrable { ...@@ -185,7 +185,7 @@ public abstract class AbstractDDPDocEventSubscriber implements Registrable {
if (rxSubscription != null) { if (rxSubscription != null) {
rxSubscription.dispose(); rxSubscription.dispose();
} }
if (!TextUtils.isEmpty(subscriptionId)) { if (!TextUtils.isEmpty(subscriptionId) && ddpClientRef.get() != null) {
ddpClientRef.get().unsubscribe(subscriptionId).continueWith(new LogIfError()); ddpClientRef.get().unsubscribe(subscriptionId).continueWith(new LogIfError());
} }
} }
......
...@@ -2,6 +2,7 @@ package chat.rocket.android.service.internal; ...@@ -2,6 +2,7 @@ package chat.rocket.android.service.internal;
import android.content.Context; import android.content.Context;
import chat.rocket.android.log.RCLog;
import io.reactivex.disposables.CompositeDisposable; import io.reactivex.disposables.CompositeDisposable;
import chat.rocket.android.RocketChatCache; import chat.rocket.android.RocketChatCache;
...@@ -47,7 +48,7 @@ public abstract class AbstractRocketChatCacheObserver implements Registrable { ...@@ -47,7 +48,7 @@ public abstract class AbstractRocketChatCacheObserver implements Registrable {
compositeDisposable.add( compositeDisposable.add(
new RocketChatCache(context) new RocketChatCache(context)
.getSelectedRoomIdPublisher() .getSelectedRoomIdPublisher()
.subscribe(this::updateRoomIdWith) .subscribe(this::updateRoomIdWith, RCLog::e)
); );
} }
......
package chat.rocket.android.service.observer; package chat.rocket.android.service.observer;
import android.content.Context; import android.content.Context;
import io.realm.Realm;
import io.realm.RealmResults;
import org.json.JSONObject; import org.json.JSONObject;
import java.util.List; import java.util.List;
import chat.rocket.android.helper.CheckSum; import chat.rocket.android.helper.CheckSum;
import chat.rocket.android.helper.LogIfError; import chat.rocket.android.helper.LogIfError;
import chat.rocket.android_ddp.rx.RxWebSocketCallback;
import chat.rocket.core.SyncState;
import chat.rocket.persistence.realm.models.internal.MethodCall;
import chat.rocket.persistence.realm.RealmHelper;
import chat.rocket.android.service.DDPClientRef; import chat.rocket.android.service.DDPClientRef;
import chat.rocket.android_ddp.DDPClientCallback; import chat.rocket.android_ddp.DDPClientCallback;
import chat.rocket.core.SyncState;
import chat.rocket.persistence.realm.RealmHelper;
import chat.rocket.persistence.realm.models.internal.MethodCall;
import io.realm.Realm;
import io.realm.RealmResults;
/** /**
* Observing MethodCall record, executing RPC if needed. * Observing MethodCall record, executing RPC if needed.
...@@ -117,9 +118,6 @@ public class MethodCallObserver extends AbstractModelObserver<MethodCall> { ...@@ -117,9 +118,6 @@ public class MethodCallObserver extends AbstractModelObserver<MethodCall> {
} else if (exception instanceof DDPClientCallback.RPC.Timeout) { } else if (exception instanceof DDPClientCallback.RPC.Timeout) {
// temp "fix"- we need to rewrite the connection layer a bit // temp "fix"- we need to rewrite the connection layer a bit
errMessage = "{\"message\": \"Connection Timeout\"}"; errMessage = "{\"message\": \"Connection Timeout\"}";
} else if (exception instanceof RxWebSocketCallback.Failure) {
// temp "fix"- we need to rewrite the connection layer a bit
errMessage = "{\"message\": \"Connection Failure\"}";
} else { } else {
errMessage = exception.getMessage(); errMessage = exception.getMessage();
} }
......
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:pathData="M-0,0L24,0L24,24L-0,24L-0,0ZM11.963,17.661L12.036,17.661L17.162,17.661C17.162,13.975 13.979,13.915 13.178,12.982L13.082,12.474C14.095,11.946 14.807,10.699 14.807,9.245C14.807,7.313 13.55,5.746 12,5.746C10.45,5.746 9.193,7.313 9.193,9.245C9.193,10.712 9.917,11.966 10.944,12.486L10.86,12.933C10.129,13.913 6.837,13.912 6.837,17.661L11.963,17.661Z"
android:fillColor="#FF000000"/>
</vector>
\ No newline at end of file
...@@ -20,6 +20,14 @@ ...@@ -20,6 +20,14 @@
android:layout_height="32dp" android:layout_height="32dp"
android:layout_margin="8dp" /> android:layout_margin="8dp" />
<ImageView
android:id="@+id/userNotFoundAvatarImageView"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_margin="8dp"
app:srcCompat="@drawable/ic_user_not_found_avatar_black_24dp"
android:visibility="gone" />
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
...@@ -51,7 +59,8 @@ ...@@ -51,7 +59,8 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.RocketChat.Message.SubUsername" android:textAppearance="@style/TextAppearance.RocketChat.Message.SubUsername"
tools:text="\@John Doe" /> tools:text="\@John Doe"
android:visibility="gone" />
<Space <Space
android:layout_width="@dimen/margin_8" android:layout_width="@dimen/margin_8"
......
...@@ -19,6 +19,14 @@ ...@@ -19,6 +19,14 @@
android:layout_height="32dp" android:layout_height="32dp"
android:layout_margin="8dp" /> android:layout_margin="8dp" />
<ImageView
android:id="@+id/userNotFoundAvatarImageView"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_margin="8dp"
app:srcCompat="@drawable/ic_user_not_found_avatar_black_24dp"
android:visibility="gone" />
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
......
...@@ -14,20 +14,28 @@ ...@@ -14,20 +14,28 @@
<string name="start_of_conversation">Start of conversation</string> <string name="start_of_conversation">Start of conversation</string>
<string name="users_of_room_title">Members List</string> <string name="users_of_room_title">Members List</string>
<string name="fmt_room_user_count">Total: %,d users</string> <plurals name="fmt_room_user_count">
<item quantity="one">Total: %,d user</item>
<item quantity="other">Total: %,d users</item>
</plurals>
<string name="sending">Sending…</string> <string name="sending">Sending…</string>
<string name="not_synced">Not synced</string> <string name="not_synced">Not synced</string>
<string name="failed_to_sync">Failed to sync</string> <string name="failed_to_sync">Failed to sync</string>
<string name="resend">Resend</string> <string name="resend">Resend</string>
<string name="discard">Discard</string> <string name="discard">Discard</string>
<string name="fmt_dialog_view_latest_message_title">New %d messages</string> <plurals name="fmt_dialog_view_latest_message_title">
<item quantity="one">New %d message</item>
<item quantity="other">New %d messages</item>
</plurals>
<string name="dialog_view_latest_message_action">View</string> <string name="dialog_view_latest_message_action">View</string>
<string name="file_uploading_title">Uploading…</string> <string name="file_uploading_title">Uploading…</string>
<string name="dialog_user_registration_email">Email</string> <string name="dialog_user_registration_email">Email</string>
<string name="dialog_user_registration_username">Username</string> <string name="dialog_user_registration_username">Username</string>
<string name="sub_username">\@%s</string>
<string name="user_not_found">User not found</string>
<string name="dialog_user_registration_password">Password</string> <string name="dialog_user_registration_password">Password</string>
<string name="fragment_home_welcome_message">Welcome to Rocket.Chat.Android\nSelect a channel from the drawer.</string> <string name="fragment_home_welcome_message">Welcome to Rocket.Chat.Android\nSelect a channel from the drawer.</string>
<string name="fragment_input_hostname_hostname">Hostname</string> <string name="fragment_input_hostname_hostname">Hostname</string>
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
machine: machine:
environment: environment:
ANDROID_HOME: /usr/local/android-sdk-linux ANDROID_HOME: /usr/local/android-sdk-linux
GRADLE_OPTS: '-Dorg.gradle.jvmargs="-Xmx2048m -XX:+HeapDumpOnOutOfMemoryError"' GRADLE_OPTS: '-Dorg.gradle.jvmargs="-Xmx1536m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError"'
dependencies: dependencies:
pre: pre:
......
...@@ -2,7 +2,7 @@ package chat.rocket.persistence.realm.repositories; ...@@ -2,7 +2,7 @@ package chat.rocket.persistence.realm.repositories;
import android.os.Looper; import android.os.Looper;
import android.support.v4.util.Pair; import android.support.v4.util.Pair;
import com.fernandocejas.arrow.optional.Optional; import com.hadisatrio.optional.Optional;
import io.reactivex.Flowable; import io.reactivex.Flowable;
import io.reactivex.Single; import io.reactivex.Single;
import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.android.schedulers.AndroidSchedulers;
......
...@@ -3,7 +3,7 @@ package chat.rocket.persistence.realm.repositories; ...@@ -3,7 +3,7 @@ package chat.rocket.persistence.realm.repositories;
import android.os.Looper; import android.os.Looper;
import android.support.v4.util.Pair; import android.support.v4.util.Pair;
import com.fernandocejas.arrow.optional.Optional; import com.hadisatrio.optional.Optional;
import io.reactivex.Flowable; import io.reactivex.Flowable;
import io.reactivex.Single; import io.reactivex.Single;
......
...@@ -2,7 +2,7 @@ package chat.rocket.persistence.realm.repositories; ...@@ -2,7 +2,7 @@ package chat.rocket.persistence.realm.repositories;
import android.os.Looper; import android.os.Looper;
import android.support.v4.util.Pair; import android.support.v4.util.Pair;
import com.fernandocejas.arrow.optional.Optional; import com.hadisatrio.optional.Optional;
import io.reactivex.Flowable; import io.reactivex.Flowable;
import io.reactivex.Single; import io.reactivex.Single;
import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.android.schedulers.AndroidSchedulers;
......
...@@ -2,7 +2,7 @@ package chat.rocket.persistence.realm.repositories; ...@@ -2,7 +2,7 @@ package chat.rocket.persistence.realm.repositories;
import android.os.Looper; import android.os.Looper;
import android.support.v4.util.Pair; import android.support.v4.util.Pair;
import com.fernandocejas.arrow.optional.Optional; import com.hadisatrio.optional.Optional;
import io.reactivex.Flowable; import io.reactivex.Flowable;
import io.reactivex.Single; import io.reactivex.Single;
import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.android.schedulers.AndroidSchedulers;
......
...@@ -2,7 +2,7 @@ package chat.rocket.persistence.realm.repositories; ...@@ -2,7 +2,7 @@ package chat.rocket.persistence.realm.repositories;
import android.os.Looper; import android.os.Looper;
import android.support.v4.util.Pair; import android.support.v4.util.Pair;
import com.fernandocejas.arrow.optional.Optional; import com.hadisatrio.optional.Optional;
import io.reactivex.Flowable; import io.reactivex.Flowable;
import io.reactivex.Single; import io.reactivex.Single;
import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.android.schedulers.AndroidSchedulers;
......
...@@ -2,7 +2,7 @@ package chat.rocket.persistence.realm.repositories; ...@@ -2,7 +2,7 @@ package chat.rocket.persistence.realm.repositories;
import android.os.Looper; import android.os.Looper;
import android.support.v4.util.Pair; import android.support.v4.util.Pair;
import com.fernandocejas.arrow.optional.Optional; import com.hadisatrio.optional.Optional;
import io.reactivex.Flowable; import io.reactivex.Flowable;
import io.reactivex.Single; import io.reactivex.Single;
import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.android.schedulers.AndroidSchedulers;
......
...@@ -2,7 +2,7 @@ package chat.rocket.persistence.realm.repositories; ...@@ -2,7 +2,7 @@ package chat.rocket.persistence.realm.repositories;
import android.os.Looper; import android.os.Looper;
import android.support.v4.util.Pair; import android.support.v4.util.Pair;
import com.fernandocejas.arrow.optional.Optional; import com.hadisatrio.optional.Optional;
import io.reactivex.Flowable; import io.reactivex.Flowable;
import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.android.schedulers.AndroidSchedulers;
......
...@@ -2,7 +2,7 @@ package chat.rocket.persistence.realm.repositories; ...@@ -2,7 +2,7 @@ package chat.rocket.persistence.realm.repositories;
import android.os.Looper; import android.os.Looper;
import android.support.v4.util.Pair; import android.support.v4.util.Pair;
import com.fernandocejas.arrow.optional.Optional; import com.hadisatrio.optional.Optional;
import io.reactivex.Flowable; import io.reactivex.Flowable;
import io.reactivex.Single; import io.reactivex.Single;
import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.android.schedulers.AndroidSchedulers;
......
...@@ -2,7 +2,7 @@ package chat.rocket.persistence.realm.repositories; ...@@ -2,7 +2,7 @@ package chat.rocket.persistence.realm.repositories;
import android.os.Looper; import android.os.Looper;
import android.support.v4.util.Pair; import android.support.v4.util.Pair;
import com.fernandocejas.arrow.optional.Optional; import com.hadisatrio.optional.Optional;
import io.reactivex.Flowable; import io.reactivex.Flowable;
import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.android.schedulers.AndroidSchedulers;
import io.realm.Case; import io.realm.Case;
......
package chat.rocket.android.widget; package chat.rocket.android.widget;
import android.content.Context; import android.content.Context;
import chat.rocket.android.widget.fresco.CustomImageFormatConfigurator;
import com.facebook.common.logging.FLog; import com.facebook.common.logging.FLog;
import com.facebook.drawee.backends.pipeline.DraweeConfig; import com.facebook.drawee.backends.pipeline.DraweeConfig;
import com.facebook.drawee.backends.pipeline.Fresco; import com.facebook.drawee.backends.pipeline.Fresco;
...@@ -9,8 +9,11 @@ import com.facebook.imagepipeline.backends.okhttp3.OkHttpImagePipelineConfigFact ...@@ -9,8 +9,11 @@ import com.facebook.imagepipeline.backends.okhttp3.OkHttpImagePipelineConfigFact
import com.facebook.imagepipeline.core.ImagePipelineConfig; import com.facebook.imagepipeline.core.ImagePipelineConfig;
import com.facebook.imagepipeline.listener.RequestListener; import com.facebook.imagepipeline.listener.RequestListener;
import com.facebook.imagepipeline.listener.RequestLoggingListener; import com.facebook.imagepipeline.listener.RequestLoggingListener;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import chat.rocket.android.widget.fresco.CustomImageFormatConfigurator;
import okhttp3.OkHttpClient; import okhttp3.OkHttpClient;
public class RocketChatWidgets { public class RocketChatWidgets {
......
plugins { plugins {
id "org.jetbrains.kotlin.jvm" version "1.1.2-2" id "org.jetbrains.kotlin.jvm" version "1.1.3-2"
} }
apply plugin: 'idea' apply plugin: 'idea'
...@@ -14,9 +14,9 @@ dependencies { ...@@ -14,9 +14,9 @@ dependencies {
compile 'io.reactivex.rxjava2:rxjava:2.1.0' compile 'io.reactivex.rxjava2:rxjava:2.1.0'
compile 'com.fernandocejas:arrow:1.0.0' compile 'com.hadisatrio:Optional:v1.0.1'
compile 'com.google.auto.value:auto-value:1.3' compileOnly 'com.google.auto.value:auto-value:1.3'
kapt 'com.google.auto.value:auto-value:1.3' kapt 'com.google.auto.value:auto-value:1.3'
kapt 'com.gabrielittner.auto.value:auto-value-with:1.0.0' kapt 'com.gabrielittner.auto.value:auto-value-with:1.0.0'
......
...@@ -6,7 +6,7 @@ import io.reactivex.Flowable ...@@ -6,7 +6,7 @@ import io.reactivex.Flowable
import io.reactivex.Single import io.reactivex.Single
import chat.rocket.core.repositories.UserRepository import chat.rocket.core.repositories.UserRepository
import com.fernandocejas.arrow.optional.Optional import com.hadisatrio.optional.Optional
import io.reactivex.functions.Function3 import io.reactivex.functions.Function3
class CanCreateRoomInteractor(private val userRepository: UserRepository, class CanCreateRoomInteractor(private val userRepository: UserRepository,
......
...@@ -5,7 +5,7 @@ import chat.rocket.core.PublicSettingsConstants ...@@ -5,7 +5,7 @@ import chat.rocket.core.PublicSettingsConstants
import chat.rocket.core.models.* import chat.rocket.core.models.*
import chat.rocket.core.repositories.* import chat.rocket.core.repositories.*
import chat.rocket.core.utils.Pair import chat.rocket.core.utils.Pair
import com.fernandocejas.arrow.optional.Optional import com.hadisatrio.optional.Optional
import io.reactivex.Single import io.reactivex.Single
import io.reactivex.functions.Function4 import io.reactivex.functions.Function4
......
package chat.rocket.core.interactors package chat.rocket.core.interactors
import com.fernandocejas.arrow.optional.Optional import com.hadisatrio.optional.Optional
import io.reactivex.Flowable import io.reactivex.Flowable
import io.reactivex.Single import io.reactivex.Single
import java.util.UUID import java.util.UUID
......
...@@ -5,7 +5,7 @@ import chat.rocket.core.models.Room ...@@ -5,7 +5,7 @@ import chat.rocket.core.models.Room
import chat.rocket.core.models.RoomRole import chat.rocket.core.models.RoomRole
import chat.rocket.core.repositories.* import chat.rocket.core.repositories.*
import chat.rocket.core.utils.Pair import chat.rocket.core.utils.Pair
import com.fernandocejas.arrow.optional.Optional import com.hadisatrio.optional.Optional
import io.reactivex.Single import io.reactivex.Single
import io.reactivex.functions.BiFunction import io.reactivex.functions.BiFunction
......
package chat.rocket.core.interactors package chat.rocket.core.interactors
import com.fernandocejas.arrow.optional.Optional import com.hadisatrio.optional.Optional
import io.reactivex.Flowable import io.reactivex.Flowable
import io.reactivex.Single import io.reactivex.Single
......
package chat.rocket.core.repositories; package chat.rocket.core.repositories;
import com.fernandocejas.arrow.optional.Optional; import com.hadisatrio.optional.Optional;
import io.reactivex.Flowable; import io.reactivex.Flowable;
import io.reactivex.Single; import io.reactivex.Single;
......
package chat.rocket.core.repositories; package chat.rocket.core.repositories;
import com.fernandocejas.arrow.optional.Optional; import com.hadisatrio.optional.Optional;
import io.reactivex.Flowable; import io.reactivex.Flowable;
import io.reactivex.Single; import io.reactivex.Single;
......
package chat.rocket.core.repositories; package chat.rocket.core.repositories;
import com.fernandocejas.arrow.optional.Optional; import com.hadisatrio.optional.Optional;
import io.reactivex.Single; import io.reactivex.Single;
import chat.rocket.core.models.Permission; import chat.rocket.core.models.Permission;
......
package chat.rocket.core.repositories; package chat.rocket.core.repositories;
import com.fernandocejas.arrow.optional.Optional; import com.hadisatrio.optional.Optional;
import io.reactivex.Single; import io.reactivex.Single;
import chat.rocket.core.models.PublicSetting; import chat.rocket.core.models.PublicSetting;
......
package chat.rocket.core.repositories; package chat.rocket.core.repositories;
import com.fernandocejas.arrow.optional.Optional; import com.hadisatrio.optional.Optional;
import io.reactivex.Flowable; import io.reactivex.Flowable;
import io.reactivex.Single; import io.reactivex.Single;
......
package chat.rocket.core.repositories; package chat.rocket.core.repositories;
import com.fernandocejas.arrow.optional.Optional; import com.hadisatrio.optional.Optional;
import io.reactivex.Single; import io.reactivex.Single;
import chat.rocket.core.models.Room; import chat.rocket.core.models.Room;
......
package chat.rocket.core.repositories; package chat.rocket.core.repositories;
import com.fernandocejas.arrow.optional.Optional; import com.hadisatrio.optional.Optional;
import io.reactivex.Flowable; import io.reactivex.Flowable;
......
package chat.rocket.core.repositories; package chat.rocket.core.repositories;
import com.fernandocejas.arrow.optional.Optional; import com.hadisatrio.optional.Optional;
import io.reactivex.Flowable; import io.reactivex.Flowable;
import io.reactivex.Single; import io.reactivex.Single;
......
package chat.rocket.core.repositories; package chat.rocket.core.repositories;
import com.fernandocejas.arrow.optional.Optional; import com.hadisatrio.optional.Optional;
import io.reactivex.Flowable; import io.reactivex.Flowable;
import java.util.List; import java.util.List;
......
...@@ -7,7 +7,7 @@ import chat.rocket.core.models.PublicSetting ...@@ -7,7 +7,7 @@ import chat.rocket.core.models.PublicSetting
import chat.rocket.core.models.Room import chat.rocket.core.models.Room
import chat.rocket.core.models.User import chat.rocket.core.models.User
import chat.rocket.core.repositories.* import chat.rocket.core.repositories.*
import com.fernandocejas.arrow.optional.Optional import com.hadisatrio.optional.Optional
import io.reactivex.Flowable import io.reactivex.Flowable
import io.reactivex.Single import io.reactivex.Single
import io.reactivex.observers.TestObserver import io.reactivex.observers.TestObserver
......
...@@ -4,7 +4,7 @@ import chat.rocket.core.models.* ...@@ -4,7 +4,7 @@ import chat.rocket.core.models.*
import chat.rocket.core.repositories.PermissionRepository import chat.rocket.core.repositories.PermissionRepository
import chat.rocket.core.repositories.RoomRoleRepository import chat.rocket.core.repositories.RoomRoleRepository
import chat.rocket.core.repositories.UserRepository import chat.rocket.core.repositories.UserRepository
import com.fernandocejas.arrow.optional.Optional import com.hadisatrio.optional.Optional
import io.reactivex.Flowable import io.reactivex.Flowable
import io.reactivex.Single import io.reactivex.Single
import io.reactivex.observers.TestObserver import io.reactivex.observers.TestObserver
......
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