passing the authentication state from the onboarding and tracking sign up after the user has consented to tracking
This commit is contained in:
parent
ac89495348
commit
28050488ba
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright (c) 2022 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.analytics.extensions
|
||||
|
||||
import im.vector.app.features.analytics.plan.Signup
|
||||
import im.vector.app.features.onboarding.AuthenticationDescription
|
||||
|
||||
fun AuthenticationDescription.AuthenticationType.toAnalyticsType() = when (this) {
|
||||
AuthenticationDescription.AuthenticationType.Password -> Signup.AuthenticationType.Password
|
||||
AuthenticationDescription.AuthenticationType.Apple -> Signup.AuthenticationType.Apple
|
||||
AuthenticationDescription.AuthenticationType.Facebook -> Signup.AuthenticationType.Facebook
|
||||
AuthenticationDescription.AuthenticationType.Github -> Signup.AuthenticationType.GitHub
|
||||
AuthenticationDescription.AuthenticationType.Gitlab -> Signup.AuthenticationType.GitLab
|
||||
AuthenticationDescription.AuthenticationType.Google -> Signup.AuthenticationType.Google
|
||||
AuthenticationDescription.AuthenticationType.SSO -> Signup.AuthenticationType.SSO
|
||||
AuthenticationDescription.AuthenticationType.Other -> Signup.AuthenticationType.Other
|
||||
}
|
|
@ -56,6 +56,7 @@ import im.vector.app.features.matrixto.MatrixToBottomSheet
|
|||
import im.vector.app.features.matrixto.OriginOfMatrixTo
|
||||
import im.vector.app.features.navigation.Navigator
|
||||
import im.vector.app.features.notifications.NotificationDrawerManager
|
||||
import im.vector.app.features.onboarding.AuthenticationDescription
|
||||
import im.vector.app.features.permalink.NavigationInterceptor
|
||||
import im.vector.app.features.permalink.PermalinkHandler
|
||||
import im.vector.app.features.permalink.PermalinkHandler.Companion.MATRIX_TO_CUSTOM_SCHEME_URL_BASE
|
||||
|
@ -91,7 +92,7 @@ import javax.inject.Inject
|
|||
@Parcelize
|
||||
data class HomeActivityArgs(
|
||||
val clearNotification: Boolean,
|
||||
val accountCreation: Boolean,
|
||||
val authenticationDescription: AuthenticationDescription? = null,
|
||||
val hasExistingSession: Boolean = false,
|
||||
val inviteNotificationRoomId: String? = null
|
||||
) : Parcelable
|
||||
|
@ -248,7 +249,7 @@ class HomeActivity :
|
|||
if (isFirstCreation()) {
|
||||
handleIntent(intent)
|
||||
}
|
||||
homeActivityViewModel.handle(HomeActivityViewActions.ViewStarted)
|
||||
homeActivityViewModel.handle(HomeActivityViewActions.ViewStarted(args?.authenticationDescription))
|
||||
}
|
||||
|
||||
private fun openGroup(shouldClearFragment: Boolean) {
|
||||
|
@ -612,13 +613,13 @@ class HomeActivity :
|
|||
fun newIntent(
|
||||
context: Context,
|
||||
clearNotification: Boolean = false,
|
||||
accountCreation: Boolean = false,
|
||||
authenticationDescription: AuthenticationDescription? = null,
|
||||
existingSession: Boolean = false,
|
||||
inviteNotificationRoomId: String? = null
|
||||
): Intent {
|
||||
val args = HomeActivityArgs(
|
||||
clearNotification = clearNotification,
|
||||
accountCreation = accountCreation,
|
||||
authenticationDescription = authenticationDescription,
|
||||
hasExistingSession = existingSession,
|
||||
inviteNotificationRoomId = inviteNotificationRoomId
|
||||
)
|
||||
|
|
|
@ -17,8 +17,9 @@
|
|||
package im.vector.app.features.home
|
||||
|
||||
import im.vector.app.core.platform.VectorViewModelAction
|
||||
import im.vector.app.features.onboarding.AuthenticationDescription
|
||||
|
||||
sealed interface HomeActivityViewActions : VectorViewModelAction {
|
||||
object ViewStarted : HomeActivityViewActions
|
||||
data class ViewStarted(val recentAuthentication: AuthenticationDescription?) : HomeActivityViewActions
|
||||
object PushPromptHasBeenReviewed : HomeActivityViewActions
|
||||
}
|
||||
|
|
|
@ -28,8 +28,12 @@ import im.vector.app.core.di.ActiveSessionHolder
|
|||
import im.vector.app.core.di.MavericksAssistedViewModelFactory
|
||||
import im.vector.app.core.di.hiltMavericksViewModelFactory
|
||||
import im.vector.app.core.platform.VectorViewModel
|
||||
import im.vector.app.features.analytics.AnalyticsTracker
|
||||
import im.vector.app.features.analytics.extensions.toAnalyticsType
|
||||
import im.vector.app.features.analytics.plan.Signup
|
||||
import im.vector.app.features.analytics.store.AnalyticsStore
|
||||
import im.vector.app.features.login.ReAuthHelper
|
||||
import im.vector.app.features.onboarding.AuthenticationDescription
|
||||
import im.vector.app.features.raw.wellknown.ElementWellKnown
|
||||
import im.vector.app.features.raw.wellknown.getElementWellknown
|
||||
import im.vector.app.features.raw.wellknown.isSecureBackupRequired
|
||||
|
@ -37,8 +41,11 @@ import im.vector.app.features.session.coroutineScope
|
|||
import im.vector.app.features.settings.VectorPreferences
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onCompletion
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.flow.takeWhile
|
||||
import kotlinx.coroutines.launch
|
||||
import org.matrix.android.sdk.api.auth.UIABaseAuth
|
||||
import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
|
||||
|
@ -72,7 +79,8 @@ class HomeActivityViewModel @AssistedInject constructor(
|
|||
private val reAuthHelper: ReAuthHelper,
|
||||
private val analyticsStore: AnalyticsStore,
|
||||
private val lightweightSettingsStorage: LightweightSettingsStorage,
|
||||
private val vectorPreferences: VectorPreferences
|
||||
private val vectorPreferences: VectorPreferences,
|
||||
private val analyticsTracker: AnalyticsTracker
|
||||
) : VectorViewModel<HomeActivityViewState, HomeActivityViewActions, HomeActivityViewEvents>(initialState) {
|
||||
|
||||
@AssistedFactory
|
||||
|
@ -84,7 +92,7 @@ class HomeActivityViewModel @AssistedInject constructor(
|
|||
override fun initialState(viewModelContext: ViewModelContext): HomeActivityViewState? {
|
||||
val activity: HomeActivity = viewModelContext.activity()
|
||||
val args: HomeActivityArgs? = activity.intent.getParcelableExtra(Mavericks.KEY_ARG)
|
||||
return args?.let { HomeActivityViewState(accountCreation = it.accountCreation) }
|
||||
return args?.let { HomeActivityViewState(authenticationDescription = it.authenticationDescription) }
|
||||
?: super.initialState(viewModelContext)
|
||||
}
|
||||
}
|
||||
|
@ -93,18 +101,18 @@ class HomeActivityViewModel @AssistedInject constructor(
|
|||
private var hasCheckedBootstrap = false
|
||||
private var onceTrusted = false
|
||||
|
||||
private fun initialize() {
|
||||
private fun initialize(recentAuthentication: AuthenticationDescription?) {
|
||||
if (isInitialized) return
|
||||
isInitialized = true
|
||||
cleanupFiles()
|
||||
observeInitialSync()
|
||||
checkSessionPushIsOn()
|
||||
observeCrossSigningReset()
|
||||
observeAnalytics()
|
||||
observeAnalytics(recentAuthentication)
|
||||
initThreadsMigration()
|
||||
}
|
||||
|
||||
private fun observeAnalytics() {
|
||||
private fun observeAnalytics(recentAuthentication: AuthenticationDescription?) {
|
||||
if (analyticsConfig.isEnabled) {
|
||||
analyticsStore.didAskUserConsentFlow
|
||||
.onEach { didAskUser ->
|
||||
|
@ -113,9 +121,31 @@ class HomeActivityViewModel @AssistedInject constructor(
|
|||
}
|
||||
}
|
||||
.launchIn(viewModelScope)
|
||||
|
||||
recentAuthentication?.let {
|
||||
when (recentAuthentication) {
|
||||
is AuthenticationDescription.Register -> {
|
||||
viewModelScope.launch {
|
||||
analyticsStore.onUserGaveConsent {
|
||||
analyticsTracker.capture(Signup(authenticationType = recentAuthentication.type.toAnalyticsType()))
|
||||
}
|
||||
}
|
||||
}
|
||||
AuthenticationDescription.Login -> {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun AnalyticsStore.onUserGaveConsent(action: () -> Unit) {
|
||||
userConsentFlow
|
||||
.takeWhile { !it }
|
||||
.onCompletion { action() }
|
||||
.collect()
|
||||
}
|
||||
|
||||
private fun cleanupFiles() {
|
||||
// Mitigation: delete all cached decrypted files each time the application is started.
|
||||
activeSessionHolder.getSafeActiveSession()?.fileService()?.clearDecryptedCache()
|
||||
|
@ -285,7 +315,7 @@ class HomeActivityViewModel @AssistedInject constructor(
|
|||
val isSecureBackupRequired = elementWellKnown?.isSecureBackupRequired() ?: false
|
||||
|
||||
// In case of account creation, it is already done before
|
||||
if (initialState.accountCreation) {
|
||||
if (initialState.authenticationDescription is AuthenticationDescription.Register) {
|
||||
if (isSecureBackupRequired) {
|
||||
_viewEvents.post(HomeActivityViewEvents.StartRecoverySetupFlow)
|
||||
} else {
|
||||
|
@ -395,8 +425,8 @@ class HomeActivityViewModel @AssistedInject constructor(
|
|||
HomeActivityViewActions.PushPromptHasBeenReviewed -> {
|
||||
vectorPreferences.setDidAskUserToEnableSessionPush()
|
||||
}
|
||||
HomeActivityViewActions.ViewStarted -> {
|
||||
initialize()
|
||||
is HomeActivityViewActions.ViewStarted -> {
|
||||
initialize(action.recentAuthentication)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,9 +17,10 @@
|
|||
package im.vector.app.features.home
|
||||
|
||||
import com.airbnb.mvrx.MavericksState
|
||||
import im.vector.app.features.onboarding.AuthenticationDescription
|
||||
import org.matrix.android.sdk.api.session.initsync.SyncStatusService
|
||||
|
||||
data class HomeActivityViewState(
|
||||
val syncStatusServiceStatus: SyncStatusService.Status = SyncStatusService.Status.Idle,
|
||||
val accountCreation: Boolean = false
|
||||
val authenticationDescription: AuthenticationDescription? = null
|
||||
) : MavericksState
|
||||
|
|
|
@ -218,10 +218,7 @@ open class LoginActivity : VectorBaseActivity<ActivityLoginBinding>(), UnlockedA
|
|||
// change the screen name
|
||||
analyticsScreenName = MobileScreen.ScreenName.Register
|
||||
}
|
||||
val intent = HomeActivity.newIntent(
|
||||
this,
|
||||
accountCreation = loginViewState.signMode == SignMode.SignUp
|
||||
)
|
||||
val intent = HomeActivity.newIntent(this)
|
||||
startActivity(intent)
|
||||
finish()
|
||||
return
|
||||
|
|
|
@ -16,12 +16,17 @@
|
|||
|
||||
package im.vector.app.features.onboarding
|
||||
|
||||
import android.os.Parcelable
|
||||
import im.vector.app.features.onboarding.AuthenticationDescription.AuthenticationType
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import org.matrix.android.sdk.api.auth.data.SsoIdentityProvider
|
||||
|
||||
sealed interface AuthenticationDescription {
|
||||
sealed interface AuthenticationDescription : Parcelable {
|
||||
@Parcelize
|
||||
object Login : AuthenticationDescription
|
||||
data class AccountCreated(val type: AuthenticationType) : AuthenticationDescription
|
||||
|
||||
@Parcelize
|
||||
data class Register(val type: AuthenticationType) : AuthenticationDescription
|
||||
|
||||
enum class AuthenticationType {
|
||||
Password,
|
||||
|
|
|
@ -276,7 +276,7 @@ class Login2Variant(
|
|||
is LoginViewEvents2.OnLoginModeNotSupported ->
|
||||
onLoginModeNotSupported(event.supportedTypes)
|
||||
is LoginViewEvents2.OnSessionCreated -> handleOnSessionCreated(event)
|
||||
is LoginViewEvents2.Finish -> terminate(true)
|
||||
is LoginViewEvents2.Finish -> terminate()
|
||||
is LoginViewEvents2.CancelRegistration -> handleCancelRegistration()
|
||||
}
|
||||
}
|
||||
|
@ -296,14 +296,13 @@ class Login2Variant(
|
|||
option = commonOption
|
||||
)
|
||||
} else {
|
||||
terminate(false)
|
||||
terminate()
|
||||
}
|
||||
}
|
||||
|
||||
private fun terminate(newAccount: Boolean) {
|
||||
private fun terminate() {
|
||||
val intent = HomeActivity.newIntent(
|
||||
activity,
|
||||
accountCreation = newAccount
|
||||
activity
|
||||
)
|
||||
activity.startActivity(intent)
|
||||
activity.finish()
|
||||
|
|
|
@ -292,9 +292,8 @@ class OnboardingViewModel @AssistedInject constructor(
|
|||
else -> when (it) {
|
||||
is RegistrationResult.Complete -> onSessionCreated(
|
||||
it.session,
|
||||
authenticationDescription = AuthenticationDescription.AccountCreated(
|
||||
awaitState().selectedAuthenticationState.type ?: AuthenticationDescription.AuthenticationType.Other
|
||||
)
|
||||
authenticationDescription = awaitState().selectedAuthenticationState.description
|
||||
?: AuthenticationDescription.Register(AuthenticationDescription.AuthenticationType.Other)
|
||||
)
|
||||
is RegistrationResult.NextStep -> onFlowResponse(it.flowResult, onNextRegistrationStepAction)
|
||||
is RegistrationResult.SendEmailSuccess -> _viewEvents.post(OnboardingViewEvents.OnSendEmailSuccess(it.email))
|
||||
|
@ -325,7 +324,10 @@ class OnboardingViewModel @AssistedInject constructor(
|
|||
private fun OnboardingViewState.hasSelectedMatrixOrg() = selectedHomeserver.userFacingUrl == matrixOrgUrl
|
||||
|
||||
private fun handleRegisterWith(action: AuthenticateAction.Register) {
|
||||
setState { copy(selectedAuthenticationState = SelectedAuthenticationState(AuthenticationDescription.AuthenticationType.Password)) }
|
||||
setState {
|
||||
val authDescription = AuthenticationDescription.Register(AuthenticationDescription.AuthenticationType.Password)
|
||||
copy(selectedAuthenticationState = SelectedAuthenticationState(authDescription))
|
||||
}
|
||||
reAuthHelper.data = action.password
|
||||
handleRegisterAction(
|
||||
RegisterAction.CreateAccount(
|
||||
|
@ -572,14 +574,14 @@ class OnboardingViewModel @AssistedInject constructor(
|
|||
session.configureAndStart(applicationContext)
|
||||
|
||||
when (authenticationDescription) {
|
||||
is AuthenticationDescription.AccountCreated -> {
|
||||
is AuthenticationDescription.Register -> {
|
||||
val personalizationState = createPersonalizationState(session, state)
|
||||
setState {
|
||||
copy(isLoading = false, personalizationState = personalizationState)
|
||||
}
|
||||
_viewEvents.post(OnboardingViewEvents.OnAccountCreated)
|
||||
}
|
||||
AuthenticationDescription.Login -> {
|
||||
AuthenticationDescription.Login -> {
|
||||
setState { copy(isLoading = false) }
|
||||
_viewEvents.post(OnboardingViewEvents.OnAccountSignedIn)
|
||||
}
|
||||
|
@ -753,7 +755,10 @@ class OnboardingViewModel @AssistedInject constructor(
|
|||
}
|
||||
|
||||
fun getSsoUrl(redirectUrl: String, deviceId: String?, provider: SsoIdentityProvider?): String? {
|
||||
setState { copy(selectedAuthenticationState = SelectedAuthenticationState(provider.toAuthenticationType())) }
|
||||
setState {
|
||||
val authDescription = AuthenticationDescription.Register(provider.toAuthenticationType())
|
||||
copy(selectedAuthenticationState = SelectedAuthenticationState(authDescription))
|
||||
}
|
||||
return authenticationService.getSsoUrl(redirectUrl, deviceId, provider?.id)
|
||||
}
|
||||
|
||||
|
|
|
@ -86,5 +86,5 @@ data class PersonalizationState(
|
|||
|
||||
@Parcelize
|
||||
data class SelectedAuthenticationState(
|
||||
val type: AuthenticationDescription.AuthenticationType? = null,
|
||||
val description: AuthenticationDescription? = null,
|
||||
) : Parcelable
|
||||
|
|
|
@ -134,7 +134,7 @@ class FtueAuthCombinedLoginFragment @Inject constructor(
|
|||
viewModel.getSsoUrl(
|
||||
redirectUrl = SSORedirectRouterActivity.VECTOR_REDIRECT_URL,
|
||||
deviceId = deviceId,
|
||||
providerId = id
|
||||
provider = id
|
||||
)?.let { openInCustomTab(it) }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -216,7 +216,7 @@ class FtueAuthVariant(
|
|||
is OnboardingViewEvents.OnAccountCreated -> onAccountCreated()
|
||||
OnboardingViewEvents.OnAccountSignedIn -> onAccountSignedIn()
|
||||
OnboardingViewEvents.OnChooseDisplayName -> onChooseDisplayName()
|
||||
OnboardingViewEvents.OnTakeMeHome -> navigateToHome(createdAccount = true)
|
||||
OnboardingViewEvents.OnTakeMeHome -> navigateToHome()
|
||||
OnboardingViewEvents.OnChooseProfilePicture -> onChooseProfilePicture()
|
||||
OnboardingViewEvents.OnPersonalizationComplete -> onPersonalizationComplete()
|
||||
OnboardingViewEvents.OnBack -> activity.popBackstack()
|
||||
|
@ -467,7 +467,7 @@ class FtueAuthVariant(
|
|||
}
|
||||
|
||||
private fun onAccountSignedIn() {
|
||||
navigateToHome(createdAccount = false)
|
||||
navigateToHome()
|
||||
}
|
||||
|
||||
private fun onAccountCreated() {
|
||||
|
@ -479,10 +479,12 @@ class FtueAuthVariant(
|
|||
)
|
||||
}
|
||||
|
||||
private fun navigateToHome(createdAccount: Boolean) {
|
||||
val intent = HomeActivity.newIntent(activity, accountCreation = createdAccount)
|
||||
activity.startActivity(intent)
|
||||
activity.finish()
|
||||
private fun navigateToHome() {
|
||||
withState(onboardingViewModel) {
|
||||
val intent = HomeActivity.newIntent(activity, authenticationDescription = it.selectedAuthenticationState.description)
|
||||
activity.startActivity(intent)
|
||||
activity.finish()
|
||||
}
|
||||
}
|
||||
|
||||
private fun onChooseDisplayName() {
|
||||
|
|
Loading…
Reference in New Issue