Unverified Commit 028f1cd8 authored by Rafael Kellermann Streit's avatar Rafael Kellermann Streit Committed by GitHub

Merge pull request #556 from RocketChat/fix/irreversible_offline_state

[WIP][BUG] Fix sync state when sending new messages after reconnection
parents e5198165 6fbbd3c1
...@@ -59,7 +59,7 @@ public class DDPClientImpl { ...@@ -59,7 +59,7 @@ public class DDPClientImpl {
CompositeDisposable disposables = new CompositeDisposable(); CompositeDisposable disposables = new CompositeDisposable();
disposables.add( disposables.add(
flowable.retry().filter(callback -> callback instanceof RxWebSocketCallback.Open) flowable.filter(callback -> callback instanceof RxWebSocketCallback.Open)
.subscribe( .subscribe(
callback -> callback ->
sendMessage("connect", sendMessage("connect",
...@@ -115,6 +115,7 @@ public class DDPClientImpl { ...@@ -115,6 +115,7 @@ public class DDPClientImpl {
if (requested) { if (requested) {
return flowable.filter(callback -> callback instanceof RxWebSocketCallback.Message) return flowable.filter(callback -> callback instanceof RxWebSocketCallback.Message)
.timeout(8, TimeUnit.SECONDS)
.map(callback -> ((RxWebSocketCallback.Message) callback).responseBodyString) .map(callback -> ((RxWebSocketCallback.Message) callback).responseBodyString)
.map(DDPClientImpl::toJson) .map(DDPClientImpl::toJson)
.filter(response -> "pong".equalsIgnoreCase(extractMsg(response))) .filter(response -> "pong".equalsIgnoreCase(extractMsg(response)))
......
...@@ -9,6 +9,7 @@ import com.hadisatrio.optional.Optional; ...@@ -9,6 +9,7 @@ import com.hadisatrio.optional.Optional;
import java.util.List; import java.util.List;
import chat.rocket.android.LaunchUtil; import chat.rocket.android.LaunchUtil;
import chat.rocket.android.R;
import chat.rocket.android.RocketChatCache; import chat.rocket.android.RocketChatCache;
import chat.rocket.android.helper.Logger; import chat.rocket.android.helper.Logger;
import chat.rocket.android.push.PushManager; import chat.rocket.android.push.PushManager;
...@@ -42,7 +43,6 @@ abstract class AbstractAuthedActivity extends AbstractFragmentActivity { ...@@ -42,7 +43,6 @@ abstract class AbstractAuthedActivity extends AbstractFragmentActivity {
} }
updateHostnameIfNeeded(rocketChatCache.getSelectedServerHostname()); updateHostnameIfNeeded(rocketChatCache.getSelectedServerHostname());
updateRoomIdIfNeeded(rocketChatCache.getSelectedRoomId());
} }
@Override @Override
...@@ -93,17 +93,22 @@ abstract class AbstractAuthedActivity extends AbstractFragmentActivity { ...@@ -93,17 +93,22 @@ abstract class AbstractAuthedActivity extends AbstractFragmentActivity {
if (hostname == null) { if (hostname == null) {
if (newHostname != null && assertServerRealmStoreExists(newHostname)) { if (newHostname != null && assertServerRealmStoreExists(newHostname)) {
updateHostname(newHostname); updateHostname(newHostname);
updateRoomIdIfNeeded(rocketChatCache.getSelectedRoomId());
} else { } else {
recoverFromHostnameError(); recoverFromHostnameError();
} }
} else { } else {
if (hostname.equals(newHostname)) { if (hostname.equals(newHostname)) {
updateHostname(newHostname); updateHostname(newHostname);
updateRoomIdIfNeeded(rocketChatCache.getSelectedRoomId());
return; return;
} }
if (assertServerRealmStoreExists(newHostname)) { if (assertServerRealmStoreExists(newHostname)) {
recreate(); Intent intent = new Intent(this, MainActivity.class);
startActivity(intent);
finish();
overridePendingTransition(R.anim.slide_in, R.anim.slide_out);
} else { } else {
recoverFromHostnameError(); recoverFromHostnameError();
} }
......
...@@ -138,6 +138,7 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract ...@@ -138,6 +138,7 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract
}); });
} }
} }
closeSidebarIfNeeded();
} }
private boolean closeSidebarIfNeeded() { private boolean closeSidebarIfNeeded() {
...@@ -249,7 +250,7 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract ...@@ -249,7 +250,7 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract
Snackbar.make(findViewById(getLayoutContainerForFragment()), Snackbar.make(findViewById(getLayoutContainerForFragment()),
R.string.fragment_retry_login_error_title, Snackbar.LENGTH_INDEFINITE) R.string.fragment_retry_login_error_title, Snackbar.LENGTH_INDEFINITE)
.setAction(R.string.fragment_retry_login_retry_title, view -> .setAction(R.string.fragment_retry_login_retry_title, view ->
presenter.onRetryLogin())); ConnectivityManager.getInstance(getApplicationContext()).keepAliveServer()));
} }
@Override @Override
......
...@@ -18,6 +18,7 @@ import chat.rocket.android.log.RCLog; ...@@ -18,6 +18,7 @@ import chat.rocket.android.log.RCLog;
import chat.rocket.android.service.ConnectivityManagerApi; import chat.rocket.android.service.ConnectivityManagerApi;
import chat.rocket.android.service.ServerConnectivity; import chat.rocket.android.service.ServerConnectivity;
import chat.rocket.android.shared.BasePresenter; import chat.rocket.android.shared.BasePresenter;
import chat.rocket.android_ddp.DDPClient;
import chat.rocket.core.PublicSettingsConstants; import chat.rocket.core.PublicSettingsConstants;
import chat.rocket.core.interactors.CanCreateRoomInteractor; import chat.rocket.core.interactors.CanCreateRoomInteractor;
import chat.rocket.core.interactors.RoomInteractor; import chat.rocket.core.interactors.RoomInteractor;
...@@ -226,9 +227,13 @@ public class MainPresenter extends BasePresenter<MainContract.View> ...@@ -226,9 +227,13 @@ public class MainPresenter extends BasePresenter<MainContract.View>
if (connectivity.state == ServerConnectivity.STATE_CONNECTED) { if (connectivity.state == ServerConnectivity.STATE_CONNECTED) {
view.showConnectionOk(); view.showConnectionOk();
view.refreshRoom(); view.refreshRoom();
return; } else if (connectivity.state == ServerConnectivity.STATE_DISCONNECTED) {
if (connectivity.code == DDPClient.REASON_NETWORK_ERROR) {
view.showConnectionError();
}
} else {
view.showConnecting();
} }
view.showConnecting();
}, },
Logger::report Logger::report
); );
......
...@@ -16,11 +16,13 @@ import java.util.concurrent.TimeUnit; ...@@ -16,11 +16,13 @@ import java.util.concurrent.TimeUnit;
import chat.rocket.android.RocketChatCache; import chat.rocket.android.RocketChatCache;
import chat.rocket.android.helper.RxHelper; import chat.rocket.android.helper.RxHelper;
import chat.rocket.android.log.RCLog; import chat.rocket.android.log.RCLog;
import chat.rocket.android_ddp.DDPClient;
import chat.rocket.core.models.ServerInfo; import chat.rocket.core.models.ServerInfo;
import chat.rocket.persistence.realm.models.RealmBasedServerInfo; import chat.rocket.persistence.realm.models.RealmBasedServerInfo;
import hugo.weaving.DebugLog; import hugo.weaving.DebugLog;
import rx.Observable; import rx.Observable;
import rx.Single; import rx.Single;
import rx.schedulers.Schedulers;
import rx.subjects.PublishSubject; import rx.subjects.PublishSubject;
/** /**
...@@ -28,7 +30,7 @@ import rx.subjects.PublishSubject; ...@@ -28,7 +30,7 @@ import rx.subjects.PublishSubject;
*/ */
/*package*/ class RealmBasedConnectivityManager /*package*/ class RealmBasedConnectivityManager
implements ConnectivityManagerApi, ConnectivityManagerInternal { implements ConnectivityManagerApi, ConnectivityManagerInternal {
private final ConcurrentHashMap<String, Integer> serverConnectivityList = new ConcurrentHashMap<>(); private volatile ConcurrentHashMap<String, Integer> serverConnectivityList = new ConcurrentHashMap<>();
private final PublishSubject<ServerConnectivity> connectivitySubject = PublishSubject.create(); private final PublishSubject<ServerConnectivity> connectivitySubject = PublishSubject.create();
private Context appContext; private Context appContext;
private final ServiceConnection serviceConnection = new ServiceConnection() { private final ServiceConnection serviceConnection = new ServiceConnection() {
...@@ -70,9 +72,16 @@ import rx.subjects.PublishSubject; ...@@ -70,9 +72,16 @@ import rx.subjects.PublishSubject;
@Override @Override
public void ensureConnections() { public void ensureConnections() {
String hostname = new RocketChatCache(appContext).getSelectedServerHostname(); String hostname = new RocketChatCache(appContext).getSelectedServerHostname();
if (hostname == null) {
return;
}
connectToServerIfNeeded(hostname, true/* force connect */) connectToServerIfNeeded(hostname, true/* force connect */)
.subscribeOn(Schedulers.io())
.subscribe(_val -> { .subscribe(_val -> {
}, RCLog::e); }, error -> {
RCLog.e(error);
notifyConnectionLost(hostname, DDPClient.REASON_NETWORK_ERROR);
});
} }
@Override @Override
...@@ -130,10 +139,10 @@ import rx.subjects.PublishSubject; ...@@ -130,10 +139,10 @@ import rx.subjects.PublishSubject;
@DebugLog @DebugLog
@Override @Override
public void notifyConnectionLost(String hostname, int reason) { public void notifyConnectionLost(String hostname, int code) {
serverConnectivityList.put(hostname, ServerConnectivity.STATE_DISCONNECTED); serverConnectivityList.put(hostname, ServerConnectivity.STATE_DISCONNECTED);
connectivitySubject.onNext( connectivitySubject.onNext(
new ServerConnectivity(hostname, ServerConnectivity.STATE_DISCONNECTED)); new ServerConnectivity(hostname, ServerConnectivity.STATE_DISCONNECTED, code));
} }
@DebugLog @DebugLog
...@@ -167,13 +176,16 @@ import rx.subjects.PublishSubject; ...@@ -167,13 +176,16 @@ import rx.subjects.PublishSubject;
.flatMap(_val -> connectToServerIfNeeded(hostname, forceConnect)); .flatMap(_val -> connectToServerIfNeeded(hostname, forceConnect));
} }
if (connectivity == ServerConnectivity.STATE_CONNECTING) { // if (connectivity == ServerConnectivity.STATE_CONNECTING) {
return waitForConnected(hostname); // return waitForConnected(hostname)
// .doOnError(error -> notifyConnectionLost(hostname, REASON_NETWORK_ERROR));
// }
if (connectivity == ServerConnectivity.STATE_DISCONNECTED) {
notifyConnecting(hostname);
} }
return connectToServer(hostname) return connectToServer(hostname);
.doOnError(RCLog::e)
.retryWhen(RxHelper.exponentialBackoff(Integer.MAX_VALUE, 500, TimeUnit.MILLISECONDS));
}); });
} }
...@@ -186,7 +198,7 @@ import rx.subjects.PublishSubject; ...@@ -186,7 +198,7 @@ import rx.subjects.PublishSubject;
if (connectivity == ServerConnectivity.STATE_CONNECTING) { if (connectivity == ServerConnectivity.STATE_CONNECTING) {
return waitForConnected(hostname) return waitForConnected(hostname)
.onErrorReturn(err -> true) .doOnError(err -> notifyConnectionLost(hostname, DDPClient.REASON_NETWORK_ERROR))
.flatMap(_val -> disconnectFromServerIfNeeded(hostname)); .flatMap(_val -> disconnectFromServerIfNeeded(hostname));
} }
...@@ -195,8 +207,7 @@ import rx.subjects.PublishSubject; ...@@ -195,8 +207,7 @@ import rx.subjects.PublishSubject;
} }
return disconnectFromServer(hostname) return disconnectFromServer(hostname)
//.doOnError(RCLog::e) .retryWhen(RxHelper.exponentialBackoff(1, 500, TimeUnit.MILLISECONDS));
.retryWhen(RxHelper.exponentialBackoff(3, 500, TimeUnit.MILLISECONDS));
}); });
} }
...@@ -237,7 +248,7 @@ import rx.subjects.PublishSubject; ...@@ -237,7 +248,7 @@ import rx.subjects.PublishSubject;
if (serverConnectivityList.get(hostname) != ServerConnectivity.STATE_CONNECTED) { if (serverConnectivityList.get(hostname) != ServerConnectivity.STATE_CONNECTED) {
// Mark as CONNECTING except for the case [forceConnect && connected] because // Mark as CONNECTING except for the case [forceConnect && connected] because
// ensureConnectionToServer doesn't notify ConnectionEstablished/Lost is already connected. // ensureConnectionToServer doesn't notify ConnectionEstablished/Lost is already connected.
serverConnectivityList.put(hostname, ServerConnectivity.STATE_CONNECTING); // serverConnectivityList.put(hostname, ServerConnectivity.STATE_CONNECTING);
} }
if (serviceInterface != null) { if (serviceInterface != null) {
......
...@@ -8,11 +8,11 @@ import android.os.Binder; ...@@ -8,11 +8,11 @@ import android.os.Binder;
import android.os.IBinder; import android.os.IBinder;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import java.util.HashMap;
import java.util.concurrent.Semaphore; import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import chat.rocket.android.helper.Logger; import chat.rocket.android.helper.Logger;
import chat.rocket.android.log.RCLog;
import chat.rocket.persistence.realm.RealmStore; import chat.rocket.persistence.realm.RealmStore;
import hugo.weaving.DebugLog; import hugo.weaving.DebugLog;
import rx.Observable; import rx.Observable;
...@@ -24,8 +24,8 @@ import rx.Single; ...@@ -24,8 +24,8 @@ import rx.Single;
public class RocketChatService extends Service implements ConnectivityServiceInterface { public class RocketChatService extends Service implements ConnectivityServiceInterface {
private ConnectivityManagerInternal connectivityManager; private ConnectivityManagerInternal connectivityManager;
private HashMap<String, RocketChatWebSocketThread> webSocketThreads; private static volatile Semaphore webSocketThreadLock = new Semaphore(1);
private Semaphore webSocketThreadLock = new Semaphore(1); private static volatile RocketChatWebSocketThread currentWebSocketThread;
public class LocalBinder extends Binder { public class LocalBinder extends Binder {
ConnectivityServiceInterface getServiceInterface() { ConnectivityServiceInterface getServiceInterface() {
...@@ -57,7 +57,6 @@ public class RocketChatService extends Service implements ConnectivityServiceInt ...@@ -57,7 +57,6 @@ public class RocketChatService extends Service implements ConnectivityServiceInt
super.onCreate(); super.onCreate();
connectivityManager = ConnectivityManager.getInstanceForInternal(getApplicationContext()); connectivityManager = ConnectivityManager.getInstanceForInternal(getApplicationContext());
connectivityManager.resetConnectivityStateList(); connectivityManager.resetConnectivityStateList();
webSocketThreads = new HashMap<>();
} }
@DebugLog @DebugLog
...@@ -71,8 +70,9 @@ public class RocketChatService extends Service implements ConnectivityServiceInt ...@@ -71,8 +70,9 @@ public class RocketChatService extends Service implements ConnectivityServiceInt
public Single<Boolean> ensureConnectionToServer(String hostname) { //called via binder. public Single<Boolean> ensureConnectionToServer(String hostname) { //called via binder.
return getOrCreateWebSocketThread(hostname) return getOrCreateWebSocketThread(hostname)
.doOnError(err -> { .doOnError(err -> {
webSocketThreads.remove(hostname); err.printStackTrace();
connectivityManager.notifyConnectionLost(hostname, ConnectivityManagerInternal.REASON_NETWORK_ERROR); currentWebSocketThread = null;
// connectivityManager.notifyConnectionLost(hostname, ConnectivityManagerInternal.REASON_NETWORK_ERROR);
}) })
.flatMap(webSocketThreads -> webSocketThreads.keepAlive()); .flatMap(webSocketThreads -> webSocketThreads.keepAlive());
} }
...@@ -80,17 +80,15 @@ public class RocketChatService extends Service implements ConnectivityServiceInt ...@@ -80,17 +80,15 @@ public class RocketChatService extends Service implements ConnectivityServiceInt
@Override @Override
public Single<Boolean> disconnectFromServer(String hostname) { //called via binder. public Single<Boolean> disconnectFromServer(String hostname) { //called via binder.
return Single.defer(() -> { return Single.defer(() -> {
if (!webSocketThreads.containsKey(hostname)) { if (!existsThreadForHostname(hostname)) {
return Single.just(true); return Single.just(true);
} }
RocketChatWebSocketThread thread = webSocketThreads.get(hostname); if (currentWebSocketThread != null) {
if (thread != null) { return currentWebSocketThread.terminate()
return thread.terminate()
// after disconnection from server // after disconnection from server
.doAfterTerminate(() -> { .doAfterTerminate(() -> {
// remove RCWebSocket key from HashMap currentWebSocketThread = null;
webSocketThreads.remove(hostname);
// remove RealmConfiguration key from HashMap // remove RealmConfiguration key from HashMap
RealmStore.sStore.remove(hostname); RealmStore.sStore.remove(hostname);
}); });
...@@ -106,25 +104,54 @@ public class RocketChatService extends Service implements ConnectivityServiceInt ...@@ -106,25 +104,54 @@ public class RocketChatService extends Service implements ConnectivityServiceInt
return Single.defer(() -> { return Single.defer(() -> {
webSocketThreadLock.acquire(); webSocketThreadLock.acquire();
int connectivityState = ConnectivityManager.getInstance(getApplicationContext()).getConnectivityState(hostname); int connectivityState = ConnectivityManager.getInstance(getApplicationContext()).getConnectivityState(hostname);
boolean isConnected = connectivityState == ServerConnectivity.STATE_CONNECTED; boolean isDisconnected = connectivityState != ServerConnectivity.STATE_CONNECTED;
if (webSocketThreads.containsKey(hostname) && isConnected) { if (currentWebSocketThread != null && existsThreadForHostname(hostname) && !isDisconnected) {
RocketChatWebSocketThread thread = webSocketThreads.get(hostname);
webSocketThreadLock.release(); webSocketThreadLock.release();
return Single.just(thread); return Single.just(currentWebSocketThread);
} }
connectivityManager.notifyConnecting(hostname); connectivityManager.notifyConnecting(hostname);
if (currentWebSocketThread != null) {
return currentWebSocketThread.terminate()
.doAfterTerminate(() -> currentWebSocketThread = null)
.doOnError(RCLog::e)
.flatMap(terminated ->
RocketChatWebSocketThread.getStarted(getApplicationContext(), hostname)
.doOnSuccess(thread -> {
currentWebSocketThread = thread;
webSocketThreadLock.release();
})
.doOnError(throwable -> {
currentWebSocketThread = null;
RCLog.e(throwable);
Logger.report(throwable);
webSocketThreadLock.release();
})
);
}
return RocketChatWebSocketThread.getStarted(getApplicationContext(), hostname) return RocketChatWebSocketThread.getStarted(getApplicationContext(), hostname)
.doOnSuccess(thread -> { .doOnSuccess(thread -> {
webSocketThreads.put(hostname, thread); currentWebSocketThread = thread;
webSocketThreadLock.release(); webSocketThreadLock.release();
}) })
.doOnError(throwable -> { .doOnError(throwable -> {
currentWebSocketThread = null;
RCLog.e(throwable);
Logger.report(throwable); Logger.report(throwable);
webSocketThreadLock.release(); webSocketThreadLock.release();
}); });
}); });
} }
private boolean existsThreadForHostname(String hostname) {
if (hostname == null || currentWebSocketThread == null) {
return false;
}
return currentWebSocketThread.getName().equals("RC_thread_" + hostname);
}
@Nullable @Nullable
@Override @Override
public IBinder onBind(Intent intent) { public IBinder onBind(Intent intent) {
......
...@@ -11,6 +11,7 @@ import java.util.ArrayList; ...@@ -11,6 +11,7 @@ import java.util.ArrayList;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import bolts.Task; import bolts.Task;
import chat.rocket.android.api.MethodCallHelper; import chat.rocket.android.api.MethodCallHelper;
...@@ -34,6 +35,7 @@ import chat.rocket.android.service.observer.PushSettingsObserver; ...@@ -34,6 +35,7 @@ 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_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.rx.RxWebSocketCallback;
import chat.rocket.core.models.ServerInfo; import chat.rocket.core.models.ServerInfo;
import chat.rocket.persistence.realm.RealmHelper; import chat.rocket.persistence.realm.RealmHelper;
import chat.rocket.persistence.realm.RealmStore; import chat.rocket.persistence.realm.RealmStore;
...@@ -121,7 +123,7 @@ public class RocketChatWebSocketThread extends HandlerThread { ...@@ -121,7 +123,7 @@ public class RocketChatWebSocketThread extends HandlerThread {
} }
}.start(); }.start();
}).flatMap(webSocket -> }).flatMap(webSocket ->
webSocket.connect().map(_val -> webSocket)); webSocket.connectWithExponentialBackoff().map(_val -> webSocket));
} }
@Override @Override
...@@ -154,14 +156,14 @@ public class RocketChatWebSocketThread extends HandlerThread { ...@@ -154,14 +156,14 @@ public class RocketChatWebSocketThread extends HandlerThread {
RCLog.d("thread %s: terminated()", Thread.currentThread().getId()); RCLog.d("thread %s: terminated()", Thread.currentThread().getId());
unregisterListenersAndClose(); unregisterListenersAndClose();
connectivityManager.notifyConnectionLost(hostname, connectivityManager.notifyConnectionLost(hostname,
ConnectivityManagerInternal.REASON_CLOSED_BY_USER); DDPClient.REASON_CLOSED_BY_USER);
RocketChatWebSocketThread.super.quit(); RocketChatWebSocketThread.super.quit();
emitter.onSuccess(true); emitter.onSuccess(true);
}); });
}); });
} else { } else {
connectivityManager.notifyConnectionLost(hostname, connectivityManager.notifyConnectionLost(hostname,
ConnectivityManagerInternal.REASON_NETWORK_ERROR); DDPClient.REASON_NETWORK_ERROR);
super.quit(); super.quit();
return Single.just(true); return Single.just(true);
} }
...@@ -205,7 +207,7 @@ public class RocketChatWebSocketThread extends HandlerThread { ...@@ -205,7 +207,7 @@ public class RocketChatWebSocketThread extends HandlerThread {
Exception error = task.getError(); Exception error = task.getError();
RCLog.e(error); RCLog.e(error);
connectivityManager.notifyConnectionLost( connectivityManager.notifyConnectionLost(
hostname, ConnectivityManagerInternal.REASON_NETWORK_ERROR); hostname, DDPClient.REASON_CLOSED_BY_USER);
emitter.onError(error); emitter.onError(error);
} else { } else {
keepAliveTimer.update(); keepAliveTimer.update();
...@@ -257,42 +259,43 @@ public class RocketChatWebSocketThread extends HandlerThread { ...@@ -257,42 +259,43 @@ public class RocketChatWebSocketThread extends HandlerThread {
} }
RCLog.d("DDPClient#connect"); RCLog.d("DDPClient#connect");
DDPClient.get().connect(hostname, info.getSession(), info.isSecure()) DDPClient.get().connect(hostname, info.getSession(), info.isSecure())
.onSuccessTask(task -> { .onSuccessTask(task -> {
final String newSession = task.getResult().session; final String newSession = task.getResult().session;
connectivityManager.notifyConnectionEstablished(hostname, newSession); connectivityManager.notifyConnectionEstablished(hostname, newSession);
// handling WebSocket#onClose() callback. // handling WebSocket#onClose() callback.
task.getResult().client.getOnCloseCallback().onSuccess(_task -> { task.getResult().client.getOnCloseCallback().onSuccess(_task -> {
if (_task.getResult().code != 1000) { RxWebSocketCallback.Close result = _task.getResult();
reconnect(); if (result.code == DDPClient.REASON_NETWORK_ERROR) {
} reconnect();
return null; }
}); return null;
});
return realmHelper.executeTransaction(realm -> {
RealmSession sessionObj = RealmSession.queryDefaultSession(realm).findFirst(); return realmHelper.executeTransaction(realm -> {
if (sessionObj == null) { RealmSession sessionObj = RealmSession.queryDefaultSession(realm).findFirst();
realm.createOrUpdateObjectFromJson(RealmSession.class, if (sessionObj == null) {
new JSONObject().put(RealmSession.ID, RealmSession.DEFAULT_ID)); realm.createOrUpdateObjectFromJson(RealmSession.class,
} else { new JSONObject().put(RealmSession.ID, RealmSession.DEFAULT_ID));
// invalidate login token. } else {
if (!TextUtils.isEmpty(sessionObj.getToken()) && sessionObj.isTokenVerified()) { // invalidate login token.
sessionObj.setTokenVerified(false); if (!TextUtils.isEmpty(sessionObj.getToken()) && sessionObj.isTokenVerified()) {
sessionObj.setError(null); sessionObj.setTokenVerified(false);
} sessionObj.setError(null);
}
} }
return null; return null;
}); });
}) })
.continueWith(task -> { .continueWith(task -> {
if (task.isFaulted()) { if (task.isFaulted()) {
emitter.onError(task.getError()); emitter.onError(task.getError());
} else { } else {
emitter.onSuccess(true); emitter.onSuccess(true);
} }
return null; return null;
}); });
})); }));
} }
...@@ -301,22 +304,25 @@ public class RocketChatWebSocketThread extends HandlerThread { ...@@ -301,22 +304,25 @@ public class RocketChatWebSocketThread extends HandlerThread {
if (reconnectSubscription.hasSubscriptions()) { if (reconnectSubscription.hasSubscriptions()) {
return; return;
} }
DDPClient.get().close();
forceInvalidateTokens(); forceInvalidateTokens();
connectivityManager.notifyConnecting(hostname); connectivityManager.notifyConnecting(hostname);
// Needed to use subscriptions because of legacy code. // Needed to use subscriptions because of legacy code.
// TODO: Should update to RxJava 2 // TODO: Should update to RxJava 2
reconnectSubscription.add( reconnectSubscription.add(
connectWithExponentialBackoff() connectWithExponentialBackoff()
.subscribe( .subscribe(
connected -> { connected -> {
if (!connected) { if (!connected) {
connectivityManager.notifyConnecting(hostname); connectivityManager.notifyConnecting(hostname);
} }
reconnectSubscription.clear(); reconnectSubscription.clear();
}, },
err -> logErrorAndUnsubscribe(reconnectSubscription, err) error -> {
) logErrorAndUnsubscribe(reconnectSubscription, error);
connectivityManager.notifyConnectionLost(hostname,
DDPClient.REASON_NETWORK_ERROR);
}
)
); );
} }
...@@ -326,7 +332,7 @@ public class RocketChatWebSocketThread extends HandlerThread { ...@@ -326,7 +332,7 @@ public class RocketChatWebSocketThread extends HandlerThread {
} }
private Single<Boolean> connectWithExponentialBackoff() { private Single<Boolean> connectWithExponentialBackoff() {
return connect().retryWhen(RxHelper.exponentialBackoff(Integer.MAX_VALUE, 500, TimeUnit.MILLISECONDS)); return connect().retryWhen(RxHelper.exponentialBackoff(3, 500, TimeUnit.MILLISECONDS));
} }
@DebugLog @DebugLog
...@@ -429,7 +435,7 @@ public class RocketChatWebSocketThread extends HandlerThread { ...@@ -429,7 +435,7 @@ public class RocketChatWebSocketThread extends HandlerThread {
RCLog.e(error); RCLog.e(error);
// Stop pinging // Stop pinging
hearbeatDisposable.clear(); hearbeatDisposable.clear();
if (error instanceof DDPClientCallback.Closed) { if (error instanceof DDPClientCallback.Closed || error instanceof TimeoutException) {
RCLog.d("Hearbeat failure: retrying connection..."); RCLog.d("Hearbeat failure: retrying connection...");
reconnect(); reconnect();
} }
...@@ -440,10 +446,8 @@ public class RocketChatWebSocketThread extends HandlerThread { ...@@ -440,10 +446,8 @@ public class RocketChatWebSocketThread extends HandlerThread {
@DebugLog @DebugLog
private void unregisterListenersAndClose() { private void unregisterListenersAndClose() {
unregisterListeners(); unregisterListeners();
if (DDPClient.get() != null) { DDPClient.get().close();
DDPClient.get().close();
}
} }
@DebugLog @DebugLog
......
...@@ -11,10 +11,18 @@ public class ServerConnectivity { ...@@ -11,10 +11,18 @@ public class ServerConnectivity {
public final String hostname; public final String hostname;
public final int state; public final int state;
public final int code;
public ServerConnectivity(String hostname, int state) { ServerConnectivity(String hostname, int state) {
this.hostname = hostname; this.hostname = hostname;
this.state = state; this.state = state;
this.code = -1;
}
ServerConnectivity(String hostname, int state, int code) {
this.hostname = hostname;
this.state = state;
this.code = code;
} }
/** /**
......
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="true"
android:interpolator="@android:anim/decelerate_interpolator">
<translate
android:duration="@android:integer/config_mediumAnimTime"
android:fromYDelta="100%p"
android:toYDelta="0">
</translate>
</set>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="@android:integer/config_mediumAnimTime"
android:fromYDelta="0%p"
android:toYDelta="0">
</translate>
</set>
\ No newline at end of file
...@@ -14,5 +14,4 @@ ...@@ -14,5 +14,4 @@
org.gradle.jvmargs=-Xmx6144M org.gradle.jvmargs=-Xmx6144M
#org.gradle.parallel=true #org.gradle.parallel=true
android.enableBuildCache=true android.enableBuildCache=true
android.enableAapt2=false \ No newline at end of file
\ No newline at end of file
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