diff --git a/build.gradle b/build.gradle
index 85b155e403..4449e4366d 100644
--- a/build.gradle
+++ b/build.gradle
@@ -29,7 +29,7 @@ buildscript {
classpath 'org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:4.0.0.2929'
classpath 'com.google.android.gms:oss-licenses-plugin:0.10.6'
classpath "com.likethesalad.android:stem-plugin:2.9.0"
- classpath 'org.owasp:dependency-check-gradle:8.2.1'
+ classpath 'org.owasp:dependency-check-gradle:11.1.0'
classpath "org.jetbrains.dokka:dokka-gradle-plugin:1.8.10"
classpath "org.jetbrains.kotlinx:kotlinx-knit:0.4.0"
classpath 'com.jakewharton:butterknife-gradle-plugin:10.2.3'
diff --git a/changelog.d/1104.feature b/changelog.d/1104.feature
new file mode 100644
index 0000000000..e9aa5e0e82
--- /dev/null
+++ b/changelog.d/1104.feature
@@ -0,0 +1 @@
+Connexion des utilisateurs avec ProConnect
\ No newline at end of file
diff --git a/library/ui-strings/src/main/res/values-fr/strings.xml b/library/ui-strings/src/main/res/values-fr/strings.xml
index 521c4d5328..8bb1be8c41 100644
--- a/library/ui-strings/src/main/res/values-fr/strings.xml
+++ b/library/ui-strings/src/main/res/values-fr/strings.xml
@@ -251,7 +251,7 @@
Nouvel appel vidéo
Envoyer des fichiers
Prendre une photo ou une vidéo
- Je me connecte
+ Se connecter
Valider
Nom d’utilisateur et/ou mot de passe incorrect
Mot de passe oublié \?
@@ -1054,7 +1054,7 @@
S’authentifier sur %1$s
Je m’inscris
Je me connecte
- Continuer avec l’authentification unique
+ Continuer avec %s
Adresse Element Matrix Services
Adresse
Hébergement privé pour les organisations
diff --git a/library/ui-strings/src/main/res/values-fr/strings_tchap.xml b/library/ui-strings/src/main/res/values-fr/strings_tchap.xml
index 87e059a5ad..a61fdf2f97 100644
--- a/library/ui-strings/src/main/res/values-fr/strings_tchap.xml
+++ b/library/ui-strings/src/main/res/values-fr/strings_tchap.xml
@@ -8,6 +8,7 @@
Les mots de passe ne correspondent pas
+ %s est désactivé pour votre domaine
Inviter par e\u2011mail
@@ -76,9 +77,11 @@
Nouvel e\u2011mail envoyé
- Je n’ai pas\nde compte
- J’ai un compte
- Adresse email
+ Se connecter par mot de passe
+ La messagerie instantanée du secteur public
+ ➜ Qu’est-ce que %s ?
+ ➜ Est-ce que %s est activé pour mon administration ?
+ Adresse mail professionnelle
Utilisez votre adresse professionnelle
Votre mot de passe doit contenir au moins 8 caractères, avec au moins un caractère de chaque type : majuscule, minuscule, chiffre, caractère spécial.
Confirmer le mot de passe
diff --git a/library/ui-strings/src/main/res/values/strings.xml b/library/ui-strings/src/main/res/values/strings.xml
index d85ec22e45..350dd852dc 100644
--- a/library/ui-strings/src/main/res/values/strings.xml
+++ b/library/ui-strings/src/main/res/values/strings.xml
@@ -2129,7 +2129,7 @@
Sign in to %1$s
Sign Up
Sign In
- Continue with SSO
+ Continue with %s
Clear history
Element Matrix Services Address
diff --git a/library/ui-strings/src/main/res/values/strings_tchap.xml b/library/ui-strings/src/main/res/values/strings_tchap.xml
index 24df4d73ff..33130f10af 100644
--- a/library/ui-strings/src/main/res/values/strings_tchap.xml
+++ b/library/ui-strings/src/main/res/values/strings_tchap.xml
@@ -8,6 +8,7 @@
Passwords don’t match
+ %s is disabled for your domain
Send an invitation
@@ -76,9 +77,11 @@
Email sent
- I do not have\nan account
- I already have\nan account
- Email
+ Login by password
+ Instant messaging of public sector
+ ➜ What is %s ?
+ Is %s available for my organization ?
+ Professional email
Use your business address
Your password must include a lower-case letter, an upper-case letter, a number and a symbol and be at a minimum 8 characters in length.
Password confirmation
diff --git a/library/ui-styles/src/main/res/drawable/ic_tchap_proconnect.xml b/library/ui-styles/src/main/res/drawable/ic_tchap_proconnect.xml
new file mode 100644
index 0000000000..225b46e561
--- /dev/null
+++ b/library/ui-styles/src/main/res/drawable/ic_tchap_proconnect.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/registration/RegistrationFlowResponse.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/registration/RegistrationFlowResponse.kt
index 98542d2086..a866ff63b1 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/registration/RegistrationFlowResponse.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/auth/registration/RegistrationFlowResponse.kt
@@ -111,5 +111,15 @@ fun RegistrationFlowResponse.toFlowResult(): FlowResult {
fun RegistrationFlowResponse.nextUncompletedStage(flowIndex: Int = 0): String? {
val completed = completedStages ?: emptyList()
- return flows?.getOrNull(flowIndex)?.stages?.firstOrNull { completed.contains(it).not() }
+ val flows = flows ?: return null
+
+ // TCHAP return LoginFlowTypes.SSO if SSO type is supported by UIA.
+ if (flowIndex == 0) {
+ flows.forEach {
+ if (!it.stages.isNullOrEmpty() && LoginFlowTypes.SSO !in completed && LoginFlowTypes.SSO in it.stages) {
+ return LoginFlowTypes.SSO
+ }
+ }
+ }
+ return flows.getOrNull(flowIndex)?.stages?.firstOrNull { it !in completed }
}
diff --git a/towncrier.toml b/towncrier.toml
index 8b5f9ff767..967a53a495 100644
--- a/towncrier.toml
+++ b/towncrier.toml
@@ -1,5 +1,5 @@
[tool.towncrier]
- version = "2.15.1"
+ version = "2.16.0"
directory = "changelog.d"
filename = "TCHAP_CHANGES.md"
name = "Changes in Tchap"
diff --git a/vector-app/build.gradle b/vector-app/build.gradle
index 228fde72da..4919d244ef 100644
--- a/vector-app/build.gradle
+++ b/vector-app/build.gradle
@@ -33,11 +33,11 @@ knit {
// Note: 2 digits max for each value
ext.versionMajor = 2
-ext.versionMinor = 15
+ext.versionMinor = 16
// Note: even values are reserved for regular release, odd values for hotfix release.
// When creating a hotfix, you should decrease the value, since the current value
// is the value for the next regular release.
-ext.versionPatch = 1
+ext.versionPatch = 0
static def getGitTimestamp() {
def cmd = 'git show -s --format=%ct'
diff --git a/vector-app/src/debug/java/im/vector/app/features/debug/features/DebugVectorFeatures.kt b/vector-app/src/debug/java/im/vector/app/features/debug/features/DebugVectorFeatures.kt
index 6a3b20899a..f0fba975ae 100644
--- a/vector-app/src/debug/java/im/vector/app/features/debug/features/DebugVectorFeatures.kt
+++ b/vector-app/src/debug/java/im/vector/app/features/debug/features/DebugVectorFeatures.kt
@@ -52,6 +52,8 @@ class DebugVectorFeatures(
override fun tchapIsSecureBackupRequired() = vectorFeatures.tchapIsSecureBackupRequired()
+ override fun tchapIsSSOEnabled() = vectorFeatures.tchapIsSSOEnabled()
+
override fun onboardingVariant(): OnboardingVariant {
return readPreferences().getEnum() ?: vectorFeatures.onboardingVariant()
}
diff --git a/vector-config/src/btchap/res/values/config-features.xml b/vector-config/src/btchap/res/values/config-features.xml
index 60e0cb1865..c890574e82 100755
--- a/vector-config/src/btchap/res/values/config-features.xml
+++ b/vector-config/src/btchap/res/values/config-features.xml
@@ -4,6 +4,7 @@
true
false
true
+ true
diff --git a/vector-config/src/devTchap/res/values/config-features.xml b/vector-config/src/devTchap/res/values/config-features.xml
index 60e0cb1865..c890574e82 100755
--- a/vector-config/src/devTchap/res/values/config-features.xml
+++ b/vector-config/src/devTchap/res/values/config-features.xml
@@ -4,6 +4,7 @@
true
false
true
+ true
diff --git a/vector-config/src/tchap/res/values/config-features.xml b/vector-config/src/tchap/res/values/config-features.xml
index d5c1f74fb4..612c7a050d 100755
--- a/vector-config/src/tchap/res/values/config-features.xml
+++ b/vector-config/src/tchap/res/values/config-features.xml
@@ -4,6 +4,7 @@
true
false
false
+ true
diff --git a/vector/src/main/AndroidManifest.xml b/vector/src/main/AndroidManifest.xml
index a15f56a045..c4b8e3e4ce 100644
--- a/vector/src/main/AndroidManifest.xml
+++ b/vector/src/main/AndroidManifest.xml
@@ -35,7 +35,7 @@
-
+
@@ -60,7 +60,7 @@
-
@@ -102,7 +102,7 @@
android:name="android.max_aspect"
android:value="9.9" />
-
+
@@ -120,16 +120,16 @@
android:theme="@style/Theme.Vector.Black.Transparent">
-
-
+
+
-
-
+
+
-
-
-
-
+
+
-
+
diff --git a/vector/src/main/java/im/vector/app/features/VectorFeatures.kt b/vector/src/main/java/im/vector/app/features/VectorFeatures.kt
index f0b8a6c060..64029ac065 100644
--- a/vector/src/main/java/im/vector/app/features/VectorFeatures.kt
+++ b/vector/src/main/java/im/vector/app/features/VectorFeatures.kt
@@ -32,6 +32,7 @@ interface VectorFeatures {
fun tchapIsThreadEnabled(): Boolean
fun tchapIsLabsVisible(domain: String): Boolean
fun tchapIsSecureBackupRequired(): Boolean
+ fun tchapIsSSOEnabled(): Boolean
fun onboardingVariant(): OnboardingVariant
fun isOnboardingAlreadyHaveAccountSplashEnabled(): Boolean
fun isOnboardingSplashCarouselEnabled(): Boolean
@@ -72,6 +73,7 @@ class DefaultVectorFeatures @Inject constructor(
override fun tchapIsLabsVisible(domain: String) = booleanProvider.getBoolean(im.vector.app.config.R.bool.settings_root_labs_visible) ||
domain == appNameProvider.getAppName()
override fun tchapIsSecureBackupRequired() = booleanProvider.getBoolean(im.vector.app.config.R.bool.tchap_is_secure_backup_required)
+ override fun tchapIsSSOEnabled() = booleanProvider.getBoolean(im.vector.app.config.R.bool.tchap_is_sso_enabled)
override fun onboardingVariant() = Config.ONBOARDING_VARIANT
override fun isOnboardingAlreadyHaveAccountSplashEnabled() = true
override fun isOnboardingSplashCarouselEnabled() = false // TCHAP no carousel
diff --git a/vector/src/main/java/im/vector/app/features/login/LoginActivity.kt b/vector/src/main/java/im/vector/app/features/login/LoginActivity.kt
index 634a537de5..af5887a96a 100644
--- a/vector/src/main/java/im/vector/app/features/login/LoginActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/login/LoginActivity.kt
@@ -235,6 +235,7 @@ open class LoginActivity : VectorBaseActivity(), UnlockedA
}
private fun inferAuthDescription(loginViewState: LoginViewState) = when (loginViewState.signMode) {
+ SignMode.TchapSignInWithSSO,
SignMode.TchapSignUp,
SignMode.TchapSignIn -> error("developer error")
SignMode.Unknown -> null
@@ -271,6 +272,7 @@ open class LoginActivity : VectorBaseActivity(), UnlockedA
private fun onSignModeSelected(loginViewEvents: LoginViewEvents.OnSignModeSelected) = withState(loginViewModel) { state ->
// state.signMode could not be ready yet. So use value from the ViewEvent
when (loginViewEvents.signMode) {
+ SignMode.TchapSignInWithSSO,
SignMode.TchapSignUp,
SignMode.TchapSignIn -> error("developer error")
SignMode.Unknown -> error("Sign mode has to be set before calling this method")
diff --git a/vector/src/main/java/im/vector/app/features/login/LoginFragment.kt b/vector/src/main/java/im/vector/app/features/login/LoginFragment.kt
index 6cb419adab..80cd5f289f 100644
--- a/vector/src/main/java/im/vector/app/features/login/LoginFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/login/LoginFragment.kt
@@ -89,6 +89,7 @@ class LoginFragment :
private fun setupAutoFill(state: LoginViewState) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
when (state.signMode) {
+ SignMode.TchapSignInWithSSO,
SignMode.TchapSignUp,
SignMode.TchapSignIn,
SignMode.Unknown -> error("developer error")
@@ -106,6 +107,7 @@ class LoginFragment :
}
private fun ssoMode(state: LoginViewState) = when (state.signMode) {
+ SignMode.TchapSignInWithSSO,
SignMode.TchapSignUp,
SignMode.TchapSignIn,
SignMode.Unknown -> error("developer error")
@@ -161,6 +163,7 @@ class LoginFragment :
private fun setupUi(state: LoginViewState) {
views.loginFieldTil.hint = getString(
when (state.signMode) {
+ SignMode.TchapSignInWithSSO,
SignMode.TchapSignUp,
SignMode.TchapSignIn,
SignMode.Unknown -> error("developer error")
@@ -178,6 +181,7 @@ class LoginFragment :
views.loginPasswordNotice.isVisible = true
} else {
val resId = when (state.signMode) {
+ SignMode.TchapSignInWithSSO,
SignMode.TchapSignUp,
SignMode.TchapSignIn,
SignMode.Unknown -> error("developer error")
@@ -231,6 +235,7 @@ class LoginFragment :
views.loginSubmit.text = getString(
when (state.signMode) {
+ SignMode.TchapSignInWithSSO,
SignMode.TchapSignUp,
SignMode.TchapSignIn,
SignMode.Unknown -> error("developer error")
diff --git a/vector/src/main/java/im/vector/app/features/login/LoginViewModel.kt b/vector/src/main/java/im/vector/app/features/login/LoginViewModel.kt
index 4f5f2bd882..965a05b4df 100644
--- a/vector/src/main/java/im/vector/app/features/login/LoginViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/login/LoginViewModel.kt
@@ -431,6 +431,7 @@ class LoginViewModel @AssistedInject constructor(
}
when (action.signMode) {
+ SignMode.TchapSignInWithSSO,
SignMode.TchapSignUp,
SignMode.TchapSignIn -> error("developer error")
SignMode.SignUp -> startRegistrationFlow()
@@ -570,6 +571,7 @@ class LoginViewModel @AssistedInject constructor(
private fun handleLoginOrRegister(action: LoginAction.LoginOrRegister) = withState { state ->
when (state.signMode) {
+ SignMode.TchapSignInWithSSO,
SignMode.TchapSignIn,
SignMode.TchapSignUp,
SignMode.Unknown -> error("Developer error, invalid sign mode")
diff --git a/vector/src/main/java/im/vector/app/features/login/SSORedirectRouterActivity.kt b/vector/src/main/java/im/vector/app/features/login/SSORedirectRouterActivity.kt
index 19c549fd45..4704048612 100644
--- a/vector/src/main/java/im/vector/app/features/login/SSORedirectRouterActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/login/SSORedirectRouterActivity.kt
@@ -35,6 +35,6 @@ class SSORedirectRouterActivity : AppCompatActivity() {
companion object {
// Note that the domain can be displayed to the user for confirmation that he trusts it. So use a human readable string
- const val VECTOR_REDIRECT_URL = "element://connect"
+ const val VECTOR_REDIRECT_URL = "tchap://connect"
}
}
diff --git a/vector/src/main/java/im/vector/app/features/login/SignMode.kt b/vector/src/main/java/im/vector/app/features/login/SignMode.kt
index 3438dbe41e..336a0a7eef 100644
--- a/vector/src/main/java/im/vector/app/features/login/SignMode.kt
+++ b/vector/src/main/java/im/vector/app/features/login/SignMode.kt
@@ -23,6 +23,9 @@ enum class SignMode {
// TCHAP Account creation
TchapSignUp,
+ // TCHAP Login with SSO
+ TchapSignInWithSSO,
+
Unknown,
// Account creation
diff --git a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingAction.kt b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingAction.kt
index 6497a84f7c..27b2ce5930 100644
--- a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingAction.kt
+++ b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingAction.kt
@@ -56,6 +56,7 @@ sealed interface OnboardingAction : VectorViewModelAction {
data class Registration(val userId: String) : UserNameEnteredAction
data class Login(val userId: String) : UserNameEnteredAction
}
+ data class LoginWithSSO(val email: String) : OnboardingAction
sealed interface AuthenticateAction : OnboardingAction {
data class TchapRegister(val email: String, val password: String, val initialDeviceName: String) : AuthenticateAction
data class TchapLogin(val email: String, val password: String, val initialDeviceName: String) : AuthenticateAction
diff --git a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt
index 9169e9686e..62afb5be84 100644
--- a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewModel.kt
@@ -179,6 +179,7 @@ class OnboardingViewModel @AssistedInject constructor(
override fun handle(action: OnboardingAction) {
when (action) {
+ is OnboardingAction.LoginWithSSO -> tchap.handleLoginWithSSO(action)
is OnboardingAction.SplashAction -> handleSplashAction(action)
is OnboardingAction.UpdateUseCase -> handleUpdateUseCase(action)
OnboardingAction.ResetUseCase -> resetUseCase()
@@ -282,8 +283,11 @@ class OnboardingViewModel @AssistedInject constructor(
private fun continueToPageAfterSplash(onboardingFlow: OnboardingFlow) {
when (onboardingFlow) {
+ // TCHAP login with SSO
+ OnboardingFlow.TchapSignInWithSSO -> handleUpdateSignMode(OnboardingAction.UpdateSignMode(SignMode.TchapSignInWithSSO))
OnboardingFlow.SignUp -> {
handleUpdateSignMode(OnboardingAction.UpdateSignMode(SignMode.TchapSignUp))
+ // TCHAP disable homeserver selection
// _viewEvents.post(
// if (vectorFeatures.isOnboardingUseCaseEnabled()) {
// OnboardingViewEvents.OpenUseCaseSelection
@@ -483,6 +487,7 @@ class OnboardingViewModel @AssistedInject constructor(
private fun handleUpdateSignMode(action: OnboardingAction.UpdateSignMode) {
updateSignMode(action.signMode)
when (action.signMode) {
+ SignMode.TchapSignInWithSSO -> _viewEvents.post(OnboardingViewEvents.OnSignModeSelected(SignMode.TchapSignInWithSSO))
SignMode.TchapSignIn -> _viewEvents.post(OnboardingViewEvents.OnSignModeSelected(SignMode.TchapSignIn))
SignMode.TchapSignUp -> _viewEvents.post(OnboardingViewEvents.OnSignModeSelected(SignMode.TchapSignUp))
SignMode.SignUp -> handleRegisterAction(RegisterAction.StartRegistration)
@@ -808,6 +813,7 @@ class OnboardingViewModel @AssistedInject constructor(
updateServerSelection(config, serverTypeOverride, authResult)
if (authResult.selectedHomeserver.preferredLoginMode.supportsSignModeScreen()) {
when (awaitState().onboardingFlow) {
+ OnboardingFlow.TchapSignInWithSSO -> error("developer error")
OnboardingFlow.SignIn -> {
updateSignMode(SignMode.SignIn)
when (vectorFeatures.isOnboardingCombinedLoginEnabled()) {
@@ -835,6 +841,7 @@ class OnboardingViewModel @AssistedInject constructor(
updateServerSelection(config, serverTypeOverride, authResult)
_viewEvents.post(OnboardingViewEvents.OnHomeserverEdited)
}
+ OnboardingFlow.TchapSignInWithSSO,
OnboardingFlow.SignIn -> {
updateServerSelection(config, serverTypeOverride, authResult)
_viewEvents.post(OnboardingViewEvents.OnHomeserverEdited)
@@ -1003,6 +1010,10 @@ class OnboardingViewModel @AssistedInject constructor(
}
}
+ fun handleLoginWithSSO(action: OnboardingAction.LoginWithSSO) {
+ startTchapAuthenticationFlow(action.email) {}
+ }
+
fun startResetPasswordFlow(email: String, onSuccess: () -> Unit) {
startTchapAuthenticationFlow(email) {
this@OnboardingViewModel.startResetPasswordFlow(email, onSuccess)
diff --git a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewState.kt b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewState.kt
index 58b28ac4e4..983fb18885 100644
--- a/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewState.kt
+++ b/vector/src/main/java/im/vector/app/features/onboarding/OnboardingViewState.kt
@@ -64,6 +64,7 @@ data class OnboardingViewState(
) : MavericksState
enum class OnboardingFlow {
+ TchapSignInWithSSO,
SignIn,
SignUp,
SignInSignUp
diff --git a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/AbstractFtueAuthFragment.kt b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/AbstractFtueAuthFragment.kt
index 7fa79535af..42b43c9463 100644
--- a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/AbstractFtueAuthFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/AbstractFtueAuthFragment.kt
@@ -31,6 +31,7 @@ import im.vector.app.features.onboarding.OnboardingAction
import im.vector.app.features.onboarding.OnboardingViewEvents
import im.vector.app.features.onboarding.OnboardingViewModel
import im.vector.app.features.onboarding.OnboardingViewState
+import im.vector.app.features.settings.VectorSettingsUrls
import im.vector.lib.strings.CommonStrings
import kotlinx.coroutines.CancellationException
@@ -167,4 +168,10 @@ abstract class AbstractFtueAuthFragment : VectorBaseFragment() {
+ @Inject lateinit var buildMeta: BuildMeta
+ @Inject lateinit var vectorFeatures: VectorFeatures
+
private val tchap = Tchap()
private var isSignupMode = false
@@ -111,7 +120,11 @@ class FtueAuthLoginFragment :
views.loginField.setAutofillHints(HintConstants.AUTOFILL_HINT_NEW_USERNAME)
views.passwordField.setAutofillHints(HintConstants.AUTOFILL_HINT_NEW_PASSWORD)
}
- SignMode.TchapSignIn,
+ SignMode.TchapSignInWithSSO,
+ SignMode.TchapSignIn -> {
+ views.loginField.setAutofillHints(HintConstants.AUTOFILL_HINT_EMAIL_ADDRESS)
+ views.passwordField.setAutofillHints(HintConstants.AUTOFILL_HINT_NEW_PASSWORD)
+ }
SignMode.SignIn,
SignMode.SignInWithMatrixId -> {
views.loginField.setAutofillHints(HintConstants.AUTOFILL_HINT_USERNAME)
@@ -125,6 +138,7 @@ class FtueAuthLoginFragment :
SignMode.Unknown -> error("developer error")
SignMode.TchapSignUp,
SignMode.SignUp -> SocialLoginButtonsView.Mode.MODE_SIGN_UP
+ SignMode.TchapSignInWithSSO,
SignMode.TchapSignIn,
SignMode.SignIn,
SignMode.SignInWithMatrixId -> SocialLoginButtonsView.Mode.MODE_SIGN_IN
@@ -148,7 +162,7 @@ class FtueAuthLoginFragment :
views.loginFieldTil.error = getString(CommonStrings.error_forbidden_digits_only_username)
error++
}
- if (password.isEmpty()) {
+ if (password.isEmpty() && state.signMode != SignMode.TchapSignInWithSSO) {
views.passwordFieldTil.error = getString(
if (isSignupMode) {
CommonStrings.error_empty_field_choose_password
@@ -166,8 +180,12 @@ class FtueAuthLoginFragment :
}
if (error == 0) {
- val initialDeviceName = getString(CommonStrings.login_default_session_public_name)
- viewModel.handle(state.signMode.toAuthenticateAction(login, password, initialDeviceName))
+ if (state.signMode != SignMode.TchapSignInWithSSO) {
+ val initialDeviceName = getString(CommonStrings.login_default_session_public_name)
+ viewModel.handle(state.signMode.toAuthenticateAction(login, password, initialDeviceName))
+ } else {
+ viewModel.handle(OnboardingAction.LoginWithSSO(login))
+ }
}
}
}
@@ -186,6 +204,7 @@ class FtueAuthLoginFragment :
SignMode.SignUp -> CommonStrings.login_signup_username_hint
SignMode.SignIn -> CommonStrings.login_signin_username_hint
SignMode.TchapSignUp,
+ SignMode.TchapSignInWithSSO,
SignMode.TchapSignIn -> CommonStrings.tchap_connection_email
SignMode.SignInWithMatrixId -> CommonStrings.login_signin_matrix_id_hint
}
@@ -199,12 +218,12 @@ class FtueAuthLoginFragment :
views.loginPasswordNotice.isVisible = true
} else {
val resId = when (state.signMode) {
- SignMode.Unknown -> error("developer error")
SignMode.TchapSignUp,
SignMode.SignUp -> CommonStrings.login_signup_to
SignMode.TchapSignIn -> CommonStrings.login_connect_to
SignMode.SignIn -> CommonStrings.login_connect_to
- SignMode.SignInWithMatrixId -> CommonStrings.login_connect_to
+ SignMode.TchapSignInWithSSO -> CommonStrings.login_social_signin_with
+ else -> error("developer error")
}
when (state.serverType) {
@@ -228,14 +247,17 @@ class FtueAuthLoginFragment :
ServerType.Unknown -> {
// TCHAP Hide views if empty
views.loginServerIcon.isVisible = false
- views.loginTitle.isVisible = false
views.loginNotice.isVisible = false
+ if (state.signMode == SignMode.TchapSignInWithSSO) {
+ views.loginTitle.text = getString(resId, TCHAP_SSO_PROVIDER)
+ } else {
+ views.loginTitle.text = getString(resId, buildMeta.applicationName)
+ }
}
}
views.loginPasswordNotice.isVisible = false
if (state.selectedHomeserver.preferredLoginMode is LoginMode.SsoAndPassword) {
- views.loginSocialLoginContainer.isVisible = true
views.loginSocialLoginButtons.render(state.selectedHomeserver.preferredLoginMode, ssoMode(state)) { provider ->
viewModel.fetchSsoUrl(
redirectUrl = SSORedirectRouterActivity.VECTOR_REDIRECT_URL,
@@ -246,7 +268,6 @@ class FtueAuthLoginFragment :
?.let { openInCustomTab(it) }
}
} else {
- views.loginSocialLoginContainer.isVisible = false
views.loginSocialLoginButtons.ssoIdentityProviders = null
}
}
@@ -260,20 +281,21 @@ class FtueAuthLoginFragment :
SignMode.Unknown -> error("developer error")
SignMode.TchapSignUp,
SignMode.SignUp -> CommonStrings.login_signup_submit
+ SignMode.TchapSignInWithSSO -> CommonStrings.login_signin_sso
SignMode.TchapSignIn,
SignMode.SignIn,
SignMode.SignInWithMatrixId -> CommonStrings.login_signin
- }
+ }, TCHAP_SSO_PROVIDER
)
}
- private fun setupSubmitButton() {
+ private fun setupSubmitButton() = withState(viewModel) { state ->
views.loginSubmit.setOnClickListener { submit() }
combine(
views.loginField.textChanges().map { it.trim().isNotEmpty() },
views.passwordField.textChanges().map { it.isNotEmpty() }
) { isLoginNotEmpty, isPasswordNotEmpty ->
- isLoginNotEmpty && isPasswordNotEmpty
+ (isLoginNotEmpty && isPasswordNotEmpty) || state.signMode == SignMode.TchapSignInWithSSO && views.loginField.text?.isEmail().orFalse()
}
.onEach {
views.loginFieldTil.error = null
@@ -327,6 +349,7 @@ class FtueAuthLoginFragment :
tchap.setupUi(state)
setupAutoFill(state)
setupButtons(state)
+ tchap.tryLoginSSO(state)
if (state.isLoading) {
// Ensure password is hidden
@@ -343,15 +366,61 @@ class FtueAuthLoginFragment :
fun setupUi(state: OnboardingViewState) {
this@FtueAuthLoginFragment.setupUi(state) // call "super" method
- if (state.signMode == SignMode.TchapSignUp) {
- views.loginFieldTil.isHelperTextEnabled = true
- views.passwordFieldTil.isHelperTextEnabled = true
- views.tchapPasswordConfirmationFieldTil.isVisible = true
- } else {
- views.loginFieldTil.isHelperTextEnabled = false
- views.passwordFieldTil.isHelperTextEnabled = false
- views.tchapPasswordConfirmationFieldTil.isVisible = false
- views.passwordField.imeOptions = EditorInfo.IME_ACTION_DONE
+
+ val isSignUpMode = state.signMode == SignMode.TchapSignUp
+ views.loginFieldTil.isHelperTextEnabled = isSignUpMode
+ views.passwordFieldTil.isHelperTextEnabled = isSignUpMode
+ views.tchapPasswordConfirmationFieldTil.isVisible = isSignUpMode
+ views.loginSocialLoginContainer.isVisible = isSignUpMode && vectorFeatures.tchapIsSSOEnabled()
+
+ when(state.signMode) {
+ SignMode.TchapSignUp -> {
+ views.loginSSOSubmit.text = getString(CommonStrings.login_signin_sso, TCHAP_SSO_PROVIDER)
+ views.loginSSOSubmit.debouncedClicks {
+ viewModel.handle(
+ OnboardingAction.SplashAction.OnIAlreadyHaveAnAccount(
+ onboardingFlow = OnboardingFlow.TchapSignInWithSSO
+ )
+ )
+ }
+ }
+ SignMode.TchapSignInWithSSO -> {
+ views.loginSubmit.setLeftDrawable(im.vector.lib.ui.styles.R.drawable.ic_tchap_proconnect)
+ views.loginSSOHelp.text = getString(CommonStrings.tchap_connection_sso_help, TCHAP_SSO_PROVIDER)
+ views.loginSSODescription.text = getString(CommonStrings.tchap_connection_sso_description, TCHAP_SSO_PROVIDER)
+ views.loginSSOHelp.debouncedClicks { openUrlInExternalBrowser(requireContext(), TCHAP_SSO_URL) }
+ views.loginSSODescription.debouncedClicks { openUrlInExternalBrowser(requireContext(), TCHAP_SSO_FAQ_URL) }
+ views.passwordFieldTil.isVisible = false
+ views.loginSSOHelp.isVisible = true
+ views.loginSSODescription.isVisible = true
+ }
+ else -> {
+ views.passwordField.imeOptions = EditorInfo.IME_ACTION_DONE
+ views.loginSSOHelp.isVisible = false
+ }
+ }
+ }
+
+ fun tryLoginSSO(state: OnboardingViewState) {
+ if (state.signMode != SignMode.TchapSignInWithSSO) return
+ if (views.loginField.text.isNullOrEmpty()) return
+ if (state.selectedHomeserver.upstreamUrl.isNullOrEmpty()) return
+ if (views.loginSocialLoginButtons.ssoIdentityProviders.isNullOrEmpty()) {
+ views.loginFieldTil.error = getString(CommonStrings.tchap_auth_sso_inactive, TCHAP_SSO_PROVIDER)
+ viewModel.handle(OnboardingAction.ResetHomeServerUrl)
+ return
+ }
+
+ views.loginSocialLoginButtons.ssoIdentityProviders?.first().let {
+ viewModel.fetchSsoUrl(
+ redirectUrl = SSORedirectRouterActivity.VECTOR_REDIRECT_URL,
+ deviceId = state.deviceId,
+ provider = it,
+ action = SSOAction.LOGIN
+ )
+ ?.let { url -> openInCustomTab(url) }
+
+ views.loginField.text?.clear()
}
}
}
diff --git a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthSplashFragment.kt b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthSplashFragment.kt
index d203919cfc..0224c8b946 100644
--- a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthSplashFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthSplashFragment.kt
@@ -17,13 +17,18 @@
package im.vector.app.features.onboarding.ftueauth
import android.annotation.SuppressLint
+import android.graphics.Typeface
import android.os.Bundle
+import android.text.Spannable
+import android.text.SpannableString
+import android.text.style.StyleSpan
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.view.isVisible
import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.core.resources.BuildMeta
+import im.vector.app.core.utils.openUrlInExternalBrowser
import im.vector.app.databinding.FragmentTchapWelcomeBinding
import im.vector.app.features.VectorFeatures
import im.vector.app.features.onboarding.OnboardingAction
@@ -53,13 +58,27 @@ class FtueAuthSplashFragment :
}
private fun setupViews() {
+ // TCHAP Login with SSO
val isAlreadyHaveAccountEnabled = vectorFeatures.isOnboardingAlreadyHaveAccountSplashEnabled()
+ views.loginSplashSSO.apply {
+ val spannable = SpannableString(getString(CommonStrings.login_social_signin_with, TCHAP_SSO_PROVIDER))
+ spannable.setSpan(StyleSpan(Typeface.BOLD), spannable.length - TCHAP_SSO_PROVIDER.length, spannable.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
+
+ text = spannable
+ isVisible = isAlreadyHaveAccountEnabled && vectorFeatures.tchapIsSSOEnabled()
+ debouncedClicks { alreadyHaveAnAccountWithSSO() }
+ }
+ views.loginSplashSSOHelp.apply {
+ text = getString(CommonStrings.tchap_connection_sso_help, TCHAP_SSO_PROVIDER)
+ isVisible = isAlreadyHaveAccountEnabled && vectorFeatures.tchapIsSSOEnabled()
+ debouncedClicks { openUrlInExternalBrowser(requireContext(), TCHAP_SSO_URL) }
+ }
views.loginSplashSubmit.apply {
setText(if (isAlreadyHaveAccountEnabled) CommonStrings.login_splash_create_account else CommonStrings.login_splash_submit)
debouncedClicks { splashSubmit(isAlreadyHaveAccountEnabled) }
}
views.loginSplashAlreadyHaveAccount.apply {
- isVisible = vectorFeatures.isOnboardingAlreadyHaveAccountSplashEnabled()
+ isVisible = isAlreadyHaveAccountEnabled
debouncedClicks { alreadyHaveAnAccount() }
}
@@ -72,6 +91,11 @@ class FtueAuthSplashFragment :
}
}
+ /** TCHAP Login with SSO. */
+ private fun alreadyHaveAnAccountWithSSO() {
+ viewModel.handle(OnboardingAction.SplashAction.OnIAlreadyHaveAnAccount(onboardingFlow = OnboardingFlow.TchapSignInWithSSO))
+ }
+
private fun splashSubmit(isAlreadyHaveAccountEnabled: Boolean) {
val getStartedFlow = if (isAlreadyHaveAccountEnabled) OnboardingFlow.SignUp else OnboardingFlow.SignInSignUp
viewModel.handle(OnboardingAction.SplashAction.OnGetStarted(onboardingFlow = getStartedFlow))
diff --git a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthVariant.kt b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthVariant.kt
index 67f8864ac3..6a5cb403c2 100644
--- a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthVariant.kt
+++ b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueAuthVariant.kt
@@ -261,6 +261,7 @@ class FtueAuthVariant(
else -> {
withState(onboardingViewModel) { state ->
when (state.onboardingFlow) {
+ OnboardingFlow.TchapSignInWithSSO -> error("developer error")
OnboardingFlow.SignIn -> onStartCombinedLogin()
OnboardingFlow.SignUp -> onStartCombinedRegister()
OnboardingFlow.SignInSignUp,
@@ -323,6 +324,7 @@ class FtueAuthVariant(
// state.signMode could not be ready yet. So use value from the ViewEvent
when (onboardingViewEvents.signMode) {
SignMode.Unknown -> error("Sign mode has to be set before calling this method")
+ SignMode.TchapSignInWithSSO -> tchap.handleSignInWithSSO()
SignMode.TchapSignUp -> tchap.handleSignUpSelected()
SignMode.TchapSignIn -> tchap.handleSignInSelected()
SignMode.SignUp -> Unit // This case is processed in handleOnboardingViewEvents
@@ -558,5 +560,6 @@ class FtueAuthVariant(
private inner class Tchap {
fun handleSignInSelected() = openAuthLoginFragmentWithTag(FRAGMENT_LOGIN_TAG)
fun handleSignUpSelected() = openAuthLoginFragmentWithTag(FRAGMENT_LOGIN_TAG)
+ fun handleSignInWithSSO() = openAuthLoginFragmentWithTag(FRAGMENT_LOGIN_TAG)
}
}
diff --git a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueExtensions.kt b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueExtensions.kt
index 7427eb0593..b0c1f59104 100644
--- a/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueExtensions.kt
+++ b/vector/src/main/java/im/vector/app/features/onboarding/ftueauth/FtueExtensions.kt
@@ -23,6 +23,7 @@ import im.vector.lib.ui.styles.R
fun SignMode.toAuthenticateAction(login: String, password: String, initialDeviceName: String): OnboardingAction.AuthenticateAction {
return when (this) {
+ SignMode.TchapSignInWithSSO,
SignMode.Unknown -> error("developer error")
SignMode.TchapSignUp -> OnboardingAction.AuthenticateAction.TchapRegister(email = login, password, initialDeviceName)
SignMode.TchapSignIn -> OnboardingAction.AuthenticateAction.TchapLogin(email = login, password, initialDeviceName)
diff --git a/vector/src/main/res/layout/fragment_login.xml b/vector/src/main/res/layout/fragment_login.xml
index 60ebf88d1e..600a121391 100644
--- a/vector/src/main/res/layout/fragment_login.xml
+++ b/vector/src/main/res/layout/fragment_login.xml
@@ -43,11 +43,53 @@
android:textColor="?vctr_content_secondary"
tools:text="@string/login_server_matrix_org_text" />
+
+
+
+
+
+
+
+
+
+
+
@@ -119,58 +161,38 @@
-
-
-
-
-
-
-
+ android:layout_gravity="end"
+ android:text="@string/auth_forgot_password" />
-
-
-
-
-
-
+ android:layout_gravity="end"
+ android:paddingHorizontal="40dp"
+ android:text="@string/auth_login"
+ tools:enabled="false"
+ tools:ignore="RelativeOverlap" />
+
+
-
+
diff --git a/vector/src/main/res/layout/fragment_tchap_welcome.xml b/vector/src/main/res/layout/fragment_tchap_welcome.xml
index 50a5f8bb52..395f64e958 100644
--- a/vector/src/main/res/layout/fragment_tchap_welcome.xml
+++ b/vector/src/main/res/layout/fragment_tchap_welcome.xml
@@ -6,55 +6,91 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
+
+
+ app:layout_constraintTop_toTopOf="parent" />
-
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/fragment_tchap_welcome_title" />
+
+
+ app:layout_constraintTop_toBottomOf="@id/loginSplashSSO"
+ tools:text="@string/tchap_connection_sso_help" />
+
+
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ tools:text="@string/login_splash_create_account"/>