Login screens: login and registration fallback
This commit is contained in:
parent
a1aa16715d
commit
b7bfb20a2e
@ -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
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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<String>) : LoginMode()
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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()
|
@ -6,7 +6,7 @@
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/login_sso_fallback_toolbar"
|
||||
android:id="@+id/loginWebToolbar"
|
||||
style="@style/VectorToolbarStyle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
@ -15,7 +15,7 @@
|
||||
tools:title="@string/auth_login" />
|
||||
|
||||
<WebView
|
||||
android:id="@+id/login_sso_fallback_webview"
|
||||
android:id="@+id/loginWebWebView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
@ -53,5 +53,6 @@
|
||||
<string name="login_server_url_form_other_notice">Enter the address of a server or a Riot you want to connect to</string>
|
||||
|
||||
<string name="login_sso_error_message">An error occurred when loading the page: %1$s (%2$d)</string>
|
||||
<string name="login_mode_not_supported">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?</string>
|
||||
|
||||
</resources>
|
||||
|
Loading…
x
Reference in New Issue
Block a user