Commit 8cd2c74d authored by Tiago Cunha's avatar Tiago Cunha

Unrealm app the app

Unfortunately there's some push related work here
parent 399dce09
...@@ -166,6 +166,21 @@ public class MethodCallHelper { ...@@ -166,6 +166,21 @@ public class MethodCallHelper {
.onSuccessTask(this::saveToken); .onSuccessTask(this::saveToken);
} }
public Task<Void> loginWithLdap(final String username, final String password) {
return call("login", TIMEOUT_MS, () -> {
JSONObject param = new JSONObject();
param.put("ldap", true);
param.put("username", username);
param.put("ldapPass", password);
param.put("ldapOptions", new JSONObject());
return new JSONArray().put(param);
}).onSuccessTask(CONVERT_TO_JSON_OBJECT)
.onSuccessTask(task -> Task.forResult(task.getResult().getString("token")))
.onSuccessTask(this::saveToken);
}
/** /**
* Login with OAuth. * Login with OAuth.
*/ */
......
...@@ -16,6 +16,7 @@ import hugo.weaving.DebugLog; ...@@ -16,6 +16,7 @@ import hugo.weaving.DebugLog;
public abstract class AbstractWebViewFragment extends AbstractFragment public abstract class AbstractWebViewFragment extends AbstractFragment
implements OnBackPressListener { implements OnBackPressListener {
private boolean isSet = false;
private WebView webview; private WebView webview;
private WebViewClient webviewClient = new WebViewClient() { private WebViewClient webviewClient = new WebViewClient() {
private boolean error; private boolean error;
...@@ -66,6 +67,10 @@ public abstract class AbstractWebViewFragment extends AbstractFragment ...@@ -66,6 +67,10 @@ public abstract class AbstractWebViewFragment extends AbstractFragment
} }
private void setupWebView() { private void setupWebView() {
if (isSet) {
return;
}
WebSettings settings = webview.getSettings(); WebSettings settings = webview.getSettings();
if (settings != null) { if (settings != null) {
settings.setJavaScriptEnabled(true); settings.setJavaScriptEnabled(true);
...@@ -80,6 +85,8 @@ public abstract class AbstractWebViewFragment extends AbstractFragment ...@@ -80,6 +85,8 @@ public abstract class AbstractWebViewFragment extends AbstractFragment
//refs: https://code.google.com/p/android/issues/detail?id=35288 //refs: https://code.google.com/p/android/issues/detail?id=35288
webview.setLayerType(View.LAYER_TYPE_SOFTWARE, null); webview.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
} }
isSet = true;
} }
@Override @Override
...@@ -92,6 +99,15 @@ public abstract class AbstractWebViewFragment extends AbstractFragment ...@@ -92,6 +99,15 @@ public abstract class AbstractWebViewFragment extends AbstractFragment
} }
} }
protected WebView getWebview() {
if (webview == null) {
return null;
}
setupWebView();
return webview;
}
protected abstract void navigateToInitialPage(WebView webview); protected abstract void navigateToInitialPage(WebView webview);
protected void onPageLoaded(WebView webview, String url) { protected void onPageLoaded(WebView webview, String url) {
......
...@@ -12,20 +12,22 @@ import org.json.JSONObject; ...@@ -12,20 +12,22 @@ import org.json.JSONObject;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import chat.rocket.android.api.MethodCallHelper; import chat.rocket.android.api.MethodCallHelper;
import chat.rocket.android.fragment.AbstractWebViewFragment; import chat.rocket.android.fragment.AbstractWebViewFragment;
import chat.rocket.android.helper.LogIfError; import chat.rocket.core.models.LoginServiceConfiguration;
import chat.rocket.android.log.RCLog; import chat.rocket.persistence.realm.repositories.RealmLoginServiceConfigurationRepository;
import chat.rocket.persistence.realm.models.ddp.RealmMeteorLoginServiceConfiguration;
import chat.rocket.persistence.realm.RealmStore;
public abstract class AbstractOAuthFragment extends AbstractWebViewFragment { public abstract class AbstractOAuthFragment extends AbstractWebViewFragment
implements OAuthContract.View {
private OAuthContract.Presenter presenter;
protected abstract String getOAuthServiceName();
protected String hostname; protected String hostname;
private String url; private String url;
private boolean resultOK;
protected abstract String getOAuthServiceName(); private boolean resultOK;
protected abstract String generateURL(RealmMeteorLoginServiceConfiguration oauthConfig); protected abstract String generateURL(LoginServiceConfiguration oauthConfig);
private boolean hasValidArgs(Bundle args) { private boolean hasValidArgs(Bundle args) {
return args != null return args != null
...@@ -54,72 +56,93 @@ public abstract class AbstractOAuthFragment extends AbstractWebViewFragment { ...@@ -54,72 +56,93 @@ public abstract class AbstractOAuthFragment extends AbstractWebViewFragment {
} }
hostname = args.getString("hostname"); hostname = args.getString("hostname");
RealmMeteorLoginServiceConfiguration oauthConfig =
RealmStore.get(hostname).executeTransactionForRead(realm -> presenter = new OAuthPresenter(
realm.where(RealmMeteorLoginServiceConfiguration.class) new RealmLoginServiceConfigurationRepository(hostname),
.equalTo(RealmMeteorLoginServiceConfiguration.SERVICE, getOAuthServiceName()) new MethodCallHelper(getContext(), hostname)
.findFirst()); );
if (oauthConfig == null) {
throw new IllegalArgumentException(
"Invalid hostname given,");
} }
url = generateURL(oauthConfig);
@Override
public void onResume() {
super.onResume();
presenter.bindView(this);
presenter.loadService(getOAuthServiceName());
}
@Override
public void onPause() {
presenter.release();
super.onPause();
} }
@Override @Override
protected void navigateToInitialPage(WebView webview) { protected void navigateToInitialPage(WebView webview) {
if (TextUtils.isEmpty(url)) {
finish();
return;
} }
resultOK = false; @Override
webview.loadUrl(url); protected void onPageLoaded(WebView webview, String url) {
webview.addJavascriptInterface(new JSInterface(result -> { super.onPageLoaded(webview, url);
// onPageFinish is called twice... Should ignore latter one.
if (resultOK) { if (url.contains(hostname) && url.contains("_oauth/" + getOAuthServiceName() + "?close")) {
return; final String jsHookUrl = "javascript:"
+ "window._rocketchet_hook.handleConfig(document.getElementById('config').innerText);";
webview.loadUrl(jsHookUrl);
}
} }
if (result != null && result.optBoolean("setCredentialToken", false)) { @Override
try { public void showService(LoginServiceConfiguration oauthConfig) {
final String credentialToken = result.getString("credentialToken"); url = generateURL(oauthConfig);
final String credentialSecret = result.getString("credentialSecret");
handleOAuthCallback(credentialToken, credentialSecret); showWebView();
resultOK = true;
} catch (JSONException exception) {
RCLog.e(exception, "failed to parse OAuth result.");
} }
@Override
public void close() {
finish();
} }
@Override
public void showLoginDone() {
resultOK = true;
onOAuthCompleted(); onOAuthCompleted();
}), "_rocketchet_hook");
} }
@Override @Override
protected void onPageLoaded(WebView webview, String url) { public void showLoginError() {
super.onPageLoaded(webview, url); onOAuthCompleted();
}
if (url.contains(hostname) && url.contains("_oauth/" + getOAuthServiceName() + "?close")) { private void showWebView() {
final String jsHookUrl = "javascript:" if (TextUtils.isEmpty(url)) {
+ "window._rocketchet_hook.handleConfig(document.getElementById('config').innerText);"; finish();
webview.loadUrl(jsHookUrl); return;
} }
final WebView webView = getWebview();
if (webView == null) {
finish();
return;
} }
private void handleOAuthCallback(final String credentialToken, final String credentialSecret) { resultOK = false;
new MethodCallHelper(getContext(), hostname) webView.loadUrl(url);
.loginWithOAuth(credentialToken, credentialSecret) webView.addJavascriptInterface(new JSInterface(result -> {
.continueWith(new LogIfError()); // onPageFinish is called twice... Should ignore latter one.
if (resultOK) {
return;
} }
protected void onOAuthCompleted() { presenter.login(result);
}), "_rocketchet_hook");
}
protected void onOAuthCompleted() {
} }
private interface JSInterfaceCallback { private interface JSInterfaceCallback {
void hanldeResult(@Nullable JSONObject result); void handleResult(@Nullable JSONObject result);
} }
private static final class JSInterface { private static final class JSInterface {
...@@ -132,9 +155,9 @@ public abstract class AbstractOAuthFragment extends AbstractWebViewFragment { ...@@ -132,9 +155,9 @@ public abstract class AbstractOAuthFragment extends AbstractWebViewFragment {
@JavascriptInterface @JavascriptInterface
public void handleConfig(String config) { public void handleConfig(String config) {
try { try {
jsInterfaceCallback.hanldeResult(new JSONObject(config)); jsInterfaceCallback.handleResult(new JSONObject(config));
} catch (Exception exception) { } catch (Exception exception) {
jsInterfaceCallback.hanldeResult(null); jsInterfaceCallback.handleResult(null);
} }
} }
} }
......
package chat.rocket.android.fragment.oauth; package chat.rocket.android.fragment.oauth;
import chat.rocket.persistence.realm.models.ddp.RealmMeteorLoginServiceConfiguration; import chat.rocket.core.models.LoginServiceConfiguration;
import okhttp3.HttpUrl; import okhttp3.HttpUrl;
public class FacebookOAuthFragment extends AbstractOAuthFragment { public class FacebookOAuthFragment extends AbstractOAuthFragment {
...@@ -11,13 +11,13 @@ public class FacebookOAuthFragment extends AbstractOAuthFragment { ...@@ -11,13 +11,13 @@ public class FacebookOAuthFragment extends AbstractOAuthFragment {
} }
@Override @Override
protected String generateURL(RealmMeteorLoginServiceConfiguration oauthConfig) { protected String generateURL(LoginServiceConfiguration oauthConfig) {
return new HttpUrl.Builder().scheme("https") return new HttpUrl.Builder().scheme("https")
.host("www.facebook.com") .host("www.facebook.com")
.addPathSegment("v2.2") .addPathSegment("v2.2")
.addPathSegment("dialog") .addPathSegment("dialog")
.addPathSegment("oauth") .addPathSegment("oauth")
.addQueryParameter("client_id", oauthConfig.getAppId()) .addQueryParameter("client_id", oauthConfig.getKey())
.addQueryParameter("redirect_uri", "https://" + hostname + "/_oauth/facebook?close") .addQueryParameter("redirect_uri", "https://" + hostname + "/_oauth/facebook?close")
.addQueryParameter("display", "popup") .addQueryParameter("display", "popup")
.addQueryParameter("scope", "email") .addQueryParameter("scope", "email")
......
package chat.rocket.android.fragment.oauth; package chat.rocket.android.fragment.oauth;
import chat.rocket.persistence.realm.models.ddp.RealmMeteorLoginServiceConfiguration; import chat.rocket.core.models.LoginServiceConfiguration;
import okhttp3.HttpUrl; import okhttp3.HttpUrl;
public class GitHubOAuthFragment extends AbstractOAuthFragment { public class GitHubOAuthFragment extends AbstractOAuthFragment {
...@@ -11,13 +11,13 @@ public class GitHubOAuthFragment extends AbstractOAuthFragment { ...@@ -11,13 +11,13 @@ public class GitHubOAuthFragment extends AbstractOAuthFragment {
} }
@Override @Override
protected String generateURL(RealmMeteorLoginServiceConfiguration oauthConfig) { protected String generateURL(LoginServiceConfiguration oauthConfig) {
return new HttpUrl.Builder().scheme("https") return new HttpUrl.Builder().scheme("https")
.host("github.com") .host("github.com")
.addPathSegment("login") .addPathSegment("login")
.addPathSegment("oauth") .addPathSegment("oauth")
.addPathSegment("authorize") .addPathSegment("authorize")
.addQueryParameter("client_id", oauthConfig.getClientId()) .addQueryParameter("client_id", oauthConfig.getKey())
.addQueryParameter("scope", "user:email") .addQueryParameter("scope", "user:email")
.addQueryParameter("state", getStateString()) .addQueryParameter("state", getStateString())
.build() .build()
......
package chat.rocket.android.fragment.oauth; package chat.rocket.android.fragment.oauth;
import chat.rocket.persistence.realm.models.ddp.RealmMeteorLoginServiceConfiguration; import chat.rocket.core.models.LoginServiceConfiguration;
import okhttp3.HttpUrl; import okhttp3.HttpUrl;
public class GoogleOAuthFragment extends AbstractOAuthFragment { public class GoogleOAuthFragment extends AbstractOAuthFragment {
...@@ -11,14 +11,14 @@ public class GoogleOAuthFragment extends AbstractOAuthFragment { ...@@ -11,14 +11,14 @@ public class GoogleOAuthFragment extends AbstractOAuthFragment {
} }
@Override @Override
protected String generateURL(RealmMeteorLoginServiceConfiguration oauthConfig) { protected String generateURL(LoginServiceConfiguration oauthConfig) {
return new HttpUrl.Builder().scheme("https") return new HttpUrl.Builder().scheme("https")
.host("accounts.google.com") .host("accounts.google.com")
.addPathSegment("o") .addPathSegment("o")
.addPathSegment("oauth2") .addPathSegment("oauth2")
.addPathSegment("auth") .addPathSegment("auth")
.addQueryParameter("response_type", "code") .addQueryParameter("response_type", "code")
.addQueryParameter("client_id", oauthConfig.getClientId()) .addQueryParameter("client_id", oauthConfig.getKey())
.addQueryParameter("scope", "profile email") .addQueryParameter("scope", "profile email")
.addQueryParameter("redirect_uri", "https://" + hostname + "/_oauth/google?close") .addQueryParameter("redirect_uri", "https://" + hostname + "/_oauth/google?close")
.addQueryParameter("state", getStateString()) .addQueryParameter("state", getStateString())
......
package chat.rocket.android.fragment.oauth;
import org.json.JSONObject;
import chat.rocket.android.shared.BaseContract;
import chat.rocket.core.models.LoginServiceConfiguration;
public interface OAuthContract {
interface View extends BaseContract.View {
void showService(LoginServiceConfiguration oauthConfig);
void close();
void showLoginDone();
void showLoginError();
}
interface Presenter extends BaseContract.Presenter<View> {
void loadService(String serviceName);
void login(JSONObject credentialJsonObject);
}
}
package chat.rocket.android.fragment.oauth;
import io.reactivex.android.schedulers.AndroidSchedulers;
import org.json.JSONObject;
import chat.rocket.android.BackgroundLooper;
import chat.rocket.android.api.MethodCallHelper;
import chat.rocket.android.helper.LogIfError;
import chat.rocket.android.helper.TextUtils;
import chat.rocket.android.shared.BasePresenter;
import chat.rocket.core.repositories.LoginServiceConfigurationRepository;
public class OAuthPresenter extends BasePresenter<OAuthContract.View>
implements OAuthContract.Presenter {
private final LoginServiceConfigurationRepository loginServiceConfigurationRepository;
private final MethodCallHelper methodCallHelper;
public OAuthPresenter(LoginServiceConfigurationRepository loginServiceConfigurationRepository,
MethodCallHelper methodCallHelper) {
this.loginServiceConfigurationRepository = loginServiceConfigurationRepository;
this.methodCallHelper = methodCallHelper;
}
@Override
public void loadService(String serviceName) {
addSubscription(
loginServiceConfigurationRepository.getByName(serviceName)
.subscribeOn(AndroidSchedulers.from(BackgroundLooper.get()))
.observeOn(AndroidSchedulers.mainThread())
.subscribe(optional -> {
if (optional.isPresent()) {
view.showService(optional.get());
} else {
view.close();
}
})
);
}
@Override
public void login(JSONObject credentialJsonObject) {
if (credentialJsonObject == null || !credentialJsonObject.optBoolean("setCredentialToken")) {
view.showLoginError();
return;
}
final String credentialToken = credentialJsonObject.optString("credentialToken");
final String credentialSecret = credentialJsonObject.optString("credentialSecret");
if (TextUtils.isEmpty(credentialToken) || TextUtils.isEmpty(credentialSecret)) {
view.showLoginError();
return;
}
view.showLoginDone();
methodCallHelper.loginWithOAuth(credentialToken, credentialSecret)
.continueWith(new LogIfError());
}
}
package chat.rocket.android.fragment.oauth; package chat.rocket.android.fragment.oauth;
import chat.rocket.persistence.realm.models.ddp.RealmMeteorLoginServiceConfiguration; import chat.rocket.core.models.LoginServiceConfiguration;
public class TwitterOAuthFragment extends AbstractOAuthFragment { public class TwitterOAuthFragment extends AbstractOAuthFragment {
...@@ -10,7 +10,7 @@ public class TwitterOAuthFragment extends AbstractOAuthFragment { ...@@ -10,7 +10,7 @@ public class TwitterOAuthFragment extends AbstractOAuthFragment {
} }
@Override @Override
protected String generateURL(RealmMeteorLoginServiceConfiguration oauthConfig) { protected String generateURL(LoginServiceConfiguration oauthConfig) {
return "https://" + hostname + "/_oauth/twitter/" return "https://" + hostname + "/_oauth/twitter/"
+ "?requestTokenAndRedirect=true&state=" + getStateString(); + "?requestTokenAndRedirect=true&state=" + getStateString();
} }
......
...@@ -25,7 +25,6 @@ abstract class AbstractServerConfigFragment extends AbstractFragment { ...@@ -25,7 +25,6 @@ abstract class AbstractServerConfigFragment extends AbstractFragment {
hostname = args.getString(LoginActivity.KEY_HOSTNAME); hostname = args.getString(LoginActivity.KEY_HOSTNAME);
if (TextUtils.isEmpty(hostname)) { if (TextUtils.isEmpty(hostname)) {
finish(); finish();
return;
} }
} }
......
package chat.rocket.android.fragment.server_config;
import java.util.List;
import chat.rocket.android.shared.BaseContract;
import chat.rocket.core.models.LoginServiceConfiguration;
public interface LoginContract {
interface View extends BaseContract.View {
void showLoader();
void hideLoader();
void showError(String message);
void showLoginServices(List<LoginServiceConfiguration> loginServiceList);
}
interface Presenter extends BaseContract.Presenter<View> {
void login(String username, String password);
}
}
...@@ -11,18 +11,21 @@ import java.util.HashMap; ...@@ -11,18 +11,21 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import chat.rocket.android.R; import chat.rocket.android.R;
import chat.rocket.android.api.MethodCallHelper; import chat.rocket.android.api.MethodCallHelper;
import chat.rocket.android.helper.TextUtils;
import chat.rocket.android.layouthelper.oauth.OAuthProviderInfo; import chat.rocket.android.layouthelper.oauth.OAuthProviderInfo;
import chat.rocket.android.log.RCLog; import chat.rocket.android.log.RCLog;
import chat.rocket.persistence.realm.models.ddp.RealmMeteorLoginServiceConfiguration; import chat.rocket.core.models.LoginServiceConfiguration;
import chat.rocket.persistence.realm.RealmListObserver; import chat.rocket.persistence.realm.repositories.RealmLoginServiceConfigurationRepository;
import chat.rocket.persistence.realm.RealmStore; import chat.rocket.persistence.realm.repositories.RealmPublicSettingRepository;
/** /**
* Login screen. * Login screen.
*/ */
public class LoginFragment extends AbstractServerConfigFragment { public class LoginFragment extends AbstractServerConfigFragment implements LoginContract.View {
private RealmListObserver<RealmMeteorLoginServiceConfiguration> authProvidersObserver;
private LoginContract.Presenter presenter;
private View btnEmail;
private View waitingView;
@Override @Override
protected int getLayout() { protected int getLayout() {
...@@ -32,37 +35,22 @@ public class LoginFragment extends AbstractServerConfigFragment { ...@@ -32,37 +35,22 @@ public class LoginFragment extends AbstractServerConfigFragment {
@Override @Override
public void onCreate(@Nullable Bundle savedInstanceState) { public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
authProvidersObserver = RealmStore.get(hostname)
.createListObserver(realm -> realm.where(RealmMeteorLoginServiceConfiguration.class).findAll()) presenter = new LoginPresenter(
.setOnUpdateListener(this::onRenderAuthProviders); new RealmLoginServiceConfigurationRepository(hostname),
new RealmPublicSettingRepository(hostname),
new MethodCallHelper(getContext(), hostname)
);
} }
@Override @Override
protected void onSetupView() { protected void onSetupView() {
final View btnEmail = rootView.findViewById(R.id.btn_login_with_email); btnEmail = rootView.findViewById(R.id.btn_login_with_email);
final TextView txtUsername = (TextView) rootView.findViewById(R.id.editor_username); final TextView txtUsername = (TextView) rootView.findViewById(R.id.editor_username);
final TextView txtPasswd = (TextView) rootView.findViewById(R.id.editor_passwd); final TextView txtPasswd = (TextView) rootView.findViewById(R.id.editor_passwd);
final View waitingView = rootView.findViewById(R.id.waiting); waitingView = rootView.findViewById(R.id.waiting);
btnEmail.setOnClickListener(view -> { btnEmail.setOnClickListener(
final CharSequence username = txtUsername.getText(); view -> presenter.login(txtUsername.getText().toString(), txtPasswd.getText().toString()));
final CharSequence passwd = txtPasswd.getText();
if (TextUtils.isEmpty(username) || TextUtils.isEmpty(passwd)) {
return;
}
view.setEnabled(false);
waitingView.setVisibility(View.VISIBLE);
new MethodCallHelper(getContext(), hostname)
.loginWithEmail(username.toString(), passwd.toString())
.continueWith(task -> {
if (task.isFaulted()) {
showError(task.getError().getMessage());
view.setEnabled(true);
waitingView.setVisibility(View.GONE);
}
return null;
});
});
final View btnUserRegistration = rootView.findViewById(R.id.btn_user_registration); final View btnUserRegistration = rootView.findViewById(R.id.btn_user_registration);
btnUserRegistration.setOnClickListener(view -> UserRegistrationDialogFragment.create(hostname, btnUserRegistration.setOnClickListener(view -> UserRegistrationDialogFragment.create(hostname,
...@@ -70,11 +58,25 @@ public class LoginFragment extends AbstractServerConfigFragment { ...@@ -70,11 +58,25 @@ public class LoginFragment extends AbstractServerConfigFragment {
.show(getFragmentManager(), UserRegistrationDialogFragment.class.getSimpleName())); .show(getFragmentManager(), UserRegistrationDialogFragment.class.getSimpleName()));
} }
private void showError(String errString) { @Override
Snackbar.make(rootView, errString, Snackbar.LENGTH_SHORT).show(); public void showLoader() {
btnEmail.setEnabled(false);
waitingView.setVisibility(View.VISIBLE);
}
@Override
public void hideLoader() {
btnEmail.setEnabled(true);
waitingView.setVisibility(View.GONE);
} }
private void onRenderAuthProviders(List<RealmMeteorLoginServiceConfiguration> authProviders) { @Override
public void showError(String message) {
Snackbar.make(rootView, message, Snackbar.LENGTH_SHORT).show();
}
@Override
public void showLoginServices(List<LoginServiceConfiguration> loginServiceList) {
HashMap<String, View> viewMap = new HashMap<>(); HashMap<String, View> viewMap = new HashMap<>();
HashMap<String, Boolean> supportedMap = new HashMap<>(); HashMap<String, Boolean> supportedMap = new HashMap<>();
for (OAuthProviderInfo info : OAuthProviderInfo.LIST) { for (OAuthProviderInfo info : OAuthProviderInfo.LIST) {
...@@ -82,7 +84,7 @@ public class LoginFragment extends AbstractServerConfigFragment { ...@@ -82,7 +84,7 @@ public class LoginFragment extends AbstractServerConfigFragment {
supportedMap.put(info.serviceName, false); supportedMap.put(info.serviceName, false);
} }
for (RealmMeteorLoginServiceConfiguration authProvider : authProviders) { for (LoginServiceConfiguration authProvider : loginServiceList) {
for (OAuthProviderInfo info : OAuthProviderInfo.LIST) { for (OAuthProviderInfo info : OAuthProviderInfo.LIST) {
if (!supportedMap.get(info.serviceName) if (!supportedMap.get(info.serviceName)
&& info.serviceName.equals(authProvider.getService())) { && info.serviceName.equals(authProvider.getService())) {
...@@ -116,12 +118,12 @@ public class LoginFragment extends AbstractServerConfigFragment { ...@@ -116,12 +118,12 @@ public class LoginFragment extends AbstractServerConfigFragment {
@Override @Override
public void onResume() { public void onResume() {
super.onResume(); super.onResume();
authProvidersObserver.sub(); presenter.bindView(this);
} }
@Override @Override
public void onPause() { public void onPause() {
authProvidersObserver.unsub(); presenter.release();
super.onPause(); super.onPause();
} }
} }
package chat.rocket.android.fragment.server_config;
import android.support.annotation.NonNull;
import com.fernandocejas.arrow.optional.Optional;
import io.reactivex.android.schedulers.AndroidSchedulers;
import bolts.Task;
import chat.rocket.android.BackgroundLooper;
import chat.rocket.android.api.MethodCallHelper;
import chat.rocket.android.helper.TextUtils;
import chat.rocket.android.shared.BasePresenter;
import chat.rocket.core.PublicSettingsConstants;
import chat.rocket.core.models.PublicSetting;
import chat.rocket.core.repositories.LoginServiceConfigurationRepository;
import chat.rocket.core.repositories.PublicSettingRepository;
public class LoginPresenter extends BasePresenter<LoginContract.View>
implements LoginContract.Presenter {
private final LoginServiceConfigurationRepository loginServiceConfigurationRepository;
private final PublicSettingRepository publicSettingRepository;
private final MethodCallHelper methodCallHelper;
public LoginPresenter(LoginServiceConfigurationRepository loginServiceConfigurationRepository,
PublicSettingRepository publicSettingRepository,
MethodCallHelper methodCallHelper) {
this.loginServiceConfigurationRepository = loginServiceConfigurationRepository;
this.publicSettingRepository = publicSettingRepository;
this.methodCallHelper = methodCallHelper;
}
@Override
public void bindView(@NonNull LoginContract.View view) {
super.bindView(view);
getLoginServices();
}
@Override
public void login(String username, String password) {
if (TextUtils.isEmpty(username) || TextUtils.isEmpty(password)) {
return;
}
view.showLoader();
addSubscription(
publicSettingRepository.getById(PublicSettingsConstants.LDAP.ENABLE)
.subscribeOn(AndroidSchedulers.from(BackgroundLooper.get()))
.observeOn(AndroidSchedulers.mainThread())
.subscribe(publicSettingOptional -> doLogin(username, password, publicSettingOptional))
);
}
private void getLoginServices() {
addSubscription(
loginServiceConfigurationRepository.getAll()
.subscribeOn(AndroidSchedulers.from(BackgroundLooper.get()))
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
loginServiceConfigurations -> view.showLoginServices(loginServiceConfigurations))
);
}
private void doLogin(String username, String password, Optional<PublicSetting> optional) {
call(username, password, optional)
.continueWith(task -> {
if (task.isFaulted()) {
view.hideLoader();
view.showError(task.getError().getMessage());
}
return null;
});
}
private Task<Void> call(String username, String password, Optional<PublicSetting> optional) {
if (optional.isPresent() && optional.get().getValueAsBoolean()) {
return methodCallHelper.loginWithLdap(username, password);
}
return methodCallHelper.loginWithEmail(username, password);
}
}
package chat.rocket.android.fragment.server_config;
import chat.rocket.android.shared.BaseContract;
public interface RetryLoginContract {
interface View extends BaseContract.View {
void showRetry(String token);
void showError(String message);
void showLoader();
void hideLoader();
}
interface Presenter extends BaseContract.Presenter<View> {
void onLogin(String token);
}
}
...@@ -7,16 +7,20 @@ import android.widget.TextView; ...@@ -7,16 +7,20 @@ import android.widget.TextView;
import chat.rocket.android.R; import chat.rocket.android.R;
import chat.rocket.android.api.MethodCallHelper; import chat.rocket.android.api.MethodCallHelper;
import chat.rocket.android.helper.TextUtils; import chat.rocket.core.interactors.SessionInteractor;
import chat.rocket.persistence.realm.models.internal.RealmSession; import chat.rocket.persistence.realm.repositories.RealmSessionRepository;
import chat.rocket.persistence.realm.RealmObjectObserver;
import chat.rocket.persistence.realm.RealmStore;
/** /**
* Login screen. * Login screen.
*/ */
public class RetryLoginFragment extends AbstractServerConfigFragment { public class RetryLoginFragment extends AbstractServerConfigFragment
private RealmObjectObserver<RealmSession> sessionObserver; implements RetryLoginContract.View {
private RetryLoginContract.Presenter presenter;
private View btnRetry;
private View waitingView;
private TextView txtError;
@Override @Override
protected int getLayout() { protected int getLayout() {
...@@ -26,56 +30,52 @@ public class RetryLoginFragment extends AbstractServerConfigFragment { ...@@ -26,56 +30,52 @@ public class RetryLoginFragment extends AbstractServerConfigFragment {
@Override @Override
public void onCreate(@Nullable Bundle savedInstanceState) { public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
sessionObserver = RealmStore.get(hostname)
.createObjectObserver(RealmSession::queryDefaultSession) presenter = new RetryLoginPresenter(
.setOnUpdateListener(this::onRenderServerConfigSession); new SessionInteractor(new RealmSessionRepository(hostname)),
new MethodCallHelper(getContext(), hostname)
);
} }
@Override @Override
protected void onSetupView() { public void onResume() {
super.onResume();
presenter.bindView(this);
} }
private void onRenderServerConfigSession(RealmSession session) { @Override
if (session == null) { public void onPause() {
return; presenter.release();
super.onPause();
} }
final String token = session.getToken(); @Override
if (!TextUtils.isEmpty(token)) { protected void onSetupView() {
final View btnRetry = rootView.findViewById(R.id.btn_retry_login); btnRetry = rootView.findViewById(R.id.btn_retry_login);
final View waitingView = rootView.findViewById(R.id.waiting); waitingView = rootView.findViewById(R.id.waiting);
waitingView.setVisibility(View.GONE); txtError = (TextView) rootView.findViewById(R.id.txt_error_description);
btnRetry.setOnClickListener(view -> { }
view.setEnabled(false);
waitingView.setVisibility(View.VISIBLE);
new MethodCallHelper(getContext(), hostname).loginWithToken(token) @Override
.continueWith(task -> { public void showRetry(String token) {
if (task.isFaulted()) {
view.setEnabled(true);
waitingView.setVisibility(View.GONE); waitingView.setVisibility(View.GONE);
} btnRetry.setOnClickListener(view -> presenter.onLogin(token));
return null;
});
});
} }
final String error = session.getError(); @Override
final TextView txtError = (TextView) rootView.findViewById(R.id.txt_error_description); public void showError(String message) {
if (!TextUtils.isEmpty(error)) { txtError.setText(message);
txtError.setText(error);
}
} }
@Override @Override
public void onResume() { public void showLoader() {
super.onResume(); btnRetry.setEnabled(false);
sessionObserver.sub(); waitingView.setVisibility(View.VISIBLE);
} }
@Override @Override
public void onPause() { public void hideLoader() {
sessionObserver.unsub(); btnRetry.setEnabled(true);
super.onPause(); waitingView.setVisibility(View.GONE);
} }
} }
package chat.rocket.android.fragment.server_config;
import android.support.annotation.NonNull;
import com.fernandocejas.arrow.optional.Optional;
import io.reactivex.android.schedulers.AndroidSchedulers;
import chat.rocket.android.BackgroundLooper;
import chat.rocket.android.api.MethodCallHelper;
import chat.rocket.android.helper.TextUtils;
import chat.rocket.android.shared.BasePresenter;
import chat.rocket.core.interactors.SessionInteractor;
import chat.rocket.core.models.Session;
public class RetryLoginPresenter extends BasePresenter<RetryLoginContract.View>
implements RetryLoginContract.Presenter {
private final SessionInteractor sessionInteractor;
private final MethodCallHelper methodCallHelper;
public RetryLoginPresenter(SessionInteractor sessionInteractor,
MethodCallHelper methodCallHelper) {
this.sessionInteractor = sessionInteractor;
this.methodCallHelper = methodCallHelper;
}
@Override
public void bindView(@NonNull RetryLoginContract.View view) {
super.bindView(view);
subscribeToDefaultSession();
}
@Override
public void onLogin(String token) {
view.showLoader();
methodCallHelper.loginWithToken(token)
.continueWith(task -> {
if (task.isFaulted()) {
view.hideLoader();
}
return null;
});
}
private void subscribeToDefaultSession() {
addSubscription(
sessionInteractor.getDefault()
.subscribeOn(AndroidSchedulers.from(BackgroundLooper.get()))
.observeOn(AndroidSchedulers.mainThread())
.subscribe(this::onSession)
);
}
private void onSession(Optional<Session> sessionOptional) {
if (!sessionOptional.isPresent()) {
return;
}
final Session session = sessionOptional.get();
final String token = session.getToken();
if (!TextUtils.isEmpty(token)) {
view.showRetry(token);
}
final String errorMessage = session.getError();
if (!TextUtils.isEmpty(errorMessage)) {
view.showError(errorMessage);
}
}
}
...@@ -15,13 +15,11 @@ public class GcmPushSettingHelper { ...@@ -15,13 +15,11 @@ public class GcmPushSettingHelper {
public static RealmResults<RealmPublicSetting> queryForGcmPushEnabled(Realm realm) { public static RealmResults<RealmPublicSetting> queryForGcmPushEnabled(Realm realm) {
return realm.where(RealmPublicSetting.class) return realm.where(RealmPublicSetting.class)
.equalTo(RealmPublicSetting.ID, PublicSettingsConstants.Push.ENABLE) .equalTo(RealmPublicSetting.ID, PublicSettingsConstants.Push.ENABLE)
.or()
.equalTo(RealmPublicSetting.ID, PublicSettingsConstants.Push.GCM_PROJECT_NUMBER)
.findAll(); .findAll();
} }
public static boolean isGcmPushEnabled(List<RealmPublicSetting> results) { public static boolean isGcmPushEnabled(List<RealmPublicSetting> results) {
return isPushEnabled(results) && hasValidGcmConfig(results); return isPushEnabled(results);
} }
private static boolean isPushEnabled(List<RealmPublicSetting> results) { private static boolean isPushEnabled(List<RealmPublicSetting> results) {
...@@ -32,13 +30,4 @@ public class GcmPushSettingHelper { ...@@ -32,13 +30,4 @@ public class GcmPushSettingHelper {
} }
return false; return false;
} }
private static boolean hasValidGcmConfig(List<RealmPublicSetting> results) {
for (RealmPublicSetting setting : results) {
if (PublicSettingsConstants.Push.GCM_PROJECT_NUMBER.equals(setting.getId())) {
return !TextUtils.isEmpty(setting.getValue());
}
}
return false;
}
} }
...@@ -10,6 +10,7 @@ import io.realm.RealmResults; ...@@ -10,6 +10,7 @@ import io.realm.RealmResults;
import java.io.IOException; import java.io.IOException;
import java.util.List; import java.util.List;
import bolts.Task; import bolts.Task;
import chat.rocket.android.R;
import chat.rocket.android.RocketChatCache; import chat.rocket.android.RocketChatCache;
import chat.rocket.android.api.RaixPushHelper; import chat.rocket.android.api.RaixPushHelper;
import chat.rocket.android.helper.LogIfError; import chat.rocket.android.helper.LogIfError;
...@@ -66,10 +67,7 @@ public class GcmPushRegistrationObserver extends AbstractModelObserver<GcmPushRe ...@@ -66,10 +67,7 @@ public class GcmPushRegistrationObserver extends AbstractModelObserver<GcmPushRe
} }
private Task<Void> registerGcmTokenForServer() throws IOException { private Task<Void> registerGcmTokenForServer() throws IOException {
final String senderId = RealmPublicSetting final String gcmToken = getGcmToken(getSenderId());
.getString(realmHelper, PublicSettingsConstants.Push.GCM_PROJECT_NUMBER, "").trim();
final String gcmToken = getGcmToken(senderId);
final RealmUser currentUser = realmHelper.executeTransactionForRead(realm -> final RealmUser currentUser = realmHelper.executeTransactionForRead(realm ->
RealmUser.queryCurrentUser(realm).findFirst()); RealmUser.queryCurrentUser(realm).findFirst());
final String userId = currentUser != null ? currentUser.getId() : null; final String userId = currentUser != null ? currentUser.getId() : null;
...@@ -84,4 +82,15 @@ public class GcmPushRegistrationObserver extends AbstractModelObserver<GcmPushRe ...@@ -84,4 +82,15 @@ public class GcmPushRegistrationObserver extends AbstractModelObserver<GcmPushRe
.getToken(senderId, GoogleCloudMessaging.INSTANCE_ID_SCOPE, null); .getToken(senderId, GoogleCloudMessaging.INSTANCE_ID_SCOPE, null);
} }
private String getSenderId() {
final String senderId = RealmPublicSetting
.getString(realmHelper, PublicSettingsConstants.Push.GCM_PROJECT_NUMBER, "").trim();
if (senderId.length() != 0) {
return senderId;
}
return context.getString(R.string.gcm_sender_id);
}
} }
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="gcm_sender_id">YOUR-SENDER-ID</string>
</resources>
\ No newline at end of file
...@@ -3,6 +3,8 @@ package chat.rocket.persistence.realm.models.ddp; ...@@ -3,6 +3,8 @@ package chat.rocket.persistence.realm.models.ddp;
import io.realm.RealmObject; import io.realm.RealmObject;
import io.realm.annotations.PrimaryKey; import io.realm.annotations.PrimaryKey;
import chat.rocket.core.models.LoginServiceConfiguration;
/** /**
* subscription model for "meteor_accounts_loginServiceConfiguration". * subscription model for "meteor_accounts_loginServiceConfiguration".
*/ */
...@@ -19,9 +21,9 @@ public class RealmMeteorLoginServiceConfiguration ...@@ -19,9 +21,9 @@ public class RealmMeteorLoginServiceConfiguration
@PrimaryKey private String _id; @PrimaryKey private String _id;
private String service; private String service;
private String consumerKey; //for Twitter private String consumerKey; // for Twitter
private String appId; //for Facebook private String appId; // for Facebook
private String clientId; //for other auth providers private String clientId; // for other auth providers
public String getId() { public String getId() {
return _id; return _id;
...@@ -62,4 +64,24 @@ public class RealmMeteorLoginServiceConfiguration ...@@ -62,4 +64,24 @@ public class RealmMeteorLoginServiceConfiguration
public void setClientId(String clientId) { public void setClientId(String clientId) {
this.clientId = clientId; this.clientId = clientId;
} }
public LoginServiceConfiguration asLoginServiceConfiguration() {
return LoginServiceConfiguration.builder()
.setId(_id)
.setService(service)
.setKey(getServiceKey())
.build();
}
private String getServiceKey() {
if (consumerKey != null) {
return consumerKey;
}
if (appId != null) {
return appId;
}
return clientId;
}
} }
...@@ -7,6 +7,7 @@ import org.json.JSONException; ...@@ -7,6 +7,7 @@ import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
import chat.rocket.core.JsonConstants; import chat.rocket.core.JsonConstants;
import chat.rocket.core.models.PublicSetting;
import chat.rocket.persistence.realm.RealmHelper; import chat.rocket.persistence.realm.RealmHelper;
/** /**
...@@ -113,4 +114,14 @@ public class RealmPublicSetting extends RealmObject { ...@@ -113,4 +114,14 @@ public class RealmPublicSetting extends RealmObject {
public void setMeta(String meta) { public void setMeta(String meta) {
this.meta = meta; this.meta = meta;
} }
public PublicSetting asPublicSetting() {
return PublicSetting.builder()
.setId(_id)
.setGroup(group)
.setType(type)
.setValue(value)
.setUpdatedAt(_updatedAt)
.build();
}
} }
package chat.rocket.persistence.realm.repositories;
import android.os.Looper;
import android.support.v4.util.Pair;
import com.fernandocejas.arrow.optional.Optional;
import io.reactivex.Flowable;
import io.reactivex.Single;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.realm.RealmResults;
import java.util.ArrayList;
import java.util.List;
import chat.rocket.core.models.LoginServiceConfiguration;
import chat.rocket.core.repositories.LoginServiceConfigurationRepository;
import chat.rocket.persistence.realm.RealmStore;
import chat.rocket.persistence.realm.models.ddp.RealmMeteorLoginServiceConfiguration;
import hu.akarnokd.rxjava.interop.RxJavaInterop;
public class RealmLoginServiceConfigurationRepository extends RealmRepository
implements LoginServiceConfigurationRepository {
private final String hostname;
public RealmLoginServiceConfigurationRepository(String hostname) {
this.hostname = hostname;
}
@Override
public Single<Optional<LoginServiceConfiguration>> getByName(String serviceName) {
return Single.defer(() -> Flowable.using(
() -> new Pair<>(RealmStore.getRealm(hostname), Looper.myLooper()),
pair -> RxJavaInterop.toV2Flowable(
pair.first.where(RealmMeteorLoginServiceConfiguration.class)
.equalTo(RealmMeteorLoginServiceConfiguration.SERVICE, serviceName)
.findAll()
.<RealmResults<RealmMeteorLoginServiceConfiguration>>asObservable()),
pair -> close(pair.first, pair.second)
)
.unsubscribeOn(AndroidSchedulers.from(Looper.myLooper()))
.filter(it -> it.isLoaded() && it.isValid() && it.size() > 0)
.map(it -> Optional.of(it.get(0).asLoginServiceConfiguration()))
.first(Optional.absent()));
}
@Override
public Flowable<List<LoginServiceConfiguration>> getAll() {
return Flowable.defer(() -> Flowable.using(
() -> new Pair<>(RealmStore.getRealm(hostname), Looper.myLooper()),
pair -> RxJavaInterop
.toV2Flowable(pair.first.where(RealmMeteorLoginServiceConfiguration.class)
.findAll()
.asObservable()),
pair -> close(pair.first, pair.second)
)
.unsubscribeOn(AndroidSchedulers.from(Looper.myLooper()))
.filter(it -> it.isLoaded() && it.isValid())
.map(this::toList));
}
private List<LoginServiceConfiguration> toList(
RealmResults<RealmMeteorLoginServiceConfiguration> realmConfigurations) {
final int total = realmConfigurations.size();
final List<LoginServiceConfiguration> serviceConfigurations = new ArrayList<>(total);
for (int i = 0; i < total; i++) {
serviceConfigurations.add(realmConfigurations.get(i).asLoginServiceConfiguration());
}
return serviceConfigurations;
}
}
package chat.rocket.persistence.realm.repositories;
import android.os.Looper;
import android.support.v4.util.Pair;
import com.fernandocejas.arrow.optional.Optional;
import io.reactivex.Flowable;
import io.reactivex.Single;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.realm.RealmResults;
import chat.rocket.core.models.PublicSetting;
import chat.rocket.core.repositories.PublicSettingRepository;
import chat.rocket.persistence.realm.RealmStore;
import chat.rocket.persistence.realm.models.ddp.RealmPublicSetting;
import hu.akarnokd.rxjava.interop.RxJavaInterop;
public class RealmPublicSettingRepository extends RealmRepository
implements PublicSettingRepository {
private final String hostname;
public RealmPublicSettingRepository(String hostname) {
this.hostname = hostname;
}
@Override
public Single<Optional<PublicSetting>> getById(String id) {
return Single.defer(() -> Flowable.using(
() -> new Pair<>(RealmStore.getRealm(hostname), Looper.myLooper()),
pair -> RxJavaInterop.toV2Flowable(
pair.first.where(RealmPublicSetting.class)
.equalTo(RealmPublicSetting.ID, id)
.findAll()
.<RealmResults<RealmPublicSetting>>asObservable()),
pair -> close(pair.first, pair.second)
)
.unsubscribeOn(AndroidSchedulers.from(Looper.myLooper()))
.filter(it -> it.isLoaded() && it.isValid() && it.size() > 0)
.map(it -> Optional.of(it.get(0).asPublicSetting()))
.first(Optional.absent()));
}
}
package chat.rocket.core.models;
import com.google.auto.value.AutoValue;
@AutoValue
public abstract class LoginServiceConfiguration {
public abstract String getId();
public abstract String getService();
public abstract String getKey();
public static Builder builder() {
return new AutoValue_LoginServiceConfiguration.Builder();
}
@AutoValue.Builder
public abstract static class Builder {
public abstract Builder setId(String id);
public abstract Builder setService(String service);
public abstract Builder setKey(String key);
public abstract LoginServiceConfiguration build();
}
}
package chat.rocket.core.models;
import com.google.auto.value.AutoValue;
import javax.annotation.Nullable;
@AutoValue
public abstract class PublicSetting {
public abstract String getId();
@Nullable
public abstract String getGroup();
@Nullable
public abstract String getType();
public abstract String getValue();
public abstract long getUpdatedAt();
public boolean getValueAsBoolean() {
return Boolean.parseBoolean(getValue());
}
public static Builder builder() {
return new AutoValue_PublicSetting.Builder();
}
@AutoValue.Builder
public abstract static class Builder {
public abstract Builder setId(String id);
public abstract Builder setGroup(String group);
public abstract Builder setType(String type);
public abstract Builder setValue(String value);
public abstract Builder setUpdatedAt(long updatedAt);
public abstract PublicSetting build();
}
}
package chat.rocket.core.repositories;
import com.fernandocejas.arrow.optional.Optional;
import io.reactivex.Flowable;
import io.reactivex.Single;
import java.util.List;
import chat.rocket.core.models.LoginServiceConfiguration;
public interface LoginServiceConfigurationRepository {
Single<Optional<LoginServiceConfiguration>> getByName(String serviceName);
Flowable<List<LoginServiceConfiguration>> getAll();
}
package chat.rocket.core.repositories;
import com.fernandocejas.arrow.optional.Optional;
import io.reactivex.Single;
import chat.rocket.core.models.PublicSetting;
public interface PublicSettingRepository {
Single<Optional<PublicSetting>> getById(String id);
}
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