Commit 5359f781 authored by Rafael Kellermann Streit's avatar Rafael Kellermann Streit Committed by GitHub

Merge pull request #468 from RocketChat/fix/logout-single-instance

[FIX] Logout from single instance
parents ed4d274b f90b8aeb
......@@ -46,7 +46,7 @@ android {
applicationId "chat.rocket.android"
minSdkVersion 16
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 38
versionCode 39
versionName "1.0.20"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true
......@@ -177,6 +177,8 @@ dependencies {
transitive = true;
}
debugCompile "com.tspoon.traceur:traceur:1.0.1"
provided 'com.parse.bolts:bolts-tasks:1.4.0'
provided 'io.reactivex.rxjava2:rxjava:2.1.0'
provided 'io.reactivex:rxjava:1.3.0'
......
......@@ -2,6 +2,7 @@ package chat.rocket.android;
import android.os.StrictMode;
import com.facebook.stetho.Stetho;
import com.tspoon.traceur.Traceur;
import com.uphyca.stetho_realm.RealmInspectorModulesProvider;
public class RocketChatApplicationDebug extends RocketChatApplication {
......@@ -11,6 +12,11 @@ public class RocketChatApplicationDebug extends RocketChatApplication {
super.onCreate();
enableStrictMode();
enableStetho();
enableTraceur();
}
private void enableTraceur() {
Traceur.enableLogging();
}
private void enableStrictMode() {
......
......@@ -40,7 +40,8 @@
<activity
android:name=".activity.AddServerActivity"
android:configChanges="orientation|screenSize"
android:windowSoftInputMode="adjustResize"/>
android:windowSoftInputMode="adjustResize"
android:launchMode="singleTop"/>
<activity
android:name=".activity.LoginActivity"
......
......@@ -17,7 +17,7 @@ public class LaunchUtil {
*/
public static void showMainActivity(Context context) {
Intent intent = new Intent(context, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT | Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT | Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
context.startActivity(intent);
}
......@@ -26,7 +26,7 @@ public class LaunchUtil {
*/
public static void showAddServerActivity(Context context) {
Intent intent = new Intent(context, AddServerActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT | Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT | Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
context.startActivity(intent);
}
......@@ -35,7 +35,7 @@ public class LaunchUtil {
*/
public static void showLoginActivity(Context context, String hostname) {
Intent intent = new Intent(context, LoginActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT | Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT | Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
intent.putExtra(LoginActivity.KEY_HOSTNAME, hostname);
context.startActivity(intent);
}
......
......@@ -20,6 +20,12 @@ import chat.rocket.persistence.realm.RocketChatPersistenceRealm;
*/
public class RocketChatApplication extends MultiDexApplication {
private static RocketChatApplication instance;
public static RocketChatApplication getInstance() {
return instance;
}
@Override
public void onCreate() {
super.onCreate();
......@@ -37,5 +43,7 @@ public class RocketChatApplication extends MultiDexApplication {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
}
instance = this;
}
}
\ No newline at end of file
......@@ -14,6 +14,8 @@ import java.util.Iterator;
import java.util.List;
import java.util.UUID;
import chat.rocket.android.helper.Logger;
import chat.rocket.android.helper.TextUtils;
import chat.rocket.android.log.RCLog;
import chat.rocket.core.utils.Pair;
import io.reactivex.BackpressureStrategy;
......@@ -41,7 +43,11 @@ public class RocketChatCache {
}
public void setSelectedServerHostname(String hostname) {
setString(KEY_SELECTED_SERVER_HOSTNAME, hostname.toLowerCase());
String newHostname = null;
if (hostname != null) {
newHostname = hostname.toLowerCase();
}
setString(KEY_SELECTED_SERVER_HOSTNAME, newHostname);
}
public void addHostname(@NonNull String hostname, @Nullable String hostnameAvatarUri, String siteName) {
......@@ -54,7 +60,7 @@ public class RocketChatCache {
json = new JSONObject(hostnameList);
}
JSONObject serverInfoJson = new JSONObject();
serverInfoJson.put("hostname", hostnameAvatarUri);
serverInfoJson.put("avatar", hostnameAvatarUri);
serverInfoJson.put("sitename", siteName);
// Replace server avatar uri if exists.
json.put(hostname, hostnameAvatarUri == null ? JSONObject.NULL : serverInfoJson);
......@@ -76,7 +82,7 @@ public class RocketChatCache {
String hostname = iter.next();
JSONObject serverInfoJson = jsonObj.getJSONObject(hostname);
serverList.add(new Pair<>(hostname, new Pair<>(
"http://" + hostname + "/" + serverInfoJson.getString("hostname"),
"http://" + hostname + "/" + serverInfoJson.getString("avatar"),
serverInfoJson.getString("sitename"))));
}
return serverList;
......@@ -86,12 +92,66 @@ public class RocketChatCache {
return Collections.emptyList();
}
public void removeHostname(String hostname) {
String json = getString(KEY_HOSTNAME_LIST, null);
if (TextUtils.isEmpty(json)) {
return;
}
try {
JSONObject jsonObj = new JSONObject(json);
jsonObj.remove(hostname);
String result = jsonObj.length() == 0 ? null : jsonObj.toString();
setString(KEY_HOSTNAME_LIST, result);
} catch (JSONException e) {
RCLog.e(e);
}
}
@Nullable
public String getFirstLoggedHostnameIfAny() {
String json = getString(KEY_HOSTNAME_LIST, null);
if (json != null) {
try {
JSONObject jsonObj = new JSONObject(json);
if (jsonObj.length() > 0 && jsonObj.keys().hasNext()) {
// Returns the first hostname on the list.
return jsonObj.keys().next();
}
} catch (JSONException e) {
RCLog.e(e);
}
}
return null;
}
public String getSelectedRoomId() {
return getString(getSelectedServerHostname() + KEY_SELECTED_ROOM_ID, null);
try {
JSONObject jsonObject = getSelectedRoomIdJsonObject();
return jsonObject.optString(getSelectedServerHostname(), null);
} catch (JSONException e) {
RCLog.e(e);
Logger.report(e);
}
return null;
}
public void setSelectedRoomId(String roomId) {
setString(getSelectedServerHostname() + KEY_SELECTED_ROOM_ID, roomId);
try {
JSONObject jsonObject = getSelectedRoomIdJsonObject();
jsonObject.put(getSelectedServerHostname(), roomId);
setString(KEY_SELECTED_ROOM_ID, jsonObject.toString());
} catch (JSONException e) {
RCLog.e(e);
Logger.report(e);
}
}
private JSONObject getSelectedRoomIdJsonObject() throws JSONException {
String json = getString(KEY_SELECTED_ROOM_ID, null);
if (json == null) {
return new JSONObject();
}
return new JSONObject(json);
}
public String getOrCreatePushId() {
......@@ -112,7 +172,7 @@ public class RocketChatCache {
}
public Flowable<Optional<String>> getSelectedRoomIdPublisher() {
return getValuePublisher(getSelectedServerHostname() + KEY_SELECTED_ROOM_ID);
return getValuePublisher(KEY_SELECTED_ROOM_ID);
}
private SharedPreferences getSharedPreferences() {
......@@ -147,4 +207,17 @@ public class RocketChatCache {
getSharedPreferences().registerOnSharedPreferenceChangeListener(listener);
}, BackpressureStrategy.LATEST);
}
public void removeSelectedRoomId(String currentHostname) {
try {
JSONObject selectedRoomIdJsonObject = getSelectedRoomIdJsonObject();
selectedRoomIdJsonObject.remove(currentHostname);
String result = selectedRoomIdJsonObject.length() == 0 ?
null : selectedRoomIdJsonObject.toString();
setString(KEY_SELECTED_ROOM_ID, result);
} catch (JSONException e) {
Logger.report(e);
RCLog.e(e);
}
}
}
......@@ -6,6 +6,9 @@ import android.support.annotation.Nullable;
import com.hadisatrio.optional.Optional;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.List;
import chat.rocket.android.LaunchUtil;
......@@ -61,6 +64,8 @@ abstract class AbstractAuthedActivity extends AbstractFragmentActivity {
if (intent.hasExtra(PushConstants.ROOM_ID)) {
rocketChatCache.setSelectedRoomId(intent.getStringExtra(PushConstants.ROOM_ID));
}
} else {
updateHostnameIfNeeded(rocketChatCache.getSelectedServerHostname());
}
if (intent.hasExtra(PushConstants.NOT_ID)) {
......@@ -194,7 +199,8 @@ abstract class AbstractAuthedActivity extends AbstractFragmentActivity {
compositeDisposable.add(
rocketChatCache.getSelectedRoomIdPublisher()
.map(Optional::get)
.distinctUntilChanged()
.map(this::convertStringToJsonObject)
.map(jsonObject -> jsonObject.optString(rocketChatCache.getSelectedServerHostname(), null))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
......@@ -203,4 +209,11 @@ abstract class AbstractAuthedActivity extends AbstractFragmentActivity {
)
);
}
private JSONObject convertStringToJsonObject(String json) throws JSONException {
if (json == null) {
return new JSONObject();
}
return new JSONObject(json);
}
}
......@@ -75,10 +75,28 @@ abstract class AbstractFragmentActivity extends RxAppCompatActivity {
.commit();
}
protected void showFragmentWithTagWithBackStack(Fragment fragment, String tag) {
getSupportFragmentManager().beginTransaction()
.replace(getLayoutContainerForFragment(), fragment, tag)
.addToBackStack(null)
.commit();
}
protected void showFragmentWithTag(Fragment fragment, String tag) {
getSupportFragmentManager().beginTransaction()
.replace(getLayoutContainerForFragment(), fragment, tag)
.commit();
}
protected void showFragmentWithBackStack(Fragment fragment) {
getSupportFragmentManager().beginTransaction()
.replace(getLayoutContainerForFragment(), fragment)
.addToBackStack(null)
.commit();
}
@Nullable
protected Fragment findFragmentByTag(String tag) {
return getSupportFragmentManager().findFragmentByTag(tag);
}
}
......@@ -88,7 +88,9 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract
}
private void showAddServerActivity() {
closeSidebarIfNeeded();
Intent intent = new Intent(this, AddServerActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT | Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
intent.putExtra(AddServerActivity.EXTRA_FINISH_ON_BACK_PRESS, true);
startActivity(intent);
}
......@@ -161,13 +163,15 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract
PublicSettingRepository publicSettingRepository = new RealmPublicSettingRepository(hostname);
RocketChatCache rocketChatCache = new RocketChatCache(this);
presenter = new MainPresenter(
roomInteractor,
createRoomInteractor,
sessionInteractor,
new MethodCallHelper(this, hostname),
ConnectivityManager.getInstance(getApplicationContext()),
new RocketChatCache(this),
rocketChatCache,
publicSettingRepository
);
......@@ -175,13 +179,21 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract
presenter.bindView(this);
presenter.loadSignedInServers(hostname);
roomId = rocketChatCache.getSelectedRoomId();
}
private void updateSidebarMainFragment() {
closeSidebarIfNeeded();
String selectedServerHostname = new RocketChatCache(this).getSelectedServerHostname();
Fragment sidebarFragment = findFragmentByTag(selectedServerHostname);
if (sidebarFragment == null) {
sidebarFragment = SidebarMainFragment.create(selectedServerHostname);
}
getSupportFragmentManager().beginTransaction()
.replace(R.id.sidebar_fragment_container, SidebarMainFragment.create(hostname))
.replace(R.id.sidebar_fragment_container, sidebarFragment, selectedServerHostname)
.commit();
getSupportFragmentManager().executePendingTransactions();
}
@Override
......@@ -239,6 +251,12 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract
R.string.server_config_activity_authenticating, Snackbar.LENGTH_INDEFINITE));
}
public void showLogoutMessage() {
statusTicker.updateStatus(StatusTicker.STATUS_LOGGING_OUT,
Snackbar.make(findViewById(getLayoutContainerForFragment()),
"Logging Out...", Snackbar.LENGTH_INDEFINITE));
}
@Override
public void showConnectionOk() {
statusTicker.updateStatus(StatusTicker.STATUS_DISMISS, null);
......@@ -251,39 +269,41 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract
LinearLayout serverListContainer = subPane.findViewById(R.id.server_list_bar);
View addServerButton = subPane.findViewById(R.id.btn_add_server);
addServerButton.setOnClickListener(view -> showAddServerActivity());
serverListContainer.removeAllViews();
for (Pair<String, Pair<String, String>> server : serverList) {
String serverHostname = server.first;
Pair<String, String> serverInfoPair = server.second;
String logoUrl = serverInfoPair.first;
String siteName = serverInfoPair.second;
if (serverListContainer.findViewWithTag(serverHostname) == null) {
int serverCount = serverListContainer.getChildCount();
View serverRow = LayoutInflater.from(this).inflate(R.layout.server_row, serverListContainer, false);
SimpleDraweeView serverButton = serverRow.findViewById(R.id.drawee_server_button);
TextView hostnameLabel = serverRow.findViewById(R.id.text_view_server_label);
TextView siteNameLabel = serverRow.findViewById(R.id.text_view_site_name_label);
ImageView dotView = serverRow.findViewById(R.id.selected_server_dot);
serverButton.setTag(serverHostname);
View serverView = serverListContainer.findViewWithTag(serverHostname);
if (serverView == null) {
View newServerView = LayoutInflater.from(this).inflate(R.layout.server_row, serverListContainer, false);
SimpleDraweeView serverButton = newServerView.findViewById(R.id.drawee_server_button);
TextView hostnameLabel = newServerView.findViewById(R.id.text_view_server_label);
TextView siteNameLabel = newServerView.findViewById(R.id.text_view_site_name_label);
ImageView dotView = newServerView.findViewById(R.id.selected_server_dot);
newServerView.setTag(serverHostname);
hostnameLabel.setText(serverHostname);
siteNameLabel.setText(siteName);
// Currently selected server
if (serverHostname.equalsIgnoreCase(hostname)) {
serverRow.setSelected(true);
if (hostname.equalsIgnoreCase(serverHostname)) {
newServerView.setSelected(true);
dotView.setVisibility(View.VISIBLE);
} else {
newServerView.setSelected(false);
dotView.setVisibility(View.GONE);
}
serverRow.setOnClickListener(view -> changeServerIfNeeded(serverHostname));
newServerView.setOnClickListener(view -> changeServerIfNeeded(serverHostname));
FrescoHelper.INSTANCE.loadImage(serverButton, logoUrl, ContextCompat.getDrawable(this, R.mipmap.ic_launcher));
serverListContainer.addView(serverRow, serverCount - 1);
serverListContainer.addView(newServerView);
}
}
serverListContainer.addView(addServerButton);
}
}
......@@ -291,15 +311,34 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract
if (!hostname.equalsIgnoreCase(serverHostname)) {
RocketChatCache rocketChatCache = new RocketChatCache(getApplicationContext());
rocketChatCache.setSelectedServerHostname(serverHostname);
recreate();
}
}
@DebugLog
public void hideLogoutMessage() {
statusTicker.updateStatus(StatusTicker.STATUS_DISMISS, null);
}
@DebugLog
public void onLogout() {
if (new RocketChatCache(getApplicationContext()).getSelectedServerHostname() == null) {
LaunchUtil.showMainActivity(this);
} else {
onHostnameUpdated();
}
}
@DebugLog
public void beforeLogoutCleanUp() {
presenter.beforeLogoutCleanUp();
}
//TODO: consider this class to define in layouthelper for more complicated operation.
private static class StatusTicker {
public static final int STATUS_DISMISS = 0;
public static final int STATUS_CONNECTION_ERROR = 1;
public static final int STATUS_TOKEN_LOGIN = 2;
public static final int STATUS_LOGGING_OUT = 3;
private int status;
private Snackbar snackbar;
......
......@@ -37,5 +37,7 @@ public interface MainContract {
void bindViewOnly(View view);
void loadSignedInServers(String hostname);
void beforeLogoutCleanUp();
}
}
......@@ -133,6 +133,11 @@ public class MainPresenter extends BasePresenter<MainContract.View>
addSubscription(subscription);
}
@Override
public void beforeLogoutCleanUp() {
clearSubscriptions();
}
private Pair<String, String> getLogoAndSiteNamePair(Pair<Optional<PublicSetting>, Optional<PublicSetting>> settingsPair) {
String logoUrl = "";
String siteName = "";
......
......@@ -2,7 +2,7 @@ package chat.rocket.android.api;
import android.content.Context;
import android.util.Patterns;
import chat.rocket.persistence.realm.models.ddp.RealmSpotlight;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
......@@ -17,7 +17,6 @@ import chat.rocket.android.service.ConnectivityManager;
import chat.rocket.android.service.DDPClientRef;
import chat.rocket.android_ddp.DDPClientCallback;
import chat.rocket.core.SyncState;
import chat.rocket.core.models.ServerInfo;
import chat.rocket.persistence.realm.RealmHelper;
import chat.rocket.persistence.realm.RealmStore;
import chat.rocket.persistence.realm.models.ddp.RealmMessage;
......@@ -25,6 +24,7 @@ import chat.rocket.persistence.realm.models.ddp.RealmPermission;
import chat.rocket.persistence.realm.models.ddp.RealmPublicSetting;
import chat.rocket.persistence.realm.models.ddp.RealmRoom;
import chat.rocket.persistence.realm.models.ddp.RealmRoomRole;
import chat.rocket.persistence.realm.models.ddp.RealmSpotlight;
import chat.rocket.persistence.realm.models.ddp.RealmSpotlightRoom;
import chat.rocket.persistence.realm.models.ddp.RealmSpotlightUser;
import chat.rocket.persistence.realm.models.internal.MethodCall;
......@@ -260,18 +260,12 @@ public class MethodCallHelper {
* Logout.
*/
public Task<Void> logout() {
return call("logout", TIMEOUT_MS).onSuccessTask(task ->
realmHelper.executeTransaction(realm -> {
realm.delete(RealmSession.class);
//check whether the server list is empty
if (!ConnectivityManager.getInstance(context).getServerList().isEmpty()){
//for each server in serverList -> remove the server
for (ServerInfo server: ConnectivityManager.getInstance(context).getServerList()) {
ConnectivityManager.getInstance(context.getApplicationContext()).removeServer(server.getHostname());
}
return call("logout", TIMEOUT_MS).onSuccessTask(task -> {
if (task.isFaulted()) {
return Task.forError(task.getError());
}
return null;
}));
});
}
/**
......
package chat.rocket.android.fragment.add_server;
import chat.rocket.android.BackgroundLooper;
import chat.rocket.android.helper.Logger;
import chat.rocket.android.helper.OkHttpHelper;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
......@@ -46,7 +47,10 @@ public class InputHostnamePresenter extends BasePresenter<InputHostnameContract.
view.showInvalidServerError();
}
},
throwable -> view.showConnectionError());
throwable -> {
Logger.report(throwable);
view.showConnectionError();
});
addSubscription(subscription);
}
......
......@@ -22,6 +22,7 @@ abstract class AbstractChatRoomFragment extends AbstractFragment {
}
protected void setToolbarTitle(CharSequence title) {
roomToolbar.hideChannelIcons();
roomToolbar.setTitle(title);
}
......
......@@ -2,6 +2,7 @@ package chat.rocket.android.fragment.sidebar;
import android.support.annotation.NonNull;
import bolts.Continuation;
import chat.rocket.core.models.RoomSidebar;
import io.reactivex.Flowable;
import java.util.List;
......@@ -22,6 +23,8 @@ public interface SidebarMainContract {
void filterRoomSidebarList(CharSequence term);
void show(User user);
void onLogoutCleanUp();
}
interface Presenter extends BaseContract.Presenter<View> {
......@@ -42,6 +45,8 @@ public interface SidebarMainContract {
void onUserOffline();
void onLogout();
void onLogout(Continuation<Void, Object> continuation);
void beforeLogoutCleanUp();
}
}
\ No newline at end of file
package chat.rocket.android.fragment.sidebar;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
......@@ -8,13 +9,23 @@ import android.support.v4.app.DialogFragment;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.SearchView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CompoundButton;
import android.widget.TextView;
import com.jakewharton.rxbinding2.support.v7.widget.RxSearchView;
import com.jakewharton.rxbinding2.widget.RxCompoundButton;
import java.util.ArrayList;
import java.util.List;
import bolts.Task;
import chat.rocket.android.BuildConfig;
import chat.rocket.android.R;
import chat.rocket.android.RocketChatCache;
import chat.rocket.android.activity.MainActivity;
import chat.rocket.android.api.MethodCallHelper;
import chat.rocket.android.fragment.AbstractFragment;
import chat.rocket.android.fragment.sidebar.dialog.AddChannelDialogFragment;
......@@ -39,15 +50,9 @@ import chat.rocket.persistence.realm.repositories.RealmServerInfoRepository;
import chat.rocket.persistence.realm.repositories.RealmSessionRepository;
import chat.rocket.persistence.realm.repositories.RealmSpotlightRepository;
import chat.rocket.persistence.realm.repositories.RealmUserRepository;
import com.jakewharton.rxbinding2.support.v7.widget.RxSearchView;
import com.jakewharton.rxbinding2.widget.RxCompoundButton;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import java.util.ArrayList;
import java.util.List;
public class SidebarMainFragment extends AbstractFragment implements SidebarMainContract.View {
private SidebarMainContract.Presenter presenter;
private RoomListAdapter adapter;
......@@ -87,26 +92,42 @@ public class SidebarMainFragment extends AbstractFragment implements SidebarMain
new SessionInteractor(new RealmSessionRepository(hostname))
);
RocketChatCache rocketChatCache = new RocketChatCache(getContext().getApplicationContext());
presenter = new SidebarMainPresenter(
hostname,
new RoomInteractor(new RealmRoomRepository(hostname)),
userRepository,
new RocketChatCache(getContext()),
rocketChatCache,
absoluteUrlHelper,
new MethodCallHelper(getContext(), hostname),
new RealmSpotlightRepository(hostname)
);
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = super.onCreateView(inflater, container, savedInstanceState);
presenter.bindView(this);
return view;
}
@Override
public void onDestroyView() {
presenter.release();
super.onDestroyView();
}
@Override
public void onResume() {
super.onResume();
presenter.bindView(this);
}
@Override
public void onPause() {
presenter.release();
super.onPause();
}
......@@ -300,12 +321,32 @@ public class SidebarMainFragment extends AbstractFragment implements SidebarMain
adapter.setRoomListHeaders(roomListHeaders);
}
@Override
public void onLogoutCleanUp() {
Activity activity = getActivity();
if (activity != null && activity instanceof MainActivity) {
((MainActivity) activity).hideLogoutMessage();
((MainActivity) activity).onLogout();
presenter.onLogout(task -> {
if (task.isFaulted()) {
return Task.forError(task.getError());
}
return null;
});
}
}
private void setupLogoutButton() {
rootView.findViewById(R.id.btn_logout).setOnClickListener(view -> {
presenter.onLogout();
closeUserActionContainer();
// destroy Activity on logout to be able to recreate most of the environment
this.getActivity().finish();
// Clear relative data and set new hostname if any.
presenter.beforeLogoutCleanUp();
final Activity activity = getActivity();
if (activity != null && activity instanceof MainActivity) {
((MainActivity) activity).showLogoutMessage();
// Clear subscriptions on MainPresenter.
((MainActivity) activity).beforeLogoutCleanUp();
}
});
}
......
......@@ -6,13 +6,18 @@ import android.support.v4.util.Pair;
import java.util.ArrayList;
import java.util.List;
import bolts.Continuation;
import bolts.Task;
import chat.rocket.android.BackgroundLooper;
import chat.rocket.android.RocketChatApplication;
import chat.rocket.android.RocketChatCache;
import chat.rocket.android.api.MethodCallHelper;
import chat.rocket.android.helper.AbsoluteUrlHelper;
import chat.rocket.android.helper.LogIfError;
import chat.rocket.android.helper.Logger;
import chat.rocket.android.helper.TextUtils;
import chat.rocket.android.service.ConnectivityManager;
import chat.rocket.android.service.ConnectivityManagerApi;
import chat.rocket.android.shared.BasePresenter;
import chat.rocket.core.interactors.RoomInteractor;
import chat.rocket.core.models.Room;
......@@ -21,6 +26,8 @@ import chat.rocket.core.models.Spotlight;
import chat.rocket.core.models.User;
import chat.rocket.core.repositories.SpotlightRepository;
import chat.rocket.core.repositories.UserRepository;
import chat.rocket.persistence.realm.RealmHelper;
import chat.rocket.persistence.realm.RealmStore;
import chat.rocket.persistence.realm.repositories.RealmSpotlightRepository;
import io.reactivex.Flowable;
import io.reactivex.android.schedulers.AndroidSchedulers;
......@@ -131,8 +138,31 @@ public class SidebarMainPresenter extends BasePresenter<SidebarMainContract.View
}
@Override
public void onLogout() {
methodCallHelper.logout().continueWith(new LogIfError());
public void onLogout(Continuation<Void, Object> continuation) {
methodCallHelper.logout().continueWith(task -> {
if (task.isFaulted()) {
Logger.report(task.getError());
return Task.forError(task.getError());
}
return task.onSuccess(continuation);
});
}
@Override
public void beforeLogoutCleanUp() {
clearSubscriptions();
String currentHostname = rocketChatCache.getSelectedServerHostname();
RealmHelper realmHelper = RealmStore.getOrCreate(currentHostname);
realmHelper.executeTransaction(realm -> {
realm.deleteAll();
ConnectivityManagerApi connectivityManagerApi = ConnectivityManager.getInstance(RocketChatApplication.getInstance());
connectivityManagerApi.removeServer(currentHostname);
rocketChatCache.removeHostname(currentHostname);
rocketChatCache.removeSelectedRoomId(currentHostname);
rocketChatCache.setSelectedServerHostname(rocketChatCache.getFirstLoggedHostnameIfAny());
view.onLogoutCleanUp();
return null;
});
}
@Override
......
......@@ -7,10 +7,11 @@ import android.os.IBinder;
import android.support.annotation.Nullable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import chat.rocket.android.helper.RxHelper;
import chat.rocket.android.log.RCLog;
import chat.rocket.core.models.ServerInfo;
......@@ -25,7 +26,7 @@ import rx.subjects.PublishSubject;
*/
/*package*/ class RealmBasedConnectivityManager
implements ConnectivityManagerApi, ConnectivityManagerInternal {
private final HashMap<String, Integer> serverConnectivityList = new HashMap<>();
private final ConcurrentHashMap<String, Integer> serverConnectivityList = new ConcurrentHashMap<>();
private final PublishSubject<ServerConnectivity> connectivitySubject = PublishSubject.create();
private Context appContext;
private final ServiceConnection serviceConnection = new ServiceConnection() {
......
......@@ -12,7 +12,6 @@ import java.util.HashMap;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import chat.rocket.android.activity.MainActivity;
import chat.rocket.persistence.realm.RealmStore;
import hugo.weaving.DebugLog;
import rx.Observable;
......@@ -93,12 +92,6 @@ public class RocketChatService extends Service implements ConnectivityServiceInt
webSocketThreads.remove(hostname);
// remove RealmConfiguration key from HashMap
RealmStore.sStore.remove(hostname);
// clear "cache" SharedPreference
this.getSharedPreferences("cache", 0).edit().clear().apply();
// start a fresh new MainActivity
Intent intent = new Intent(this, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
this.startActivity(intent);
});
} else {
return Observable.timer(1, TimeUnit.SECONDS).toSingle()
......
......@@ -255,6 +255,10 @@ public class RocketChatWebSocketThread extends HandlerThread {
return prepareDDPClient()
.flatMap(_val -> Single.fromEmitter(emitter -> {
ServerInfo info = connectivityManager.getServerInfoForHost(hostname);
if (info == null) {
emitter.onSuccess(false);
return;
}
RCLog.d("DDPClient#connect");
ddpClient.connect(info.getSession(), info.isSecure())
.onSuccessTask(task -> {
......
......@@ -29,11 +29,17 @@ public class RealmLoginServiceConfigurationRepository extends RealmRepository
public Single<Optional<LoginServiceConfiguration>> getByName(String serviceName) {
return Single.defer(() -> Flowable.using(
() -> new Pair<>(RealmStore.getRealm(hostname), Looper.myLooper()),
pair -> RxJavaInterop.toV2Flowable(
pair -> {
if (pair.first == null) {
return Flowable.empty();
}
return RxJavaInterop.toV2Flowable(
pair.first.where(RealmMeteorLoginServiceConfiguration.class)
.equalTo(RealmMeteorLoginServiceConfiguration.SERVICE, serviceName)
.findAll()
.<RealmResults<RealmMeteorLoginServiceConfiguration>>asObservable()),
.<RealmResults<RealmMeteorLoginServiceConfiguration>>asObservable());
},
pair -> close(pair.first, pair.second)
)
.unsubscribeOn(AndroidSchedulers.from(Looper.myLooper()))
......@@ -46,10 +52,16 @@ public class RealmLoginServiceConfigurationRepository extends RealmRepository
public Flowable<List<LoginServiceConfiguration>> getAll() {
return Flowable.defer(() -> Flowable.using(
() -> new Pair<>(RealmStore.getRealm(hostname), Looper.myLooper()),
pair -> RxJavaInterop
pair -> {
if (pair.first == null) {
return Flowable.empty();
}
return RxJavaInterop
.toV2Flowable(pair.first.where(RealmMeteorLoginServiceConfiguration.class)
.findAll()
.asObservable()),
.asObservable());
},
pair -> close(pair.first, pair.second)
)
.unsubscribeOn(AndroidSchedulers.from(Looper.myLooper()))
......
......@@ -36,11 +36,17 @@ public class RealmMessageRepository extends RealmRepository implements MessageRe
public Single<Optional<Message>> getById(String messageId) {
return Single.defer(() -> Flowable.using(
() -> new Pair<>(RealmStore.getRealm(hostname), Looper.myLooper()),
pair -> RxJavaInterop.toV2Flowable(
pair -> {
if (pair.first == null) {
return Flowable.empty();
}
return RxJavaInterop.toV2Flowable(
pair.first.where(RealmMessage.class)
.equalTo(RealmMessage.ID, messageId)
.findAll()
.<RealmResults<RealmMessage>>asObservable()),
.<RealmResults<RealmMessage>>asObservable());
},
pair -> close(pair.first, pair.second)
)
.unsubscribeOn(AndroidSchedulers.from(Looper.myLooper()))
......@@ -133,11 +139,17 @@ public class RealmMessageRepository extends RealmRepository implements MessageRe
public Flowable<List<Message>> getAllFrom(Room room) {
return Flowable.defer(() -> Flowable.using(
() -> new Pair<>(RealmStore.getRealm(hostname), Looper.myLooper()),
pair -> RxJavaInterop.toV2Flowable(pair.first.where(RealmMessage.class)
pair -> {
if (pair.first == null) {
return Flowable.empty();
}
return RxJavaInterop.toV2Flowable(pair.first.where(RealmMessage.class)
.equalTo(RealmMessage.ROOM_ID, room.getRoomId())
.isNotNull(RealmMessage.USER)
.findAllSorted(RealmMessage.TIMESTAMP, Sort.DESCENDING)
.asObservable()),
.asObservable());
},
pair -> close(pair.first, pair.second)
)
.unsubscribeOn(AndroidSchedulers.from(Looper.myLooper()))
......@@ -150,12 +162,18 @@ public class RealmMessageRepository extends RealmRepository implements MessageRe
public Single<Integer> unreadCountFor(Room room, User user) {
return Single.defer(() -> Flowable.using(
() -> new Pair<>(RealmStore.getRealm(hostname), Looper.myLooper()),
pair -> RxJavaInterop.toV2Flowable(pair.first.where(RealmMessage.class)
pair -> {
if (pair.first == null) {
return Flowable.empty();
}
return RxJavaInterop.toV2Flowable(pair.first.where(RealmMessage.class)
.equalTo(RealmMessage.ROOM_ID, room.getId())
.greaterThanOrEqualTo(RealmMessage.TIMESTAMP, room.getLastSeen())
.notEqualTo(RealmMessage.USER_ID, user.getId())
.findAll()
.asObservable()),
.asObservable());
},
pair -> close(pair.first, pair.second)
)
.unsubscribeOn(AndroidSchedulers.from(Looper.myLooper()))
......
......@@ -26,11 +26,16 @@ public class RealmPermissionRepository extends RealmRepository implements Permis
public Single<Optional<Permission>> getById(String id) {
return Single.defer(() -> Flowable.using(
() -> new Pair<>(RealmStore.getRealm(hostname), Looper.myLooper()),
pair -> RxJavaInterop.toV2Flowable(
pair -> {
if (pair.first == null) {
return Flowable.empty();
}
return RxJavaInterop.toV2Flowable(
pair.first.where(RealmPermission.class)
.equalTo(RealmPermission.Columns.ID, id)
.findAll()
.<RealmResults<RealmPermission>>asObservable()),
.<RealmResults<RealmPermission>>asObservable());
},
pair -> close(pair.first, pair.second)
)
.unsubscribeOn(AndroidSchedulers.from(Looper.myLooper()))
......
......@@ -27,11 +27,16 @@ public class RealmPublicSettingRepository extends RealmRepository
public Single<Optional<PublicSetting>> getById(String id) {
return Single.defer(() -> Flowable.using(
() -> new Pair<>(RealmStore.getRealm(hostname), Looper.myLooper()),
pair -> RxJavaInterop.toV2Flowable(
pair -> {
if (pair.first == null) {
return Flowable.empty();
}
return RxJavaInterop.toV2Flowable(
pair.first.where(RealmPublicSetting.class)
.equalTo(RealmPublicSetting.ID, id)
.findAll()
.<RealmResults<RealmPublicSetting>>asObservable()),
.<RealmResults<RealmPublicSetting>>asObservable());
},
pair -> close(pair.first, pair.second)
)
.unsubscribeOn(AndroidSchedulers.from(Looper.myLooper()))
......
......@@ -34,10 +34,16 @@ public class RealmRoomRepository extends RealmRepository implements RoomReposito
public Flowable<List<Room>> getAll() {
return Flowable.defer(() -> Flowable.using(
() -> new Pair<>(RealmStore.getRealm(hostname), Looper.myLooper()),
pair -> RxJavaInterop.toV2Flowable(
pair -> {
if (pair.first == null) {
return Flowable.empty();
}
return RxJavaInterop.toV2Flowable(
pair.first.where(RealmRoom.class)
.findAll()
.asObservable()),
.asObservable());
},
pair -> close(pair.first, pair.second)
)
.unsubscribeOn(AndroidSchedulers.from(Looper.myLooper()))
......@@ -51,6 +57,10 @@ public class RealmRoomRepository extends RealmRepository implements RoomReposito
return Flowable.defer(() -> Flowable.using(
() -> new Pair<>(RealmStore.getRealm(hostname), Looper.myLooper()),
pair -> {
if (pair.first == null) {
return Flowable.empty();
}
RealmRoom realmRoom = pair.first.where(RealmRoom.class)
.equalTo(RealmRoom.ROOM_ID, roomId)
.findFirst();
......@@ -84,6 +94,9 @@ public class RealmRoomRepository extends RealmRepository implements RoomReposito
return Flowable.defer(() -> Flowable.using(
() -> new Pair<>(RealmStore.getRealm(hostname), Looper.myLooper()),
pair -> {
if (pair.first == null) {
return Flowable.empty();
}
LoadMessageProcedure messageProcedure = pair.first.where(LoadMessageProcedure.class)
.equalTo(LoadMessageProcedure.ID, roomId)
......@@ -147,7 +160,11 @@ public class RealmRoomRepository extends RealmRepository implements RoomReposito
public Flowable<List<Room>> getSortedLikeName(String name, SortDirection direction, int limit) {
return Flowable.defer(() -> Flowable.using(
() -> new Pair<>(RealmStore.getRealm(hostname), Looper.myLooper()),
pair -> RxJavaInterop.toV2Flowable(
pair -> {
if (pair.first == null) {
return Flowable.empty();
}
return RxJavaInterop.toV2Flowable(
pair.first.where(RealmRoom.class)
.like(RealmRoom.NAME, "*" + name + "*", Case.INSENSITIVE)
.beginGroup()
......@@ -157,7 +174,8 @@ public class RealmRoomRepository extends RealmRepository implements RoomReposito
.endGroup()
.findAllSorted(RealmRoom.NAME,
direction.equals(SortDirection.ASC) ? Sort.ASCENDING : Sort.DESCENDING)
.asObservable()),
.asObservable());
},
pair -> close(pair.first, pair.second)
)
.unsubscribeOn(AndroidSchedulers.from(Looper.myLooper()))
......@@ -170,7 +188,11 @@ public class RealmRoomRepository extends RealmRepository implements RoomReposito
public Flowable<List<Room>> getLatestSeen(int limit) {
return Flowable.defer(() -> Flowable.using(
() -> new Pair<>(RealmStore.getRealm(hostname), Looper.myLooper()),
pair -> RxJavaInterop.toV2Flowable(
pair -> {
if (pair.first == null) {
return Flowable.empty();
}
return RxJavaInterop.toV2Flowable(
pair.first.where(RealmRoom.class)
.beginGroup()
.equalTo(RealmRoom.TYPE, RealmRoom.TYPE_CHANNEL)
......@@ -178,7 +200,8 @@ public class RealmRoomRepository extends RealmRepository implements RoomReposito
.equalTo(RealmRoom.TYPE, RealmRoom.TYPE_PRIVATE)
.endGroup()
.findAllSorted(RealmRoom.LAST_SEEN, Sort.ASCENDING)
.asObservable()),
.asObservable());
},
pair -> close(pair.first, pair.second)
)
.unsubscribeOn(AndroidSchedulers.from(Looper.myLooper()))
......
......@@ -29,12 +29,17 @@ public class RealmRoomRoleRepository extends RealmRepository implements RoomRole
public Single<Optional<RoomRole>> getFor(Room room, User user) {
return Single.defer(() -> Flowable.using(
() -> new Pair<>(RealmStore.getRealm(hostname), Looper.myLooper()),
pair -> RxJavaInterop.toV2Flowable(
pair -> {
if (pair.first == null) {
return Flowable.empty();
}
return RxJavaInterop.toV2Flowable(
pair.first.where(RealmRoomRole.class)
.equalTo(RealmRoomRole.Columns.ROOM_ID, room.getId())
.equalTo(RealmRoomRole.Columns.USER + "." + RealmUser.ID, user.getId())
.findAll()
.<RealmResults<RealmRoomRole>>asObservable()),
.<RealmResults<RealmRoomRole>>asObservable());
},
pair -> close(pair.first, pair.second)
)
.unsubscribeOn(AndroidSchedulers.from(Looper.myLooper()))
......
......@@ -26,11 +26,17 @@ public class RealmSessionRepository extends RealmRepository implements SessionRe
public Flowable<Optional<Session>> getById(int id) {
return Flowable.defer(() -> Flowable.using(
() -> new Pair<>(RealmStore.getRealm(hostname), Looper.myLooper()),
pair -> RxJavaInterop.toV2Flowable(
pair -> {
if (pair.first == null) {
return Flowable.empty();
}
return RxJavaInterop.toV2Flowable(
pair.first.where(RealmSession.class)
.equalTo(RealmSession.ID, id)
.findAll()
.<RealmSession>asObservable()),
.<RealmSession>asObservable());
},
pair -> close(pair.first, pair.second)
)
.unsubscribeOn(AndroidSchedulers.from(Looper.myLooper()))
......
......@@ -20,7 +20,12 @@ class RealmSpotlightRepository(private val hostname: String) : RealmRepository()
override fun getSuggestionsFor(term: String, limit: Int): Flowable<List<Spotlight>> {
return Flowable.defer { Flowable.using<RealmResults<RealmSpotlight>, Pair<Realm, Looper>>({
Pair(RealmStore.getRealm(hostname), Looper.myLooper())
}, { pair -> RxJavaInterop.toV2Flowable<RealmResults<RealmSpotlight>>(pair.first.where(RealmSpotlight::class.java)
}, { pair ->
if (pair.first == null) {
return@using Flowable.empty()
}
return@using RxJavaInterop.toV2Flowable<RealmResults<RealmSpotlight>>(pair.first.where(RealmSpotlight::class.java)
.findAllSorted(Columns.TYPE, Sort.DESCENDING)
.asObservable())
}) { pair -> close(pair.first, pair.second) }
......
......@@ -29,7 +29,12 @@ public class RealmSpotlightRoomRepository extends RealmRepository implements Spo
public Flowable<List<SpotlightRoom>> getSuggestionsFor(String name, SortDirection direction, int limit) {
return Flowable.defer(() -> Flowable.using(
() -> new Pair<>(RealmStore.getRealm(hostname), Looper.myLooper()),
pair -> RxJavaInterop.toV2Flowable(
pair -> {
if (pair.first == null) {
return Flowable.empty();
}
return RxJavaInterop.toV2Flowable(
pair.first.where(RealmSpotlightRoom.class)
.like(RealmSpotlightRoom.Columns.NAME, "*" + name + "*", Case.INSENSITIVE)
.beginGroup()
......@@ -38,7 +43,8 @@ public class RealmSpotlightRoomRepository extends RealmRepository implements Spo
.equalTo(RealmSpotlightRoom.Columns.TYPE, RealmRoom.TYPE_PRIVATE)
.endGroup()
.findAllSorted(RealmSpotlightRoom.Columns.NAME, direction.equals(SortDirection.ASC) ? Sort.ASCENDING : Sort.DESCENDING)
.asObservable()),
.asObservable());
},
pair -> close(pair.first, pair.second)
)
.unsubscribeOn(AndroidSchedulers.from(Looper.myLooper()))
......
......@@ -28,7 +28,12 @@ public class RealmSpotlightUserRepository extends RealmRepository implements Spo
public Flowable<List<SpotlightUser>> getSuggestionsFor(String name, SortDirection direction, int limit) {
return Flowable.defer(() -> Flowable.using(
() -> new Pair<>(RealmStore.getRealm(hostname), Looper.myLooper()),
pair -> RxJavaInterop.toV2Flowable(
pair -> {
if (pair.first == null) {
return Flowable.empty();
}
return RxJavaInterop.toV2Flowable(
pair.first.where(RealmSpotlightUser.class)
.beginGroup()
.like(RealmSpotlightUser.Columns.USERNAME, "*" + name + "*", Case.INSENSITIVE)
......@@ -41,7 +46,8 @@ public class RealmSpotlightUserRepository extends RealmRepository implements Spo
.endGroup()
.findAllSorted(RealmSpotlightUser.Columns.USERNAME,
direction.equals(SortDirection.ASC) ? Sort.ASCENDING : Sort.DESCENDING)
.asObservable()),
.asObservable());
},
pair -> close(pair.first, pair.second)
)
.unsubscribeOn(AndroidSchedulers.from(Looper.myLooper()))
......
......@@ -31,10 +31,16 @@ public class RealmUserRepository extends RealmRepository implements UserReposito
public Flowable<List<User>> getAll() {
return Flowable.defer(() -> Flowable.using(
() -> new Pair<>(RealmStore.getRealm(hostname), Looper.myLooper()),
pair -> RxJavaInterop.toV2Flowable(
pair -> {
if (pair.first == null) {
return Flowable.empty();
}
return RxJavaInterop.toV2Flowable(
pair.first.where(RealmUser.class)
.findAll()
.asObservable()),
.asObservable());
},
pair -> close(pair.first, pair.second))
.unsubscribeOn(AndroidSchedulers.from(Looper.myLooper()))
.filter(roomSubscriptions -> roomSubscriptions != null && roomSubscriptions.isLoaded()
......@@ -60,11 +66,17 @@ public class RealmUserRepository extends RealmRepository implements UserReposito
private Flowable<RealmResults<RealmUser>> realmGetCurrent() {
return Flowable.using(
() -> new Pair<>(RealmStore.getRealm(hostname), Looper.myLooper()),
pair -> RxJavaInterop.toV2Flowable(
pair -> {
if (pair.first == null) {
return Flowable.empty();
}
return RxJavaInterop.toV2Flowable(
pair.first.where(RealmUser.class)
.isNotEmpty(RealmUser.EMAILS)
.findAll()
.<RealmResults<RealmUser>>asObservable()),
.<RealmResults<RealmUser>>asObservable());
},
pair -> close(pair.first, pair.second));
}
......@@ -90,6 +102,10 @@ public class RealmUserRepository extends RealmRepository implements UserReposito
}
private Flowable<Optional<RealmUser>> realmQueryUsername(Realm realm, String username) {
if (realm == null) {
return Flowable.empty();
}
RealmUser realmUser = realm.where(RealmUser.class)
.equalTo(RealmUser.USERNAME, username)
.findFirst();
......@@ -118,11 +134,17 @@ public class RealmUserRepository extends RealmRepository implements UserReposito
private Flowable<RealmResults<RealmUser>> realmGetSortedLikeName(String name) {
return Flowable.using(
() -> new Pair<>(RealmStore.getRealm(hostname), Looper.myLooper()),
pair -> RxJavaInterop.toV2Flowable(
pair -> {
if (pair.first == null) {
return Flowable.empty();
}
return RxJavaInterop.toV2Flowable(
pair.first.where(RealmUser.class)
.like(RealmUser.USERNAME, "*" + name + "*", Case.INSENSITIVE)
.findAllSorted(RealmUser.USERNAME, Sort.DESCENDING)
.asObservable()),
.asObservable());
},
pair -> close(pair.first, pair.second));
}
......
......@@ -92,6 +92,11 @@ public class RoomToolbar extends Toolbar {
toolbarText.setText(title);
}
public void hideChannelIcons() {
roomTypeImage.setVisibility(GONE);
userStatusImage.setVisibility(GONE);
}
public void showPrivateChannelIcon() {
roomTypeImage.setImageDrawable(privateChannelDrawable);
userStatusImage.setVisibility(GONE);
......
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