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

Merge branch 'develop' into ux

parents bff56d0d 8d2dfc0f
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.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