Refactor + fix sso in LoginFragment

This commit is contained in:
Valere 2020-12-03 15:37:10 +01:00 committed by Benoit Marty
parent 351793d456
commit 42d1bf57f6
3 changed files with 103 additions and 83 deletions

View File

@ -0,0 +1,93 @@
/*
* Copyright (c) 2020 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.app.features.login
import android.content.ComponentName
import android.net.Uri
import androidx.browser.customtabs.CustomTabsClient
import androidx.browser.customtabs.CustomTabsServiceConnection
import androidx.browser.customtabs.CustomTabsSession
import com.airbnb.mvrx.withState
import im.vector.app.core.utils.openUrlInChromeCustomTab
abstract class AbstractSSOLoginFragment : AbstractLoginFragment() {
// For sso
private var customTabsServiceConnection: CustomTabsServiceConnection? = null
private var customTabsClient: CustomTabsClient? = null
private var customTabsSession: CustomTabsSession? = null
override fun onStart() {
super.onStart()
val hasSSO = withState(loginViewModel) { it.loginMode.hasSso() }
if (hasSSO) {
val packageName = CustomTabsClient.getPackageName(requireContext(), null)
// packageName can be null if there are 0 or several CustomTabs compatible browsers installed on the device
if (packageName != null) {
customTabsServiceConnection = object : CustomTabsServiceConnection() {
override fun onCustomTabsServiceConnected(name: ComponentName, client: CustomTabsClient) {
customTabsClient = client
.also { it.warmup(0L) }
prefetchIfNeeded()
}
override fun onServiceDisconnected(name: ComponentName?) {
}
}
.also {
CustomTabsClient.bindCustomTabsService(
requireContext(),
// Despite the API, packageName cannot be null
packageName,
it
)
}
}
}
}
override fun onStop() {
super.onStop()
val hasSSO = withState(loginViewModel) { it.loginMode.hasSso() }
if (hasSSO) {
customTabsServiceConnection?.let { requireContext().unbindService(it) }
customTabsServiceConnection = null
}
}
private fun prefetchUrl(url: String) {
if (customTabsSession == null) {
customTabsSession = customTabsClient?.newSession(null)
}
customTabsSession?.mayLaunchUrl(Uri.parse(url), null, null)
}
fun openInCustomTab(ssoUrl: String) {
openUrlInChromeCustomTab(requireContext(), customTabsSession, ssoUrl)
}
private fun prefetchIfNeeded() {
withState(loginViewModel) { state ->
if (state.loginMode.hasSso() && state.loginMode.ssoProviders().isNullOrEmpty()) {
// in this case we can prefetch (not other cases for privacy concerns)
prefetchUrl(state.getSsoUrl(null))
}
}
}
}

View File

@ -51,7 +51,7 @@ import javax.inject.Inject
* In signup mode:
* - the user is asked for login and password
*/
class LoginFragment @Inject constructor() : AbstractLoginFragment() {
class LoginFragment @Inject constructor() : AbstractSSOLoginFragment() {
private var passwordShown = false
private var isSignupMode = false
@ -176,6 +176,11 @@ class LoginFragment @Inject constructor() : AbstractLoginFragment() {
if (state.loginMode is LoginMode.SsoAndPassword) {
loginSocialLoginContainer.isVisible = true
loginSocialLoginButtons.identityProviders = state.loginMode.identityProviders
loginSocialLoginButtons.listener = object : SocialLoginButtonsView.InteractionListener {
override fun onProviderSelected(id: String?) {
openInCustomTab(state.getSsoUrl(id))
}
}
} else {
loginSocialLoginContainer.isVisible = false
loginSocialLoginButtons.identityProviders = null

View File

@ -16,31 +16,18 @@
package im.vector.app.features.login
import android.content.ComponentName
import android.net.Uri
import androidx.browser.customtabs.CustomTabsClient
import androidx.browser.customtabs.CustomTabsServiceConnection
import androidx.browser.customtabs.CustomTabsSession
import androidx.core.view.isVisible
import butterknife.OnClick
import com.airbnb.mvrx.withState
import im.vector.app.R
import im.vector.app.core.extensions.toReducedUrl
import im.vector.app.core.utils.openUrlInChromeCustomTab
import kotlinx.android.synthetic.main.fragment_login_signup_signin_selection.*
import javax.inject.Inject
/**
* In this screen, the user is asked to sign up or to sign in to the homeserver
*/
class LoginSignUpSignInSelectionFragment @Inject constructor() : AbstractLoginFragment() {
// Map of sso urls by providers if any
private var ssoUrls = emptyMap<String?, String>().toMutableMap()
private var customTabsServiceConnection: CustomTabsServiceConnection? = null
private var customTabsClient: CustomTabsClient? = null
private var customTabsSession: CustomTabsSession? = null
class LoginSignUpSignInSelectionFragment @Inject constructor() : AbstractSSOLoginFragment() {
override fun getLayoutResId() = R.layout.fragment_login_signup_signin_selection
@ -73,7 +60,8 @@ class LoginSignUpSignInSelectionFragment @Inject constructor() : AbstractLoginFr
loginSignupSigninSocialLoginButtons.identityProviders = identityProviders
loginSignupSigninSocialLoginButtons.listener = object : SocialLoginButtonsView.InteractionListener {
override fun onProviderSelected(id: String?) {
ssoUrls[id]?.let { openUrlInChromeCustomTab(requireContext(), customTabsSession, it) }
val url = withState(loginViewModel) { it.getSsoUrl(id) }
openInCustomTab(url)
}
}
}
@ -85,72 +73,6 @@ class LoginSignUpSignInSelectionFragment @Inject constructor() : AbstractLoginFr
}
}
override fun onStart() {
super.onStart()
val hasSSO = withState(loginViewModel) { it.loginMode.hasSso() }
if (hasSSO) {
val packageName = CustomTabsClient.getPackageName(requireContext(), null)
// packageName can be null if there are 0 or several CustomTabs compatible browsers installed on the device
if (packageName != null) {
customTabsServiceConnection = object : CustomTabsServiceConnection() {
override fun onCustomTabsServiceConnected(name: ComponentName, client: CustomTabsClient) {
customTabsClient = client
.also { it.warmup(0L) }
// prefetch urls
prefetchSsoUrls()
}
override fun onServiceDisconnected(name: ComponentName?) {
}
}
.also {
CustomTabsClient.bindCustomTabsService(
requireContext(),
// Despite the API, packageName cannot be null
packageName,
it
)
}
}
}
}
override fun onStop() {
super.onStop()
val hasSSO = withState(loginViewModel) { it.loginMode.hasSso() }
if (hasSSO) {
customTabsServiceConnection?.let { requireContext().unbindService(it) }
customTabsServiceConnection = null
}
}
private fun prefetchSsoUrls() = withState(loginViewModel) { state ->
val providers = state.loginMode.ssoProviders()
if (providers.isNullOrEmpty()) {
state.getSsoUrl(null).let {
ssoUrls[null] = it
prefetchUrl(it)
}
} else {
providers.forEach { identityProvider ->
state.getSsoUrl(identityProvider.id).let {
ssoUrls[identityProvider.id] = it
// we don't prefetch for privacy reasons
}
}
}
}
private fun prefetchUrl(url: String) {
if (customTabsSession == null) {
customTabsSession = customTabsClient?.newSession(null)
}
customTabsSession?.mayLaunchUrl(Uri.parse(url), null, null)
}
private fun setupButtons(state: LoginViewState) {
when (state.loginMode) {
is LoginMode.Sso -> {
@ -168,7 +90,7 @@ class LoginSignUpSignInSelectionFragment @Inject constructor() : AbstractLoginFr
@OnClick(R.id.loginSignupSigninSubmit)
fun submit() = withState(loginViewModel) { state ->
if (state.loginMode is LoginMode.Sso) {
ssoUrls[null]?.let { openUrlInChromeCustomTab(requireContext(), customTabsSession, it) }
openInCustomTab(state.getSsoUrl(null))
} else {
loginViewModel.handle(LoginAction.UpdateSignMode(SignMode.SignUp))
}