Commit f900423f authored by Leonardo Aramaki's avatar Leonardo Aramaki

Update reconnection logic to dispatch a KeepAliveJob when connections goes...

Update reconnection logic to dispatch a KeepAliveJob when connections goes off. Change a tidbits of the logic to close connections and reconnect
parent e251631d
...@@ -84,7 +84,11 @@ public class DDPClient { ...@@ -84,7 +84,11 @@ public class DDPClient {
} }
public void close() { public void close() {
impl.close(REASON_CLOSED_BY_USER, "closed by DDPClient#close()"); close(REASON_CLOSED_BY_USER);
}
public void close(int reason) {
impl.close(reason, "closed by DDPClient#close()");
} }
/** /**
......
...@@ -5,7 +5,7 @@ import android.graphics.drawable.Drawable; ...@@ -5,7 +5,7 @@ import android.graphics.drawable.Drawable;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.design.widget.Snackbar; import android.support.graphics.drawable.AnimatedVectorDrawableCompat;
import android.support.v4.app.Fragment; import android.support.v4.app.Fragment;
import android.support.v4.content.ContextCompat; import android.support.v4.content.ContextCompat;
import android.support.v4.widget.SlidingPaneLayout; import android.support.v4.widget.SlidingPaneLayout;
...@@ -20,6 +20,7 @@ import com.facebook.drawee.view.SimpleDraweeView; ...@@ -20,6 +20,7 @@ import com.facebook.drawee.view.SimpleDraweeView;
import java.util.List; import java.util.List;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
import chat.rocket.android.ConnectionStatusManager;
import chat.rocket.android.LaunchUtil; import chat.rocket.android.LaunchUtil;
import chat.rocket.android.R; import chat.rocket.android.R;
import chat.rocket.android.RocketChatCache; import chat.rocket.android.RocketChatCache;
...@@ -31,6 +32,7 @@ import chat.rocket.android.helper.KeyboardHelper; ...@@ -31,6 +32,7 @@ import chat.rocket.android.helper.KeyboardHelper;
import chat.rocket.android.service.ConnectivityManager; import chat.rocket.android.service.ConnectivityManager;
import chat.rocket.android.service.ConnectivityManagerApi; import chat.rocket.android.service.ConnectivityManagerApi;
import chat.rocket.android.widget.RoomToolbar; import chat.rocket.android.widget.RoomToolbar;
import chat.rocket.android.widget.helper.DebouncingOnClickListener;
import chat.rocket.android.widget.helper.FrescoHelper; import chat.rocket.android.widget.helper.FrescoHelper;
import chat.rocket.core.interactors.CanCreateRoomInteractor; import chat.rocket.core.interactors.CanCreateRoomInteractor;
import chat.rocket.core.interactors.RoomInteractor; import chat.rocket.core.interactors.RoomInteractor;
...@@ -41,6 +43,8 @@ import chat.rocket.persistence.realm.repositories.RealmPublicSettingRepository; ...@@ -41,6 +43,8 @@ import chat.rocket.persistence.realm.repositories.RealmPublicSettingRepository;
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;
import de.keyboardsurfer.android.widget.crouton.Configuration;
import de.keyboardsurfer.android.widget.crouton.Crouton;
import hugo.weaving.DebugLog; import hugo.weaving.DebugLog;
/** /**
...@@ -50,7 +54,11 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract ...@@ -50,7 +54,11 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract
private RoomToolbar toolbar; private RoomToolbar toolbar;
private SlidingPaneLayout pane; private SlidingPaneLayout pane;
private MainContract.Presenter presenter; private MainContract.Presenter presenter;
private volatile AtomicReference<Snackbar> statusTicker = new AtomicReference<>(); private volatile AtomicReference<Crouton> croutonStatusTicker = new AtomicReference<>();
private View croutonView;
private ImageView croutonTryAgainImage;
private TextView croutonText;
private AnimatedVectorDrawableCompat tryAgainSpinnerAnimatedDrawable;
@Override @Override
public int getLayoutContainerForFragment() { public int getLayoutContainerForFragment() {
...@@ -63,6 +71,7 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract ...@@ -63,6 +71,7 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract
setContentView(R.layout.activity_main); setContentView(R.layout.activity_main);
toolbar = findViewById(R.id.activity_main_toolbar); toolbar = findViewById(R.id.activity_main_toolbar);
pane = findViewById(R.id.sliding_pane); pane = findViewById(R.id.sliding_pane);
loadCroutonViewIfNeeded();
setupToolbar(); setupToolbar();
} }
...@@ -95,10 +104,7 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract ...@@ -95,10 +104,7 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract
if (presenter != null) { if (presenter != null) {
presenter.release(); presenter.release();
} }
// Dismiss any status ticker Crouton.cancelAllCroutons();
if (statusTicker.get() != null) {
statusTicker.get().dismiss();
}
super.onPause(); super.onPause();
} }
...@@ -254,44 +260,80 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract ...@@ -254,44 +260,80 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract
@Override @Override
public void showConnectionError() { public void showConnectionError() {
if (statusTicker.get() != null && statusTicker.get().isShown()) { ConnectionStatusManager.INSTANCE.setConnectionError(this::showConnectionErrorCrouton);
statusTicker.get().setText(R.string.fragment_retry_login_error_title)
.setAction(R.string.fragment_retry_login_retry_title, view -> retryConnection());
} else {
Snackbar newStatusTicker = Snackbar.make(findViewById(getLayoutContainerForFragment()),
R.string.fragment_retry_login_error_title, Snackbar.LENGTH_INDEFINITE)
.setAction(R.string.fragment_retry_login_retry_title, view -> retryConnection());
statusTicker.set(newStatusTicker);
statusTicker.get().show();
}
} }
@Override @Override
public void showConnecting() { public void showConnecting() {
if (statusTicker.get() != null && statusTicker.get().isShown()) { ConnectionStatusManager.INSTANCE.setConnecting(this::showConnectingCrouton);
statusTicker.get().setText(R.string.server_config_activity_authenticating)
.setAction(null, null);
} else {
Snackbar newStatusTicker = Snackbar.make(findViewById(getLayoutContainerForFragment()),
R.string.server_config_activity_authenticating, Snackbar.LENGTH_INDEFINITE);
statusTicker.set(newStatusTicker);
statusTicker.get().show();
}
} }
@Override @Override
public void showConnectionOk() { public void showConnectionOk() {
dismissStatusTickerIfShowing(); ConnectionStatusManager.INSTANCE.setOnline(this::dismissStatusTickerIfShowing);
}
private void showConnectingCrouton(boolean success) {
if (success) {
croutonText.setText(R.string.server_config_activity_authenticating);
croutonTryAgainImage.setOnClickListener(null);
tryAgainSpinnerAnimatedDrawable.start();
Crouton.cancelAllCroutons();
updateCrouton();
croutonStatusTicker.get().show();
}
}
private void showConnectionErrorCrouton(boolean success) {
if (success) {
tryAgainSpinnerAnimatedDrawable.stop();
croutonText.setText(R.string.fragment_retry_login_error_title);
croutonTryAgainImage.setOnClickListener(new DebouncingOnClickListener() {
@Override
public void doClick(View v) {
retryConnection();
}
});
Crouton.cancelAllCroutons();
updateCrouton();
croutonStatusTicker.get().show();
}
}
private void loadCroutonViewIfNeeded() {
if (croutonView == null) {
croutonView = LayoutInflater.from(this).inflate(R.layout.crouton_status_ticker, null);
croutonTryAgainImage = croutonView.findViewById(R.id.try_again_image);
croutonText = croutonView.findViewById(R.id.text_view_status);
tryAgainSpinnerAnimatedDrawable =
AnimatedVectorDrawableCompat.create(this, R.drawable.ic_loading_animated);
croutonTryAgainImage.setImageDrawable(tryAgainSpinnerAnimatedDrawable);
updateCrouton();
}
}
private void updateCrouton() {
Configuration configuration = new Configuration.Builder()
.setDuration(Configuration.DURATION_INFINITE).build();
Crouton crouton = Crouton.make(this, croutonView, getLayoutContainerForFragment())
.setConfiguration(configuration);
croutonStatusTicker.set(crouton);
} }
private void dismissStatusTickerIfShowing() { private void dismissStatusTickerIfShowing(boolean success) {
if (statusTicker != null) { if (success && croutonStatusTicker.get() != null) {
statusTicker.get().dismiss(); croutonStatusTicker.get().hide();
} }
} }
private void retryConnection() { private void retryConnection() {
statusTicker.set(null); croutonStatusTicker.set(null);
showConnecting();
ConnectivityManager.getInstance(getApplicationContext()).keepAliveServer(); ConnectivityManager.getInstance(getApplicationContext()).keepAliveServer();
} }
......
...@@ -16,7 +16,6 @@ import chat.rocket.android.helper.LogIfError; ...@@ -16,7 +16,6 @@ import chat.rocket.android.helper.LogIfError;
import chat.rocket.android.helper.Logger; import chat.rocket.android.helper.Logger;
import chat.rocket.android.log.RCLog; import chat.rocket.android.log.RCLog;
import chat.rocket.android.service.ConnectivityManagerApi; import chat.rocket.android.service.ConnectivityManagerApi;
import chat.rocket.android.service.KeepAliveJob;
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.android_ddp.DDPClient;
...@@ -228,13 +227,10 @@ public class MainPresenter extends BasePresenter<MainContract.View> ...@@ -228,13 +227,10 @@ public class MainPresenter extends BasePresenter<MainContract.View>
.subscribe( .subscribe(
connectivity -> { connectivity -> {
if (connectivity.state == ServerConnectivity.STATE_CONNECTED) { if (connectivity.state == ServerConnectivity.STATE_CONNECTED) {
KeepAliveJob.Companion.cancel();
//TODO: notify almost connected or something like that. //TODO: notify almost connected or something like that.
// view.showConnectionOk(); // view.showConnectionOk();
} else if (connectivity.state == ServerConnectivity.STATE_DISCONNECTED) { } else if (connectivity.state == ServerConnectivity.STATE_DISCONNECTED) {
KeepAliveJob.Companion.cancel();
if (connectivity.code == DDPClient.REASON_NETWORK_ERROR) { if (connectivity.code == DDPClient.REASON_NETWORK_ERROR) {
KeepAliveJob.Companion.schedule();
view.showConnectionError(); view.showConnectionError();
} }
} else if (connectivity.state == ServerConnectivity.STATE_SESSION_ESTABLISHED) { } else if (connectivity.state == ServerConnectivity.STATE_SESSION_ESTABLISHED) {
......
...@@ -80,7 +80,7 @@ public class RocketChatService extends Service implements ConnectivityServiceInt ...@@ -80,7 +80,7 @@ public class RocketChatService extends Service implements ConnectivityServiceInt
} }
if (currentWebSocketThread != null) { if (currentWebSocketThread != null) {
return currentWebSocketThread.terminate() return currentWebSocketThread.terminate(false)
// after disconnection from server // after disconnection from server
.doAfterTerminate(() -> { .doAfterTerminate(() -> {
currentWebSocketThread = null; currentWebSocketThread = null;
...@@ -106,7 +106,7 @@ public class RocketChatService extends Service implements ConnectivityServiceInt ...@@ -106,7 +106,7 @@ public class RocketChatService extends Service implements ConnectivityServiceInt
} }
if (currentWebSocketThread != null) { if (currentWebSocketThread != null) {
return currentWebSocketThread.terminate() return currentWebSocketThread.terminate(isDisconnected)
.doAfterTerminate(() -> currentWebSocketThread = null) .doAfterTerminate(() -> currentWebSocketThread = null)
.flatMap(terminated -> .flatMap(terminated ->
RocketChatWebSocketThread.getStarted(getApplicationContext(), hostname) RocketChatWebSocketThread.getStarted(getApplicationContext(), hostname)
......
...@@ -54,7 +54,7 @@ public class RocketChatWebSocketThread extends HandlerThread { ...@@ -54,7 +54,7 @@ public class RocketChatWebSocketThread extends HandlerThread {
LoginServiceConfigurationSubscriber.class, LoginServiceConfigurationSubscriber.class,
ActiveUsersSubscriber.class, ActiveUsersSubscriber.class,
UserDataSubscriber.class, UserDataSubscriber.class,
MethodCallObserver.class, // MethodCallObserver.class,
LoadMessageProcedureObserver.class, LoadMessageProcedureObserver.class,
GetUsersOfRoomsProcedureObserver.class, GetUsersOfRoomsProcedureObserver.class,
NewMessageObserver.class, NewMessageObserver.class,
...@@ -125,16 +125,21 @@ public class RocketChatWebSocketThread extends HandlerThread { ...@@ -125,16 +125,21 @@ public class RocketChatWebSocketThread extends HandlerThread {
/** /**
* terminate WebSocket thread. * terminate WebSocket thread.
*
* @param isDisconnected {@code true} If we're trying to terminate a disconnected a websocket
* thread which means it has failed.
*/ */
@DebugLog @DebugLog
/* package */ Single<Boolean> terminate() { /* package */ Single<Boolean> terminate(boolean isDisconnected) {
if (isAlive()) { if (isAlive()) {
return Single.create(emitter -> { return Single.create(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());
unregisterListenersAndClose(); int reason = (isDisconnected) ?
DDPClient.REASON_NETWORK_ERROR : DDPClient.REASON_CLOSED_BY_USER;
unregisterListenersAndClose(reason);
connectivityManager.notifyConnectionLost(hostname, connectivityManager.notifyConnectionLost(hostname,
DDPClient.REASON_CLOSED_BY_USER); isDisconnected ? DDPClient.REASON_NETWORK_ERROR : DDPClient.REASON_CLOSED_BY_USER);
RocketChatWebSocketThread.super.quit(); RocketChatWebSocketThread.super.quit();
emitter.onSuccess(true); emitter.onSuccess(true);
}); });
...@@ -162,7 +167,7 @@ public class RocketChatWebSocketThread extends HandlerThread { ...@@ -162,7 +167,7 @@ public class RocketChatWebSocketThread extends HandlerThread {
@DebugLog @DebugLog
/* package */ Single<Boolean> keepAlive() { /* package */ Single<Boolean> keepAlive() {
return checkIfConnectionAlive() return checkIfConnectionAlive()
.flatMap(alive -> alive ? Single.just(true) : connectWithExponentialBackoff()); .flatMap(alive -> connectWithExponentialBackoff());
} }
@DebugLog @DebugLog
...@@ -228,8 +233,6 @@ public class RocketChatWebSocketThread extends HandlerThread { ...@@ -228,8 +233,6 @@ public class RocketChatWebSocketThread extends HandlerThread {
RxWebSocketCallback.Close result = _task.getResult(); RxWebSocketCallback.Close result = _task.getResult();
if (result.code == DDPClient.REASON_NETWORK_ERROR) { if (result.code == DDPClient.REASON_NETWORK_ERROR) {
reconnect(); reconnect();
} else {
unregisterListenersAndClose();
} }
return null; return null;
}); });
...@@ -367,9 +370,6 @@ public class RocketChatWebSocketThread extends HandlerThread { ...@@ -367,9 +370,6 @@ public class RocketChatWebSocketThread extends HandlerThread {
@DebugLog @DebugLog
private void createObserversAndRegister() { private void createObserversAndRegister() {
SessionObserver sessionObserver = new SessionObserver(appContext, hostname, realmHelper);
sessionObserver.register();
listeners.add(sessionObserver);
for (Class clazz : REGISTERABLE_CLASSES) { for (Class clazz : REGISTERABLE_CLASSES) {
try { try {
Constructor ctor = clazz.getConstructor(Context.class, String.class, RealmHelper.class); Constructor ctor = clazz.getConstructor(Context.class, String.class, RealmHelper.class);
...@@ -384,6 +384,10 @@ public class RocketChatWebSocketThread extends HandlerThread { ...@@ -384,6 +384,10 @@ public class RocketChatWebSocketThread extends HandlerThread {
RCLog.w(exception, "Failed to register listeners!!"); RCLog.w(exception, "Failed to register listeners!!");
} }
} }
// Register SessionObserver late.
SessionObserver sessionObserver = new SessionObserver(appContext, hostname, realmHelper);
sessionObserver.register();
listeners.add(sessionObserver);
listenersRegistered = true; listenersRegistered = true;
startHeartBeat(); startHeartBeat();
} }
...@@ -412,9 +416,9 @@ public class RocketChatWebSocketThread extends HandlerThread { ...@@ -412,9 +416,9 @@ public class RocketChatWebSocketThread extends HandlerThread {
} }
@DebugLog @DebugLog
private void unregisterListenersAndClose() { private void unregisterListenersAndClose(int reason) {
unregisterListeners(); unregisterListeners();
DDPClient.get().close(); DDPClient.get().close(reason);
} }
@DebugLog @DebugLog
......
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