diff --git a/vector/src/main/java/im/vector/riotx/core/di/FragmentModule.kt b/vector/src/main/java/im/vector/riotx/core/di/FragmentModule.kt index 1457519052..d788f4c04c 100644 --- a/vector/src/main/java/im/vector/riotx/core/di/FragmentModule.kt +++ b/vector/src/main/java/im/vector/riotx/core/di/FragmentModule.kt @@ -37,7 +37,7 @@ import im.vector.riotx.features.home.room.detail.RoomDetailFragment import im.vector.riotx.features.home.room.list.RoomListFragment import im.vector.riotx.features.login.LoginFragment import im.vector.riotx.features.login.LoginServerUrlFormFragment -import im.vector.riotx.features.login.LoginSsoFallbackFragment +import im.vector.riotx.features.login.LoginWebFragment import im.vector.riotx.features.reactions.EmojiSearchResultFragment import im.vector.riotx.features.roomdirectory.PublicRoomsFragment import im.vector.riotx.features.roomdirectory.createroom.CreateRoomFragment @@ -124,8 +124,8 @@ interface FragmentModule { @Binds @IntoMap - @FragmentKey(LoginSsoFallbackFragment::class) - fun bindLoginSsoFallbackFragment(fragment: LoginSsoFallbackFragment): Fragment + @FragmentKey(LoginWebFragment::class) + fun bindLoginWebFragment(fragment: LoginWebFragment): Fragment @Binds @IntoMap diff --git a/vector/src/main/java/im/vector/riotx/features/login/LoginAction.kt b/vector/src/main/java/im/vector/riotx/features/login/LoginAction.kt index 310d07b746..754372e82c 100644 --- a/vector/src/main/java/im/vector/riotx/features/login/LoginAction.kt +++ b/vector/src/main/java/im/vector/riotx/features/login/LoginAction.kt @@ -24,7 +24,7 @@ sealed class LoginAction : VectorViewModelAction { data class UpdateHomeServer(val homeServerUrl: String) : LoginAction() data class UpdateSignMode(val signMode: SignMode) : LoginAction() data class Login(val login: String, val password: String) : LoginAction() - data class SsoLoginSuccess(val credentials: Credentials) : LoginAction() + data class WebLoginSuccess(val credentials: Credentials) : LoginAction() data class InitWith(val loginConfig: LoginConfig) : LoginAction() // Reset actions diff --git a/vector/src/main/java/im/vector/riotx/features/login/LoginActivity.kt b/vector/src/main/java/im/vector/riotx/features/login/LoginActivity.kt index 7e90931716..c3a748f442 100644 --- a/vector/src/main/java/im/vector/riotx/features/login/LoginActivity.kt +++ b/vector/src/main/java/im/vector/riotx/features/login/LoginActivity.kt @@ -65,11 +65,11 @@ class LoginActivity : VectorBaseActivity() { loginSharedActionViewModel.observe() .subscribe { when (it) { - is LoginNavigation.OpenServerSelection -> addFragmentToBackstack(R.id.loginFragmentContainer, LoginServerSelectionFragment::class.java) - is LoginNavigation.OnServerSelectionDone -> onServerSelectionDone() - is LoginNavigation.OnSignModeSelected -> onSignModeSelected() - is LoginNavigation.OnLoginFlowRetrieved -> onLoginFlowRetrieved() - is LoginNavigation.OnSsoLoginFallbackError -> onSsoLoginFallbackError(it) + is LoginNavigation.OpenServerSelection -> addFragmentToBackstack(R.id.loginFragmentContainer, LoginServerSelectionFragment::class.java) + is LoginNavigation.OnServerSelectionDone -> onServerSelectionDone() + is LoginNavigation.OnSignModeSelected -> onSignModeSelected() + is LoginNavigation.OnLoginFlowRetrieved -> onLoginFlowRetrieved() + is LoginNavigation.OnWebLoginError -> onWebLoginError(it) } } .disposeOnDestroy() @@ -97,14 +97,14 @@ class LoginActivity : VectorBaseActivity() { loginLoading.isVisible = loginViewState.isLoading() } - private fun onSsoLoginFallbackError(onSsoLoginFallbackError: LoginNavigation.OnSsoLoginFallbackError) { + private fun onWebLoginError(onWebLoginError: LoginNavigation.OnWebLoginError) { // Pop the backstack supportFragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE) // And inform the user AlertDialog.Builder(this) .setTitle(R.string.dialog_title_error) - .setMessage(getString(R.string.login_sso_error_message, onSsoLoginFallbackError.description, onSsoLoginFallbackError.errorCode)) + .setMessage(getString(R.string.login_sso_error_message, onWebLoginError.description, onWebLoginError.errorCode)) .setPositiveButton(R.string.ok, null) .show() } @@ -124,17 +124,28 @@ class LoginActivity : VectorBaseActivity() { SignMode.SignIn -> { // It depends on the LoginMode withState(loginViewModel) { - when (it.asyncHomeServerLoginFlowRequest.invoke()) { - null -> error("Developer error") - LoginMode.Password -> addFragmentToBackstack(R.id.loginFragmentContainer, LoginFragment::class.java) - LoginMode.Sso -> addFragmentToBackstack(R.id.loginFragmentContainer, LoginSsoFallbackFragment::class.java) - LoginMode.Unsupported -> TODO() // TODO Import Fallback login fragment from Riot-Android + when (val loginMode = it.asyncHomeServerLoginFlowRequest.invoke()) { + null -> error("Developer error") + LoginMode.Password -> addFragmentToBackstack(R.id.loginFragmentContainer, LoginFragment::class.java) + LoginMode.Sso -> addFragmentToBackstack(R.id.loginFragmentContainer, LoginWebFragment::class.java) + is LoginMode.Unsupported -> onLoginModeNotSupported(loginMode) } } } } } + private fun onLoginModeNotSupported(unsupportedLoginMode: LoginMode.Unsupported) { + AlertDialog.Builder(this) + .setTitle(R.string.app_name) + .setMessage(getString(R.string.login_mode_not_supported, unsupportedLoginMode.types.joinToString { "'$it'" })) + .setPositiveButton(R.string.yes) { _, _ -> + addFragmentToBackstack(R.id.loginFragmentContainer, LoginWebFragment::class.java) + } + .setNegativeButton(R.string.no, null) + .show() + } + override fun onResume() { super.onResume() diff --git a/vector/src/main/java/im/vector/riotx/features/login/LoginFragment.kt b/vector/src/main/java/im/vector/riotx/features/login/LoginFragment.kt index 2bc5a6171e..58017d0f21 100644 --- a/vector/src/main/java/im/vector/riotx/features/login/LoginFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/login/LoginFragment.kt @@ -105,11 +105,6 @@ class LoginFragment @Inject constructor( loginSubmit.setOnClickListener { authenticate() } } -// // TODO Move to server selection screen -// private fun openSso() { -// loginSharedActionViewModel.post(LoginNavigation.OpenSsoLoginFallback) -// } - private fun setupPasswordReveal() { passwordShown = false diff --git a/vector/src/main/java/im/vector/riotx/features/login/LoginMode.kt b/vector/src/main/java/im/vector/riotx/features/login/LoginMode.kt index ae40d3a95a..bea4c41cb8 100644 --- a/vector/src/main/java/im/vector/riotx/features/login/LoginMode.kt +++ b/vector/src/main/java/im/vector/riotx/features/login/LoginMode.kt @@ -16,8 +16,8 @@ package im.vector.riotx.features.login -enum class LoginMode { - Password, - Sso, - Unsupported +sealed class LoginMode { + object Password : LoginMode() + object Sso : LoginMode() + data class Unsupported(val types: List) : LoginMode() } diff --git a/vector/src/main/java/im/vector/riotx/features/login/LoginNavigation.kt b/vector/src/main/java/im/vector/riotx/features/login/LoginNavigation.kt index 2c366a0eb7..f0ff456734 100644 --- a/vector/src/main/java/im/vector/riotx/features/login/LoginNavigation.kt +++ b/vector/src/main/java/im/vector/riotx/features/login/LoginNavigation.kt @@ -24,6 +24,5 @@ sealed class LoginNavigation : VectorSharedAction { object OnServerSelectionDone : LoginNavigation() object OnLoginFlowRetrieved : LoginNavigation() object OnSignModeSelected : LoginNavigation() - //object OpenSsoLoginFallback : LoginNavigation() - data class OnSsoLoginFallbackError(val errorCode: Int, val description: String, val failingUrl: String) : LoginNavigation() + data class OnWebLoginError(val errorCode: Int, val description: String, val failingUrl: String) : LoginNavigation() } diff --git a/vector/src/main/java/im/vector/riotx/features/login/LoginViewModel.kt b/vector/src/main/java/im/vector/riotx/features/login/LoginViewModel.kt index f4a9b24812..9ba5fa739b 100644 --- a/vector/src/main/java/im/vector/riotx/features/login/LoginViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/login/LoginViewModel.kt @@ -73,7 +73,7 @@ class LoginViewModel @AssistedInject constructor(@Assisted initialState: LoginVi is LoginAction.InitWith -> handleInitWith(action) is LoginAction.UpdateHomeServer -> handleUpdateHomeserver(action) is LoginAction.Login -> handleLogin(action) - is LoginAction.SsoLoginSuccess -> handleSsoLoginSuccess(action) + is LoginAction.WebLoginSuccess -> handleWebLoginSuccess(action) is LoginAction.ResetAction -> handleResetAction(action) } } @@ -167,7 +167,7 @@ class LoginViewModel @AssistedInject constructor(@Assisted initialState: LoginVi } } - private fun handleSsoLoginSuccess(action: LoginAction.SsoLoginSuccess) { + private fun handleWebLoginSuccess(action: LoginAction.WebLoginSuccess) { val homeServerConnectionConfigFinal = homeServerConnectionConfig if (homeServerConnectionConfigFinal == null) { @@ -233,7 +233,7 @@ class LoginViewModel @AssistedInject constructor(@Assisted initialState: LoginVi // SSO login is taken first data.flows.any { it.type == InteractiveAuthenticationFlow.TYPE_LOGIN_SSO } -> LoginMode.Sso data.flows.any { it.type == InteractiveAuthenticationFlow.TYPE_LOGIN_PASSWORD } -> LoginMode.Password - else -> LoginMode.Unsupported + else -> LoginMode.Unsupported(data.flows.mapNotNull { it.type }.toList()) } setState { diff --git a/vector/src/main/java/im/vector/riotx/features/login/LoginSsoFallbackFragment.kt b/vector/src/main/java/im/vector/riotx/features/login/LoginWebFragment.kt similarity index 86% rename from vector/src/main/java/im/vector/riotx/features/login/LoginSsoFallbackFragment.kt rename to vector/src/main/java/im/vector/riotx/features/login/LoginWebFragment.kt index fcf34bbdc4..d7230f0075 100644 --- a/vector/src/main/java/im/vector/riotx/features/login/LoginSsoFallbackFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/login/LoginWebFragment.kt @@ -34,47 +34,47 @@ import im.vector.matrix.android.api.auth.data.Credentials import im.vector.matrix.android.api.util.JsonDict import im.vector.matrix.android.internal.di.MoshiProvider import im.vector.riotx.R -import kotlinx.android.synthetic.main.fragment_login_sso_fallback.* +import kotlinx.android.synthetic.main.fragment_login_web.* import timber.log.Timber import java.net.URLDecoder import javax.inject.Inject /** - * Only login is supported for the moment + * This screen is displayed for SSO login and also when the application does not support login flow or registration flow + * of the homeserfver, as a fallback to login or to create an account */ -class LoginSsoFallbackFragment @Inject constructor() : AbstractLoginFragment() { +class LoginWebFragment @Inject constructor() : AbstractLoginFragment() { - private var homeServerUrl: String = "" + private lateinit var homeServerUrl: String + private lateinit var signMode: SignMode - enum class Mode { - MODE_LOGIN, - // Not supported in RiotX for the moment - MODE_REGISTER - } - - // Mode (MODE_LOGIN or MODE_REGISTER) - private var mMode = Mode.MODE_LOGIN - - override fun getLayoutResId() = R.layout.fragment_login_sso_fallback + override fun getLayoutResId() = R.layout.fragment_login_web override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - setupToolbar(login_sso_fallback_toolbar) - login_sso_fallback_toolbar.title = getString(R.string.login) + homeServerUrl = loginViewModel.getHomeServerUrl() + signMode = loginViewModel.signMode.takeIf { it != SignMode.Unknown } ?: error("Developer error: Invalid sign mode") - setupWebview() + setupToolbar(loginWebToolbar) + setupTitle() + setupWebView() + } + + private fun setupTitle() { + loginWebToolbar.title = when (signMode) { + SignMode.SignIn -> getString(R.string.login_signin) + else -> getString(R.string.login_signup) + } } @SuppressLint("SetJavaScriptEnabled") - private fun setupWebview() { - login_sso_fallback_webview.settings.javaScriptEnabled = true + private fun setupWebView() { + loginWebWebView.settings.javaScriptEnabled = true // Due to https://developers.googleblog.com/2016/08/modernizing-oauth-interactions-in-native-apps.html, we hack // the user agent to bypass the limitation of Google, as a quick fix (a proper solution will be to use the SSO SDK) - login_sso_fallback_webview.settings.userAgentString = "Mozilla/5.0 Google" - - homeServerUrl = loginViewModel.getHomeServerUrl() + loginWebWebView.settings.userAgentString = "Mozilla/5.0 Google" if (!homeServerUrl.endsWith("/")) { homeServerUrl += "/" @@ -109,14 +109,14 @@ class LoginSsoFallbackFragment @Inject constructor() : AbstractLoginFragment() { } private fun launchWebView() { - if (mMode == Mode.MODE_LOGIN) { - login_sso_fallback_webview.loadUrl(homeServerUrl + "_matrix/static/client/login/") + if (signMode == SignMode.SignIn) { + loginWebWebView.loadUrl(homeServerUrl + "_matrix/static/client/login/") } else { // MODE_REGISTER - login_sso_fallback_webview.loadUrl(homeServerUrl + "_matrix/static/client/register/") + loginWebWebView.loadUrl(homeServerUrl + "_matrix/static/client/register/") } - login_sso_fallback_webview.webViewClient = object : WebViewClient() { + loginWebWebView.webViewClient = object : WebViewClient() { override fun onReceivedSslError(view: WebView, handler: SslErrorHandler, error: SslError) { AlertDialog.Builder(requireActivity()) @@ -131,20 +131,20 @@ class LoginSsoFallbackFragment @Inject constructor() : AbstractLoginFragment() { } false }) + .setCancelable(false) .show() } override fun onReceivedError(view: WebView, errorCode: Int, description: String, failingUrl: String) { super.onReceivedError(view, errorCode, description, failingUrl) - // on error case, close this fragment - loginSharedActionViewModel.post(LoginNavigation.OnSsoLoginFallbackError(errorCode, description, failingUrl)) + loginSharedActionViewModel.post(LoginNavigation.OnWebLoginError(errorCode, description, failingUrl)) } override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) { super.onPageStarted(view, url, favicon) - login_sso_fallback_toolbar.subtitle = url + loginWebToolbar.subtitle = url } override fun onPageFinished(view: WebView, url: String) { @@ -160,7 +160,7 @@ class LoginSsoFallbackFragment @Inject constructor() : AbstractLoginFragment() { view.loadUrl(mxcJavascriptSendObjectMessage) - if (mMode == Mode.MODE_LOGIN) { + if (signMode == SignMode.SignIn) { // The function the fallback page calls when the login is complete val mxcJavascriptOnRegistered = "javascript:window.matrixLogin.onLogin = function(response) {" + " sendObjectMessage({ 'action': 'onLogin', 'credentials': response });" + @@ -227,7 +227,7 @@ class LoginSsoFallbackFragment @Inject constructor() : AbstractLoginFragment() { if (parameters != null) { val action = parameters["action"] as String - if (mMode == Mode.MODE_LOGIN) { + if (signMode == SignMode.SignIn) { try { if (action == "onLogin") { @Suppress("UNCHECKED_CAST") @@ -248,7 +248,7 @@ class LoginSsoFallbackFragment @Inject constructor() : AbstractLoginFragment() { refreshToken = null ) - loginViewModel.handle(LoginAction.SsoLoginSuccess(safeCredentials)) + loginViewModel.handle(LoginAction.WebLoginSuccess(safeCredentials)) } } } catch (e: Exception) { @@ -273,7 +273,7 @@ class LoginSsoFallbackFragment @Inject constructor() : AbstractLoginFragment() { refreshToken = null ) - loginViewModel.handle(LoginAction.SsoLoginSuccess(credentials)) + loginViewModel.handle(LoginAction.WebLoginSuccess(credentials)) } } } @@ -291,8 +291,8 @@ class LoginSsoFallbackFragment @Inject constructor() : AbstractLoginFragment() { } override fun onBackPressed(): Boolean { - return if (login_sso_fallback_webview.canGoBack()) { - login_sso_fallback_webview.goBack() + return if (loginWebWebView.canGoBack()) { + loginWebWebView.goBack() true } else { super.onBackPressed() diff --git a/vector/src/main/res/layout/fragment_login_sso_fallback.xml b/vector/src/main/res/layout/fragment_login_web.xml similarity index 86% rename from vector/src/main/res/layout/fragment_login_sso_fallback.xml rename to vector/src/main/res/layout/fragment_login_web.xml index e83680d2cd..6383b9f137 100644 --- a/vector/src/main/res/layout/fragment_login_sso_fallback.xml +++ b/vector/src/main/res/layout/fragment_login_web.xml @@ -6,7 +6,7 @@ android:orientation="vertical"> diff --git a/vector/src/main/res/values/strings_riotX.xml b/vector/src/main/res/values/strings_riotX.xml index c64b020eba..17bb99ac4a 100644 --- a/vector/src/main/res/values/strings_riotX.xml +++ b/vector/src/main/res/values/strings_riotX.xml @@ -53,5 +53,6 @@ Enter the address of a server or a Riot you want to connect to An error occurred when loading the page: %1$s (%2$d) + The application is not able to signin to this homeserver. The homeserver supports the following signin type(s): %1$s.\n\nDo you want to signin using a web client?