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;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import chat.rocket.android.log.RCLog;
import io.reactivex.BackpressureStrategy;
import io.reactivex.Flowable;
import io.reactivex.FlowableOnSubscribe;
import io.reactivex.exceptions.OnErrorNotImplementedException;
import io.reactivex.flowables.ConnectableFlowable;
import java.io.IOException;
import chat.rocket.android.log.RCLog;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
......@@ -15,8 +16,10 @@ import okhttp3.WebSocket;
import okhttp3.WebSocketListener;
public class RxWebSocket {
public static final int REASON_NETWORK_ERROR = 100;
private OkHttpClient httpClient;
private WebSocket webSocket;
private boolean hadErrorsBefore;
public RxWebSocket(OkHttpClient client) {
httpClient = client;
......@@ -30,6 +33,7 @@ public class RxWebSocket {
.newWebSocket(request, new WebSocketListener() {
@Override
public void onOpen(WebSocket webSocket, Response response) {
hadErrorsBefore = false;
RxWebSocket.this.webSocket = webSocket;
emitter.onNext(new RxWebSocketCallback.Open(RxWebSocket.this.webSocket, response));
}
......@@ -37,7 +41,11 @@ public class RxWebSocket {
@Override
public void onFailure(WebSocket webSocket, Throwable err, Response response) {
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) {
RCLog.w(ex, "OnErrorNotImplementedException ignored");
}
......@@ -51,11 +59,10 @@ public class RxWebSocket {
@Override
public void onClosed(WebSocket webSocket, int code, String reason) {
emitter.onNext(new RxWebSocketCallback.Close(webSocket, code, reason));
emitter.onComplete();
}
}),
BackpressureStrategy.BUFFER
).publish();
).delay(4, TimeUnit.SECONDS).publish();
}
public boolean sendText(String message) throws IOException {
......
package chat.rocket.android_ddp.rx;
import static android.R.attr.type;
import chat.rocket.android.log.RCLog;
import okhttp3.Response;
import okhttp3.WebSocket;
......@@ -28,25 +26,8 @@ public class RxWebSocketCallback {
public Open(WebSocket websocket, Response response) {
super("Open", websocket);
this.response = response;
}
}
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();
if (response != null && response.body() != null) {
this.response.body().close();
}
}
}
......
......@@ -35,7 +35,7 @@ android {
applicationId "chat.rocket.android"
minSdkVersion 16
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 26
versionCode 27
versionName "1.0.16"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
......
......@@ -16,11 +16,11 @@ import chat.rocket.android.fragment.chatroom.HomeFragment;
import chat.rocket.android.fragment.chatroom.RoomFragment;
import chat.rocket.android.fragment.sidebar.SidebarMainFragment;
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.RoomInteractor;
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.RealmSessionRepository;
import chat.rocket.persistence.realm.repositories.RealmUserRepository;
......@@ -34,6 +34,7 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract
private StatusTicker statusTicker;
private MainContract.Presenter presenter;
private RoomFragment roomFragment;
@Override
protected int getLayoutContainerForFragment() {
......@@ -179,7 +180,8 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract
@Override
public void showRoom(String hostname, String roomId) {
showFragment(RoomFragment.create(hostname, roomId));
roomFragment = RoomFragment.create(hostname, roomId);
showFragment(roomFragment);
closeSidebarIfNeeded();
KeyboardHelper.hideSoftKeyboard(this);
}
......@@ -222,6 +224,9 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract
@Override
public void showConnectionOk() {
statusTicker.updateStatus(StatusTicker.STATUS_DISMISS, null);
if (roomFragment != null) {
roomFragment.refreshRoom();
}
}
//TODO: consider this class to define in layouthelper for more complicated operation.
......
......@@ -3,22 +3,23 @@ package chat.rocket.android.activity;
import android.support.annotation.NonNull;
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.RocketChatCache;
import chat.rocket.android.api.MethodCallHelper;
import chat.rocket.android.helper.LogIfError;
import chat.rocket.android.helper.Logger;
import chat.rocket.android.service.ConnectivityManagerApi;
import chat.rocket.android.service.ServerConnectivity;
import chat.rocket.android.shared.BasePresenter;
import chat.rocket.core.interactors.CanCreateRoomInteractor;
import chat.rocket.core.interactors.RoomInteractor;
import chat.rocket.core.interactors.SessionInteractor;
import chat.rocket.core.models.Session;
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>
implements MainContract.Presenter {
......@@ -63,6 +64,7 @@ public class MainPresenter extends BasePresenter<MainContract.View>
openRoom();
subscribeToNetworkChanges();
subscribeToUnreadCount();
subscribeToSession();
setUserOnline();
......@@ -96,10 +98,8 @@ public class MainPresenter extends BasePresenter<MainContract.View>
@Override
public void onRetryLogin() {
final Disposable subscription = sessionInteractor.retryLogin()
.subscribe();
addSubscription(subscription);
view.showConnecting();
connectivityManagerApi.keepAliveServer();
}
private void openRoom() {
......@@ -161,6 +161,23 @@ public class MainPresenter extends BasePresenter<MainContract.View>
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() {
methodCallHelper.setUserPresence(User.STATUS_ONLINE)
.continueWith(new LogIfError());
......
package chat.rocket.android.api;
import android.support.annotation.Nullable;
import io.reactivex.Flowable;
import org.json.JSONArray;
import org.json.JSONException;
import java.util.UUID;
import bolts.Task;
import chat.rocket.android.helper.OkHttpHelper;
import chat.rocket.android.helper.TextUtils;
......@@ -13,6 +14,7 @@ import chat.rocket.android.log.RCLog;
import chat.rocket.android_ddp.DDPClient;
import chat.rocket.android_ddp.DDPClientCallback;
import chat.rocket.android_ddp.DDPSubscription;
import io.reactivex.Flowable;
/**
* DDP client wrapper.
......
......@@ -49,5 +49,7 @@ public interface RoomContract {
void onUnreadCount();
void onMarkAsRead();
void refreshRoom();
}
}
......@@ -21,6 +21,11 @@ import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;
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.R;
import chat.rocket.android.api.MethodCallHelper;
......@@ -70,15 +75,13 @@ import chat.rocket.persistence.realm.repositories.RealmSessionRepository;
import chat.rocket.persistence.realm.repositories.RealmSpotlightRoomRepository;
import chat.rocket.persistence.realm.repositories.RealmSpotlightUserRepository;
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 io.reactivex.Single;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
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.RuntimePermissions;
......@@ -188,7 +191,7 @@ public class RoomFragment extends AbstractChatRoomFragment implements
protected void onSetupView() {
messageRecyclerView = rootView.findViewById(R.id.messageRecyclerView);
messageListAdapter = new MessageListAdapter(getContext());
messageListAdapter = new MessageListAdapter(getContext(), hostname);
messageRecyclerView.setAdapter(messageListAdapter);
messageListAdapter.setOnItemClickListener(this);
messageListAdapter.setOnItemLongClickListener(this);
......@@ -251,8 +254,8 @@ public class RoomFragment extends AbstractChatRoomFragment implements
protected Snackbar getUnreadCountIndicatorView(int count) {
// TODO: replace with another custom View widget, not to hide message composer.
final String caption = getResources().getString(
R.string.fmt_dialog_view_latest_message_title, count);
final String caption = getResources().getQuantityString(
R.plurals.fmt_dialog_view_latest_message_title, count, count);
return Snackbar.make(rootView, caption, Snackbar.LENGTH_LONG)
.setAction(R.string.dialog_view_latest_message_action, view -> scrollToLatestMessage());
......@@ -369,7 +372,7 @@ public class RoomFragment extends AbstractChatRoomFragment implements
new DefaultTempSpotlightUserCaller(methodCallHelper)
),
pair.first.get(),
RocketChatUserStatusProvider.getInstance(),
RocketChatUserStatusProvider.INSTANCE,
AndroidSchedulers.from(BackgroundLooper.get()),
AndroidSchedulers.mainThread()
)
......@@ -599,4 +602,8 @@ public class RoomFragment extends AbstractChatRoomFragment implements
edittingMessage = message;
messageFormManager.setEditMessage(message.getMessage());
}
public void refreshRoom() {
presenter.loadMessages();
}
}
\ No newline at end of file
......@@ -4,7 +4,7 @@ import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.util.Pair;
import com.fernandocejas.arrow.optional.Optional;
import com.hadisatrio.optional.Optional;
import io.reactivex.Single;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
......@@ -55,7 +55,11 @@ public class RoomPresenter extends BasePresenter<RoomContract.View>
@Override
public void bindView(@NonNull RoomContract.View view) {
super.bindView(view);
refreshRoom();
}
@Override
public void refreshRoom() {
getRoomRoles();
getRoomInfo();
getRoomHistoryStateInfo();
......
......@@ -7,7 +7,7 @@ import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.TextView;
import com.fernandocejas.arrow.optional.Optional;
import com.hadisatrio.optional.Optional;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
import org.json.JSONArray;
......@@ -117,7 +117,7 @@ public class UsersOfRoomDialogFragment extends AbstractChatRoomDialogFragment {
RecyclerView recyclerView = (RecyclerView) getDialog().findViewById(R.id.recyclerview);
recyclerView.setLayoutManager(new GridLayoutManager(getContext(), 2));
recyclerView.setAdapter(
new RoomUserAdapter(getContext(), realmHelper, rocketChatAbsoluteUrlOptional.get()));
new RoomUserAdapter(getContext(), realmHelper, rocketChatAbsoluteUrlOptional.get(), hostname));
}
private void requestGetUsersOfRoom() {
......@@ -192,7 +192,7 @@ public class UsersOfRoomDialogFragment extends AbstractChatRoomDialogFragment {
*/
private void onRenderTotalCount(long total) {
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;
import android.support.annotation.NonNull;
import com.fernandocejas.arrow.optional.Optional;
import com.hadisatrio.optional.Optional;
import io.reactivex.android.schedulers.AndroidSchedulers;
import bolts.Task;
......
......@@ -2,7 +2,7 @@ package chat.rocket.android.fragment.server_config;
import android.support.annotation.NonNull;
import com.fernandocejas.arrow.optional.Optional;
import com.hadisatrio.optional.Optional;
import io.reactivex.android.schedulers.AndroidSchedulers;
import chat.rocket.android.BackgroundLooper;
......
......@@ -224,10 +224,10 @@ public class SidebarMainFragment extends AbstractFragment implements SidebarMain
private void onRenderCurrentUser(User user, RocketChatAbsoluteUrl absoluteUrl) {
if (user != null && absoluteUrl != null) {
new UserRenderer(getContext(), user)
.avatarInto(rootView.findViewById(R.id.current_user_avatar), absoluteUrl)
.usernameInto(rootView.findViewById(R.id.current_user_name))
.statusColorInto(rootView.findViewById(R.id.current_user_status));
UserRenderer userRenderer = new UserRenderer(user);
userRenderer.showAvatar(rootView.findViewById(R.id.current_user_avatar), hostname);
userRenderer.showUsername(rootView.findViewById(R.id.current_user_name));
userRenderer.showStatusColor(rootView.findViewById(R.id.current_user_status));
}
}
......
......@@ -5,7 +5,7 @@ import android.os.Bundle;
import android.view.View;
import android.widget.AutoCompleteTextView;
import android.widget.TextView;
import com.fernandocejas.arrow.optional.Optional;
import com.hadisatrio.optional.Optional;
import com.jakewharton.rxbinding2.widget.RxTextView;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
......@@ -97,7 +97,7 @@ public class AddDirectMessageDialogFragment extends AbstractAddRoomDialogFragmen
(realm, text) -> realm.where(RealmUser.class)
.contains(RealmUser.USERNAME, text, Case.INSENSITIVE)
.findAllSorted(RealmUser.USERNAME),
context -> new SuggestUserAdapter(context, rocketChatAbsoluteUrlOptional.get()));
context -> new SuggestUserAdapter(context, rocketChatAbsoluteUrlOptional.get(), hostname));
autoCompleteTextView.setAdapter(adapter);
}
......
package chat.rocket.android.helper;
import com.fernandocejas.arrow.optional.Optional;
import com.hadisatrio.optional.Optional;
import io.reactivex.Flowable;
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;
public abstract class AbstractMessageViewHolder extends ModelViewHolder<PairedMessage> {
protected final RocketChatAvatar avatar;
protected final ImageView userNotFoundAvatarImageView;
protected final ImageView errorImageView;
protected final TextView username;
protected final TextView subUsername;
protected final TextView timestamp;
protected final View userAndTimeContainer;
protected final AbsoluteUrl absoluteUrl;
protected final String hostname;
protected final View newDayContainer;
protected final TextView newDayText;
/**
* constructor WITH hostname.
*/
public AbstractMessageViewHolder(View itemView, AbsoluteUrl absoluteUrl) {
public AbstractMessageViewHolder(View itemView, AbsoluteUrl absoluteUrl, String hostname) {
super(itemView);
avatar = itemView.findViewById(R.id.user_avatar);
userNotFoundAvatarImageView = itemView.findViewById(R.id.userNotFoundAvatarImageView);
errorImageView = itemView.findViewById(R.id.errorImageView);
username = itemView.findViewById(R.id.username);
subUsername = itemView.findViewById(R.id.sub_username);
......@@ -36,6 +39,7 @@ public abstract class AbstractMessageViewHolder extends ModelViewHolder<PairedMe
newDayContainer = itemView.findViewById(R.id.newday_container);
newDayText = itemView.findViewById(R.id.newday_text);
this.absoluteUrl = absoluteUrl;
this.hostname = hostname;
}
/**
......
......@@ -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_SYSTEM_MESSAGE = 2;
private String hostname;
private AbsoluteUrl absoluteUrl;
private boolean autoloadImages = false;
private boolean hasNext;
private boolean isLoaded;
public MessageListAdapter(Context context) {
public MessageListAdapter(Context context, String hostname) {
super(context);
this.hostname = hostname;
}
public void setAbsoluteUrl(AbsoluteUrl absoluteUrl) {
......@@ -92,11 +94,11 @@ public class MessageListAdapter extends ExtModelListAdapter<Message, PairedMessa
protected AbstractMessageViewHolder onCreateRealmModelViewHolder(int viewType, View itemView) {
switch (viewType) {
case VIEW_TYPE_NORMAL_MESSAGE:
return new MessageNormalViewHolder(itemView, absoluteUrl);
return new MessageNormalViewHolder(itemView, absoluteUrl, hostname);
case VIEW_TYPE_SYSTEM_MESSAGE:
return new MessageSystemViewHolder(itemView, absoluteUrl);
return new MessageSystemViewHolder(itemView, absoluteUrl, hostname);
default:
return new AbstractMessageViewHolder(itemView, absoluteUrl) {
return new AbstractMessageViewHolder(itemView, absoluteUrl, hostname) {
@Override
protected void bindMessage(PairedMessage pairedMessage, boolean autoloadImages) {}
};
......
......@@ -19,8 +19,8 @@ public class MessageNormalViewHolder extends AbstractMessageViewHolder {
/**
* constructor WITH hostname.
*/
public MessageNormalViewHolder(View itemView, AbsoluteUrl absoluteUrl) {
super(itemView, absoluteUrl);
public MessageNormalViewHolder(View itemView, AbsoluteUrl absoluteUrl, String hostname) {
super(itemView, absoluteUrl, hostname);
body = itemView.findViewById(R.id.message_body);
urls = itemView.findViewById(R.id.message_urls);
attachments = itemView.findViewById(R.id.message_attachments);
......@@ -28,12 +28,12 @@ public class MessageNormalViewHolder extends AbstractMessageViewHolder {
@Override
protected void bindMessage(PairedMessage pairedMessage, boolean autoloadImages) {
new MessageRenderer(itemView.getContext(), pairedMessage.target, autoloadImages)
.avatarInto(avatar, absoluteUrl)
.usernameInto(username, subUsername)
.timestampInto(timestamp)
.bodyInto(body)
.urlsInto(urls)
.attachmentsInto(attachments, absoluteUrl);
MessageRenderer messageRenderer = new MessageRenderer(pairedMessage.target, autoloadImages);
messageRenderer.showAvatar(avatar, hostname, userNotFoundAvatarImageView);
messageRenderer.showUsername(username, subUsername);
messageRenderer.showTimestampOrMessageState(timestamp);
messageRenderer.showBody(body);
messageRenderer.showUrl(urls);
messageRenderer.showAttachment(attachments, absoluteUrl);
}
}
......@@ -16,18 +16,17 @@ public class MessageSystemViewHolder extends AbstractMessageViewHolder {
/**
* constructor WITH hostname.
*/
public MessageSystemViewHolder(View itemView, AbsoluteUrl absoluteUrl) {
super(itemView, absoluteUrl);
public MessageSystemViewHolder(View itemView, AbsoluteUrl absoluteUrl, String hostname) {
super(itemView, absoluteUrl, hostname);
body = itemView.findViewById(R.id.message_body);
}
@Override
protected void bindMessage(PairedMessage pairedMessage, boolean autoloadImages) {
new MessageRenderer(itemView.getContext(), pairedMessage.target, autoloadImages)
.avatarInto(avatar, absoluteUrl)
.usernameInto(username, subUsername)
.timestampInto(timestamp);
MessageRenderer messageRenderer = new MessageRenderer(pairedMessage.target, autoloadImages);
messageRenderer.showAvatar(avatar, hostname, userNotFoundAvatarImageView);
messageRenderer.showUsername(username, subUsername);
messageRenderer.showTimestampOrMessageState(timestamp);
if (pairedMessage.target != null) {
body.setText(MessageType.parse(pairedMessage.target.getType())
.getString(body.getContext(), pairedMessage.target));
......
......@@ -24,16 +24,18 @@ public class RoomUserAdapter extends RecyclerView.Adapter<RoomUserViewHolder> {
private final LayoutInflater inflater;
private final RealmHelper realmHelper;
private final AbsoluteUrl absoluteUrl;
private final String hostname;
private List<String> usernames;
/**
* 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.inflater = LayoutInflater.from(context);
this.realmHelper = realmHelper;
this.absoluteUrl = absoluteUrl;
this.hostname = hostname;
}
@Override
......@@ -57,14 +59,15 @@ public class RoomUserAdapter extends RecyclerView.Adapter<RoomUserViewHolder> {
.setUsername(username)
.setUtcOffset(0)
.build();
new UserRenderer(context, user)
.avatarInto(holder.avatar, absoluteUrl)
.usernameInto(holder.username);
UserRenderer userRenderer = new UserRenderer(user);
userRenderer.showAvatar(holder.avatar, hostname);
userRenderer.showUsername(holder.username);
} else {
new UserRenderer(context, realmUser.asUser())
.statusColorInto(holder.status)
.avatarInto(holder.avatar, absoluteUrl)
.usernameInto(holder.username);
UserRenderer userRenderer = new UserRenderer(realmUser.asUser());
userRenderer.showAvatar(holder.avatar, hostname);
userRenderer.showUsername(holder.username);
userRenderer.showStatusColor(holder.status);
}
}
......
......@@ -16,17 +16,19 @@ import chat.rocket.android.renderer.UserRenderer;
*/
public class SuggestUserAdapter extends RealmAutoCompleteAdapter<RealmUser> {
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);
this.absoluteUrl = absoluteUrl;
this.hostname = hostname;
}
@Override
protected void onBindItemView(View itemView, RealmUser user) {
new UserRenderer(itemView.getContext(), user.asUser())
.statusColorInto(itemView.findViewById(R.id.room_user_status))
.avatarInto(itemView.findViewById(R.id.room_user_avatar), absoluteUrl);
UserRenderer userRenderer = new UserRenderer(user.asUser());
userRenderer.showStatusColor(itemView.findViewById(R.id.room_user_status));
userRenderer.showAvatar(itemView.findViewById(R.id.room_user_avatar), hostname);
}
@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;
void notifyConnectionEstablished(String hostname, String session);
void notifyConnectionLost(String hostname, int reason);
void notifyConnecting(String hostname);
}
......@@ -133,6 +133,14 @@ import rx.subjects.PublishSubject;
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
public Observable<ServerConnectivity> getServerConnectivityAsObservable() {
return Observable.concat(Observable.from(getCurrentConnectivityList()), connectivitySubject);
......
......@@ -9,6 +9,7 @@ import android.os.IBinder;
import android.support.annotation.Nullable;
import java.util.HashMap;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import chat.rocket.android.activity.MainActivity;
......@@ -24,6 +25,7 @@ public class RocketChatService extends Service implements ConnectivityServiceInt
private ConnectivityManagerInternal connectivityManager;
private HashMap<String, RocketChatWebSocketThread> webSocketThreads;
private Semaphore webSocketThreadLock = new Semaphore(1);
public class LocalBinder extends Binder {
ConnectivityServiceInterface getServiceInterface() {
......@@ -49,6 +51,7 @@ public class RocketChatService extends Service implements ConnectivityServiceInt
context.unbindService(serviceConnection);
}
@DebugLog
@Override
public void onCreate() {
super.onCreate();
......@@ -57,6 +60,7 @@ public class RocketChatService extends Service implements ConnectivityServiceInt
webSocketThreads = new HashMap<>();
}
@DebugLog
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
connectivityManager.ensureConnections();
......@@ -106,12 +110,17 @@ public class RocketChatService extends Service implements ConnectivityServiceInt
@DebugLog
private Single<RocketChatWebSocketThread> getOrCreateWebSocketThread(String hostname) {
return Single.defer(() -> {
webSocketThreadLock.acquire();
if (webSocketThreads.containsKey(hostname)) {
RocketChatWebSocketThread thread = webSocketThreads.get(hostname);
webSocketThreadLock.release();
return Single.just(thread);
}
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;
import android.content.Context;
import android.os.Handler;
import android.os.HandlerThread;
import org.json.JSONObject;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.concurrent.TimeUnit;
import bolts.Task;
import chat.rocket.android.RocketChatCache;
import chat.rocket.android.api.DDPClientWrapper;
import chat.rocket.android.api.MethodCallHelper;
import chat.rocket.android.helper.LogIfError;
import chat.rocket.android.helper.RxHelper;
import chat.rocket.android.helper.TextUtils;
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.LoginServiceConfigurationSubscriber;
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.FileUploadingToUrlObserver;
import chat.rocket.android.service.observer.FileUploadingWithUfsObserver;
......@@ -32,8 +34,13 @@ import chat.rocket.android.service.observer.NewMessageObserver;
import chat.rocket.android.service.observer.PushSettingsObserver;
import chat.rocket.android.service.observer.SessionObserver;
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 rx.Single;
import rx.subscriptions.CompositeSubscription;
/**
* Thread for handling WebSocket connection.
......@@ -62,6 +69,7 @@ public class RocketChatWebSocketThread extends HandlerThread {
private final ArrayList<Registrable> listeners = new ArrayList<>();
private DDPClientWrapper ddpClient;
private boolean listenersRegistered;
private RocketChatCache rocketChatCache;
private final DDPClientRef ddpClientRef = new DDPClientRef() {
@Override
public DDPClientWrapper get() {
......@@ -69,7 +77,6 @@ public class RocketChatWebSocketThread extends HandlerThread {
}
};
private static class KeepAliveTimer {
private long lastTime;
private final long thresholdMs;
......@@ -96,6 +103,7 @@ public class RocketChatWebSocketThread extends HandlerThread {
this.hostname = hostname;
this.realmHelper = RealmStore.getOrCreate(hostname);
this.connectivityManager = ConnectivityManager.getInstanceForInternal(appContext);
this.rocketChatCache = new RocketChatCache(appContext);
}
/**
......@@ -147,7 +155,7 @@ public class RocketChatWebSocketThread extends HandlerThread {
return Single.fromEmitter(emitter -> {
new Handler(getLooper()).post(() -> {
RCLog.d("thread %s: terminated()", Thread.currentThread().getId());
unregisterListeners();
unregisterListenersAndClose();
connectivityManager.notifyConnectionLost(hostname,
ConnectivityManagerInternal.REASON_CLOSED_BY_USER);
RocketChatWebSocketThread.super.quit();
......@@ -196,9 +204,9 @@ public class RocketChatWebSocketThread extends HandlerThread {
public void run() {
ddpClient.ping().continueWith(task -> {
if (task.isFaulted()) {
RCLog.e(task.getError());
Exception error = task.getError();
RCLog.e(error);
emitter.onSuccess(false);
ddpClient.close();
} else {
keepAliveTimer.update();
emitter.onSuccess(true);
......@@ -232,9 +240,26 @@ public class RocketChatWebSocketThread extends HandlerThread {
// handling WebSocket#onClose() callback.
task.getResult().client.getOnCloseCallback().onSuccess(_task -> {
if (listenersRegistered) {
terminate();
}
ddpClient.close();
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;
});
......@@ -265,6 +290,11 @@ public class RocketChatWebSocketThread extends HandlerThread {
}));
}
private void logErrorAndUnsubscribe(CompositeSubscription subscriptions, Throwable err) {
RCLog.e(err);
subscriptions.clear();
}
@DebugLog
private Single<Boolean> connect() {
return connectDDPClient()
......@@ -284,7 +314,7 @@ public class RocketChatWebSocketThread extends HandlerThread {
return new MethodCallHelper(realmHelper, ddpClientRef).getPermissions();
}
//@DebugLog
@DebugLog
private void registerListeners() {
if (!Thread.currentThread().getName().equals("RC_thread_" + hostname)) {
// execute in Looper.
......@@ -293,7 +323,7 @@ public class RocketChatWebSocketThread extends HandlerThread {
}
if (listenersRegistered) {
return;
unregisterListeners();
}
listenersRegistered = true;
......@@ -308,12 +338,30 @@ public class RocketChatWebSocketThread extends HandlerThread {
registrable.register();
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) {
RCLog.w(exception, "Failed to register listeners!!");
}
}
}
@DebugLog
private void unregisterListenersAndClose() {
unregisterListeners();
if (ddpClient != null) {
ddpClient.close();
ddpClient = null;
}
}
@DebugLog
private void unregisterListeners() {
Iterator<Registrable> iterator = listeners.iterator();
......@@ -323,9 +371,5 @@ public class RocketChatWebSocketThread extends HandlerThread {
iterator.remove();
}
listenersRegistered = false;
if (ddpClient != null) {
ddpClient.close();
ddpClient = null;
}
}
}
......@@ -6,7 +6,7 @@ package chat.rocket.android.service;
public class ServerConnectivity {
public static final int STATE_CONNECTED = 1;
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;
public final String hostname;
......
......@@ -185,7 +185,7 @@ public abstract class AbstractDDPDocEventSubscriber implements Registrable {
if (rxSubscription != null) {
rxSubscription.dispose();
}
if (!TextUtils.isEmpty(subscriptionId)) {
if (!TextUtils.isEmpty(subscriptionId) && ddpClientRef.get() != null) {
ddpClientRef.get().unsubscribe(subscriptionId).continueWith(new LogIfError());
}
}
......
......@@ -2,6 +2,7 @@ package chat.rocket.android.service.internal;
import android.content.Context;
import chat.rocket.android.log.RCLog;
import io.reactivex.disposables.CompositeDisposable;
import chat.rocket.android.RocketChatCache;
......@@ -47,7 +48,7 @@ public abstract class AbstractRocketChatCacheObserver implements Registrable {
compositeDisposable.add(
new RocketChatCache(context)
.getSelectedRoomIdPublisher()
.subscribe(this::updateRoomIdWith)
.subscribe(this::updateRoomIdWith, RCLog::e)
);
}
......
package chat.rocket.android.service.observer;
import android.content.Context;
import io.realm.Realm;
import io.realm.RealmResults;
import org.json.JSONObject;
import java.util.List;
import chat.rocket.android.helper.CheckSum;
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_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.
......@@ -117,9 +118,6 @@ public class MethodCallObserver extends AbstractModelObserver<MethodCall> {
} else if (exception instanceof DDPClientCallback.RPC.Timeout) {
// temp "fix"- we need to rewrite the connection layer a bit
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 {
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 @@
android:layout_height="32dp"
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
android:layout_width="match_parent"
android:layout_height="wrap_content"
......@@ -51,7 +59,8 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.RocketChat.Message.SubUsername"
tools:text="\@John Doe" />
tools:text="\@John Doe"
android:visibility="gone" />
<Space
android:layout_width="@dimen/margin_8"
......
......@@ -19,6 +19,14 @@
android:layout_height="32dp"
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
android:layout_width="match_parent"
android:layout_height="wrap_content"
......
......@@ -14,20 +14,28 @@
<string name="start_of_conversation">Start of conversation</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="not_synced">Not synced</string>
<string name="failed_to_sync">Failed to sync</string>
<string name="resend">Resend</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="file_uploading_title">Uploading…</string>
<string name="dialog_user_registration_email">Email</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="fragment_home_welcome_message">Welcome to Rocket.Chat.Android\nSelect a channel from the drawer.</string>
<string name="fragment_input_hostname_hostname">Hostname</string>
......
......@@ -7,7 +7,7 @@
machine:
environment:
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:
pre:
......
......@@ -2,7 +2,7 @@ package chat.rocket.persistence.realm.repositories;
import android.os.Looper;
import android.support.v4.util.Pair;
import com.fernandocejas.arrow.optional.Optional;
import com.hadisatrio.optional.Optional;
import io.reactivex.Flowable;
import io.reactivex.Single;
import io.reactivex.android.schedulers.AndroidSchedulers;
......
......@@ -3,7 +3,7 @@ package chat.rocket.persistence.realm.repositories;
import android.os.Looper;
import android.support.v4.util.Pair;
import com.fernandocejas.arrow.optional.Optional;
import com.hadisatrio.optional.Optional;
import io.reactivex.Flowable;
import io.reactivex.Single;
......
......@@ -2,7 +2,7 @@ package chat.rocket.persistence.realm.repositories;
import android.os.Looper;
import android.support.v4.util.Pair;
import com.fernandocejas.arrow.optional.Optional;
import com.hadisatrio.optional.Optional;
import io.reactivex.Flowable;
import io.reactivex.Single;
import io.reactivex.android.schedulers.AndroidSchedulers;
......
......@@ -2,7 +2,7 @@ package chat.rocket.persistence.realm.repositories;
import android.os.Looper;
import android.support.v4.util.Pair;
import com.fernandocejas.arrow.optional.Optional;
import com.hadisatrio.optional.Optional;
import io.reactivex.Flowable;
import io.reactivex.Single;
import io.reactivex.android.schedulers.AndroidSchedulers;
......
......@@ -2,7 +2,7 @@ package chat.rocket.persistence.realm.repositories;
import android.os.Looper;
import android.support.v4.util.Pair;
import com.fernandocejas.arrow.optional.Optional;
import com.hadisatrio.optional.Optional;
import io.reactivex.Flowable;
import io.reactivex.Single;
import io.reactivex.android.schedulers.AndroidSchedulers;
......
......@@ -2,7 +2,7 @@ package chat.rocket.persistence.realm.repositories;
import android.os.Looper;
import android.support.v4.util.Pair;
import com.fernandocejas.arrow.optional.Optional;
import com.hadisatrio.optional.Optional;
import io.reactivex.Flowable;
import io.reactivex.Single;
import io.reactivex.android.schedulers.AndroidSchedulers;
......
......@@ -2,7 +2,7 @@ package chat.rocket.persistence.realm.repositories;
import android.os.Looper;
import android.support.v4.util.Pair;
import com.fernandocejas.arrow.optional.Optional;
import com.hadisatrio.optional.Optional;
import io.reactivex.Flowable;
import io.reactivex.android.schedulers.AndroidSchedulers;
......
......@@ -2,7 +2,7 @@ package chat.rocket.persistence.realm.repositories;
import android.os.Looper;
import android.support.v4.util.Pair;
import com.fernandocejas.arrow.optional.Optional;
import com.hadisatrio.optional.Optional;
import io.reactivex.Flowable;
import io.reactivex.Single;
import io.reactivex.android.schedulers.AndroidSchedulers;
......
......@@ -2,7 +2,7 @@ package chat.rocket.persistence.realm.repositories;
import android.os.Looper;
import android.support.v4.util.Pair;
import com.fernandocejas.arrow.optional.Optional;
import com.hadisatrio.optional.Optional;
import io.reactivex.Flowable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.realm.Case;
......
package chat.rocket.android.widget;
import android.content.Context;
import chat.rocket.android.widget.fresco.CustomImageFormatConfigurator;
import com.facebook.common.logging.FLog;
import com.facebook.drawee.backends.pipeline.DraweeConfig;
import com.facebook.drawee.backends.pipeline.Fresco;
......@@ -9,8 +9,11 @@ import com.facebook.imagepipeline.backends.okhttp3.OkHttpImagePipelineConfigFact
import com.facebook.imagepipeline.core.ImagePipelineConfig;
import com.facebook.imagepipeline.listener.RequestListener;
import com.facebook.imagepipeline.listener.RequestLoggingListener;
import java.util.HashSet;
import java.util.Set;
import chat.rocket.android.widget.fresco.CustomImageFormatConfigurator;
import okhttp3.OkHttpClient;
public class RocketChatWidgets {
......
plugins {
id "org.jetbrains.kotlin.jvm" version "1.1.2-2"
id "org.jetbrains.kotlin.jvm" version "1.1.3-2"
}
apply plugin: 'idea'
......@@ -14,9 +14,9 @@ dependencies {
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.gabrielittner.auto.value:auto-value-with:1.0.0'
......
......@@ -6,7 +6,7 @@ import io.reactivex.Flowable
import io.reactivex.Single
import chat.rocket.core.repositories.UserRepository
import com.fernandocejas.arrow.optional.Optional
import com.hadisatrio.optional.Optional
import io.reactivex.functions.Function3
class CanCreateRoomInteractor(private val userRepository: UserRepository,
......
......@@ -5,7 +5,7 @@ import chat.rocket.core.PublicSettingsConstants
import chat.rocket.core.models.*
import chat.rocket.core.repositories.*
import chat.rocket.core.utils.Pair
import com.fernandocejas.arrow.optional.Optional
import com.hadisatrio.optional.Optional
import io.reactivex.Single
import io.reactivex.functions.Function4
......
package chat.rocket.core.interactors
import com.fernandocejas.arrow.optional.Optional
import com.hadisatrio.optional.Optional
import io.reactivex.Flowable
import io.reactivex.Single
import java.util.UUID
......
......@@ -5,7 +5,7 @@ import chat.rocket.core.models.Room
import chat.rocket.core.models.RoomRole
import chat.rocket.core.repositories.*
import chat.rocket.core.utils.Pair
import com.fernandocejas.arrow.optional.Optional
import com.hadisatrio.optional.Optional
import io.reactivex.Single
import io.reactivex.functions.BiFunction
......
package chat.rocket.core.interactors
import com.fernandocejas.arrow.optional.Optional
import com.hadisatrio.optional.Optional
import io.reactivex.Flowable
import io.reactivex.Single
......
package chat.rocket.core.repositories;
import com.fernandocejas.arrow.optional.Optional;
import com.hadisatrio.optional.Optional;
import io.reactivex.Flowable;
import io.reactivex.Single;
......
package chat.rocket.core.repositories;
import com.fernandocejas.arrow.optional.Optional;
import com.hadisatrio.optional.Optional;
import io.reactivex.Flowable;
import io.reactivex.Single;
......
package chat.rocket.core.repositories;
import com.fernandocejas.arrow.optional.Optional;
import com.hadisatrio.optional.Optional;
import io.reactivex.Single;
import chat.rocket.core.models.Permission;
......
package chat.rocket.core.repositories;
import com.fernandocejas.arrow.optional.Optional;
import com.hadisatrio.optional.Optional;
import io.reactivex.Single;
import chat.rocket.core.models.PublicSetting;
......
package chat.rocket.core.repositories;
import com.fernandocejas.arrow.optional.Optional;
import com.hadisatrio.optional.Optional;
import io.reactivex.Flowable;
import io.reactivex.Single;
......
package chat.rocket.core.repositories;
import com.fernandocejas.arrow.optional.Optional;
import com.hadisatrio.optional.Optional;
import io.reactivex.Single;
import chat.rocket.core.models.Room;
......
package chat.rocket.core.repositories;
import com.fernandocejas.arrow.optional.Optional;
import com.hadisatrio.optional.Optional;
import io.reactivex.Flowable;
......
package chat.rocket.core.repositories;
import com.fernandocejas.arrow.optional.Optional;
import com.hadisatrio.optional.Optional;
import io.reactivex.Flowable;
import io.reactivex.Single;
......
package chat.rocket.core.repositories;
import com.fernandocejas.arrow.optional.Optional;
import com.hadisatrio.optional.Optional;
import io.reactivex.Flowable;
import java.util.List;
......
......@@ -7,7 +7,7 @@ import chat.rocket.core.models.PublicSetting
import chat.rocket.core.models.Room
import chat.rocket.core.models.User
import chat.rocket.core.repositories.*
import com.fernandocejas.arrow.optional.Optional
import com.hadisatrio.optional.Optional
import io.reactivex.Flowable
import io.reactivex.Single
import io.reactivex.observers.TestObserver
......
......@@ -4,7 +4,7 @@ import chat.rocket.core.models.*
import chat.rocket.core.repositories.PermissionRepository
import chat.rocket.core.repositories.RoomRoleRepository
import chat.rocket.core.repositories.UserRepository
import com.fernandocejas.arrow.optional.Optional
import com.hadisatrio.optional.Optional
import io.reactivex.Flowable
import io.reactivex.Single
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