From 84e23e19119697cb831043212dac5cfc39bb5a46 Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Mon, 17 Jan 2022 12:13:30 +0000 Subject: [PATCH 01/10] storing the user usecase selection and clearinng on signout --- .../im/vector/app/features/MainActivity.kt | 3 + .../app/features/onboarding/FtueUseCase.kt | 14 +++-- .../onboarding/OnboardingViewModel.kt | 16 +++-- .../onboarding/store/OnboardingStore.kt | 59 +++++++++++++++++++ 4 files changed, 82 insertions(+), 10 deletions(-) create mode 100644 vector/src/main/java/im/vector/app/features/onboarding/store/OnboardingStore.kt diff --git a/vector/src/main/java/im/vector/app/features/MainActivity.kt b/vector/src/main/java/im/vector/app/features/MainActivity.kt index b4706780b7..89ce4a0545 100644 --- a/vector/src/main/java/im/vector/app/features/MainActivity.kt +++ b/vector/src/main/java/im/vector/app/features/MainActivity.kt @@ -36,6 +36,7 @@ import im.vector.app.features.analytics.VectorAnalytics import im.vector.app.features.home.HomeActivity import im.vector.app.features.home.ShortcutsHandler import im.vector.app.features.notifications.NotificationDrawerManager +import im.vector.app.features.onboarding.store.OnboardingStore import im.vector.app.features.pin.PinCodeStore import im.vector.app.features.pin.PinLocker import im.vector.app.features.pin.UnlockedActivity @@ -98,6 +99,7 @@ class MainActivity : VectorBaseActivity(), UnlockedActivity @Inject lateinit var pinLocker: PinLocker @Inject lateinit var popupAlertManager: PopupAlertManager @Inject lateinit var vectorAnalytics: VectorAnalytics + @Inject lateinit var onboardingStore: OnboardingStore override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -193,6 +195,7 @@ class MainActivity : VectorBaseActivity(), UnlockedActivity pinLocker.unlock() pinCodeStore.deleteEncodedPin() vectorAnalytics.onSignOut() + onboardingStore.clear() } withContext(Dispatchers.IO) { // On BG thread diff --git a/vector/src/main/java/im/vector/app/features/onboarding/FtueUseCase.kt b/vector/src/main/java/im/vector/app/features/onboarding/FtueUseCase.kt index e720b7307c..952612b43f 100644 --- a/vector/src/main/java/im/vector/app/features/onboarding/FtueUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/onboarding/FtueUseCase.kt @@ -16,9 +16,13 @@ package im.vector.app.features.onboarding -enum class FtueUseCase { - FRIENDS_FAMILY, - TEAMS, - COMMUNITIES, - SKIP +enum class FtueUseCase(val persistableValue: String) { + FRIENDS_FAMILY("friends_family"), + TEAMS("teams"), + COMMUNITIES("communities"), + SKIP("skip"); + + companion object { + fun from(persistedValue: String) = values().first { it.persistableValue == persistedValue } + } } 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 43f37f4601..d806a86600 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 @@ -42,6 +42,7 @@ import im.vector.app.features.login.LoginMode import im.vector.app.features.login.ReAuthHelper import im.vector.app.features.login.ServerType import im.vector.app.features.login.SignMode +import im.vector.app.features.onboarding.store.OnboardingStore import kotlinx.coroutines.Job import kotlinx.coroutines.launch import org.matrix.android.sdk.api.MatrixPatterns.getDomain @@ -73,7 +74,8 @@ class OnboardingViewModel @AssistedInject constructor( private val reAuthHelper: ReAuthHelper, private val stringProvider: StringProvider, private val homeServerHistoryService: HomeServerHistoryService, - private val vectorFeatures: VectorFeatures + private val vectorFeatures: VectorFeatures, + private val onboardingStore: OnboardingStore ) : VectorViewModel(initialState) { @AssistedFactory @@ -125,7 +127,7 @@ class OnboardingViewModel @AssistedInject constructor( when (action) { is OnboardingAction.OnGetStarted -> handleSplashAction(action.resetLoginConfig, action.onboardingFlow) is OnboardingAction.OnIAlreadyHaveAnAccount -> handleSplashAction(action.resetLoginConfig, action.onboardingFlow) - is OnboardingAction.UpdateUseCase -> handleUpdateUseCase() + is OnboardingAction.UpdateUseCase -> handleUpdateUseCase(action) OnboardingAction.ResetUseCase -> resetUseCase() is OnboardingAction.UpdateServerType -> handleUpdateServerType(action) is OnboardingAction.UpdateSignMode -> handleUpdateSignMode(action) @@ -458,13 +460,17 @@ class OnboardingViewModel @AssistedInject constructor( } } - private fun handleUpdateUseCase() { - // TODO act on the use case selection + private fun handleUpdateUseCase(action: OnboardingAction.UpdateUseCase) { + viewModelScope.launch { + onboardingStore.setUseCase(action.useCase) + } _viewEvents.post(OnboardingViewEvents.OpenServerSelection) } private fun resetUseCase() { - // TODO remove stored use case + viewModelScope.launch { + onboardingStore.resetUseCase() + } } private fun handleUpdateServerType(action: OnboardingAction.UpdateServerType) { diff --git a/vector/src/main/java/im/vector/app/features/onboarding/store/OnboardingStore.kt b/vector/src/main/java/im/vector/app/features/onboarding/store/OnboardingStore.kt new file mode 100644 index 0000000000..cc4d324aa0 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/onboarding/store/OnboardingStore.kt @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2021 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.onboarding.store + +import android.content.Context +import androidx.datastore.core.DataStore +import androidx.datastore.preferences.core.Preferences +import androidx.datastore.preferences.core.edit +import androidx.datastore.preferences.core.stringPreferencesKey +import androidx.datastore.preferences.preferencesDataStore +import im.vector.app.features.onboarding.FtueUseCase +import kotlinx.coroutines.flow.first +import javax.inject.Inject + +private val Context.dataStore: DataStore by preferencesDataStore(name = "vector_onboarding") + +/** + * Local storage for: + * - messaging use case (Enum/String) + */ +class OnboardingStore @Inject constructor( + private val context: Context +) { + private val useCaseKey = stringPreferencesKey("use_case") + + suspend fun readUseCase() = context.dataStore.data.first().let { preferences -> + preferences[useCaseKey]?.let { FtueUseCase.from(it) } + } + + suspend fun setUseCase(useCase: FtueUseCase) { + context.dataStore.edit { settings -> + settings[useCaseKey] = useCase.persistableValue + } + } + + suspend fun resetUseCase() { + context.dataStore.edit { settings -> + settings.remove(useCaseKey) + } + } + + suspend fun clear() { + context.dataStore.edit { it.clear() } + } +} From 515c8ce7c2c4ea0cce947be1a0fcbe6a70996815 Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Thu, 20 Jan 2022 10:43:04 +0000 Subject: [PATCH 02/10] adding ability to update the user properties (identity) via the vector analytics --- .../im/vector/app/features/analytics/VectorAnalytics.kt | 6 ++++++ .../features/analytics/impl/DefaultVectorAnalytics.kt | 9 +++++++++ 2 files changed, 15 insertions(+) diff --git a/vector/src/main/java/im/vector/app/features/analytics/VectorAnalytics.kt b/vector/src/main/java/im/vector/app/features/analytics/VectorAnalytics.kt index 95322412bd..ddab0412dd 100644 --- a/vector/src/main/java/im/vector/app/features/analytics/VectorAnalytics.kt +++ b/vector/src/main/java/im/vector/app/features/analytics/VectorAnalytics.kt @@ -16,6 +16,7 @@ package im.vector.app.features.analytics +import im.vector.app.features.analytics.plan.Identity import kotlinx.coroutines.flow.Flow interface VectorAnalytics : AnalyticsTracker { @@ -58,4 +59,9 @@ interface VectorAnalytics : AnalyticsTracker { * To be called when application is started */ fun init() + + /** + * Update user specific properties + */ + fun updateUserProperties(identity: Identity) } diff --git a/vector/src/main/java/im/vector/app/features/analytics/impl/DefaultVectorAnalytics.kt b/vector/src/main/java/im/vector/app/features/analytics/impl/DefaultVectorAnalytics.kt index eaf2e42705..d32cef604b 100644 --- a/vector/src/main/java/im/vector/app/features/analytics/impl/DefaultVectorAnalytics.kt +++ b/vector/src/main/java/im/vector/app/features/analytics/impl/DefaultVectorAnalytics.kt @@ -17,6 +17,7 @@ package im.vector.app.features.analytics.impl import android.content.Context +import com.posthog.android.Options import com.posthog.android.PostHog import com.posthog.android.Properties import im.vector.app.BuildConfig @@ -25,6 +26,7 @@ import im.vector.app.features.analytics.VectorAnalytics import im.vector.app.features.analytics.itf.VectorAnalyticsEvent import im.vector.app.features.analytics.itf.VectorAnalyticsScreen import im.vector.app.features.analytics.log.analyticsTag +import im.vector.app.features.analytics.plan.Identity import im.vector.app.features.analytics.store.AnalyticsStore import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.flow.Flow @@ -34,6 +36,9 @@ import timber.log.Timber import javax.inject.Inject import javax.inject.Singleton +private val REUSE_EXISTING_ID: String? = null +private val IGNORED_OPTIONS: Options? = null + @Singleton class DefaultVectorAnalytics @Inject constructor( private val context: Context, @@ -170,6 +175,10 @@ class DefaultVectorAnalytics @Inject constructor( ?.screen(screen.getName(), screen.getProperties()?.toPostHogProperties()) } + override fun updateUserProperties(identity: Identity) { + posthog?.identify(REUSE_EXISTING_ID, identity.getProperties().toPostHogProperties(), IGNORED_OPTIONS) + } + private fun Map?.toPostHogProperties(): Properties? { if (this == null) return null From 6c1cbccff36314adbb2bfa9b6e23b56748a44c7c Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Thu, 20 Jan 2022 10:43:24 +0000 Subject: [PATCH 03/10] updating the identity with the use case selection as part of the onboarding flow --- .../im/vector/app/features/onboarding/FtueUseCase.kt | 11 +++++++++++ .../app/features/onboarding/OnboardingViewModel.kt | 11 ++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/vector/src/main/java/im/vector/app/features/onboarding/FtueUseCase.kt b/vector/src/main/java/im/vector/app/features/onboarding/FtueUseCase.kt index 952612b43f..23bea3eb99 100644 --- a/vector/src/main/java/im/vector/app/features/onboarding/FtueUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/onboarding/FtueUseCase.kt @@ -16,6 +16,8 @@ package im.vector.app.features.onboarding +import im.vector.app.features.analytics.plan.Identity + enum class FtueUseCase(val persistableValue: String) { FRIENDS_FAMILY("friends_family"), TEAMS("teams"), @@ -26,3 +28,12 @@ enum class FtueUseCase(val persistableValue: String) { fun from(persistedValue: String) = values().first { it.persistableValue == persistedValue } } } + +fun FtueUseCase.toTrackingValue(): Identity.FtueUseCaseSelection { + return when (this) { + FtueUseCase.FRIENDS_FAMILY -> Identity.FtueUseCaseSelection.PersonalMessaging + FtueUseCase.TEAMS -> Identity.FtueUseCaseSelection.WorkMessaging + FtueUseCase.COMMUNITIES -> Identity.FtueUseCaseSelection.CommunityMessaging + FtueUseCase.SKIP -> Identity.FtueUseCaseSelection.Skip + } +} 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 d806a86600..7ab7662620 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 @@ -36,6 +36,8 @@ import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider import im.vector.app.core.utils.ensureTrailingSlash import im.vector.app.features.VectorFeatures +import im.vector.app.features.analytics.VectorAnalytics +import im.vector.app.features.analytics.plan.Identity import im.vector.app.features.login.HomeServerConnectionConfigFactory import im.vector.app.features.login.LoginConfig import im.vector.app.features.login.LoginMode @@ -75,7 +77,8 @@ class OnboardingViewModel @AssistedInject constructor( private val stringProvider: StringProvider, private val homeServerHistoryService: HomeServerHistoryService, private val vectorFeatures: VectorFeatures, - private val onboardingStore: OnboardingStore + private val onboardingStore: OnboardingStore, + private val vectorAnalytics: VectorAnalytics ) : VectorViewModel(initialState) { @AssistedFactory @@ -463,6 +466,9 @@ class OnboardingViewModel @AssistedInject constructor( private fun handleUpdateUseCase(action: OnboardingAction.UpdateUseCase) { viewModelScope.launch { onboardingStore.setUseCase(action.useCase) + vectorAnalytics.updateUserProperties( + Identity(ftueUseCaseSelection = action.useCase.toTrackingValue()) + ) } _viewEvents.post(OnboardingViewEvents.OpenServerSelection) } @@ -470,6 +476,9 @@ class OnboardingViewModel @AssistedInject constructor( private fun resetUseCase() { viewModelScope.launch { onboardingStore.resetUseCase() + vectorAnalytics.updateUserProperties( + Identity(ftueUseCaseSelection = null) + ) } } From 0a7c421faa3f45041d1fae6147ddb5b373bc1506 Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Fri, 21 Jan 2022 12:55:21 +0000 Subject: [PATCH 04/10] making use of the analytics tracker instead of the vector analytics for the smaller tracking subset --- .../im/vector/app/features/analytics/AnalyticsTracker.kt | 6 ++++++ .../im/vector/app/features/analytics/VectorAnalytics.kt | 6 ------ .../vector/app/features/onboarding/OnboardingViewModel.kt | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/analytics/AnalyticsTracker.kt b/vector/src/main/java/im/vector/app/features/analytics/AnalyticsTracker.kt index e1da0f4434..e85919a45f 100644 --- a/vector/src/main/java/im/vector/app/features/analytics/AnalyticsTracker.kt +++ b/vector/src/main/java/im/vector/app/features/analytics/AnalyticsTracker.kt @@ -18,6 +18,7 @@ package im.vector.app.features.analytics import im.vector.app.features.analytics.itf.VectorAnalyticsEvent import im.vector.app.features.analytics.itf.VectorAnalyticsScreen +import im.vector.app.features.analytics.plan.Identity interface AnalyticsTracker { /** @@ -29,4 +30,9 @@ interface AnalyticsTracker { * Track a displayed screen */ fun screen(screen: VectorAnalyticsScreen) + + /** + * Update user specific properties + */ + fun updateUserProperties(identity: Identity) } diff --git a/vector/src/main/java/im/vector/app/features/analytics/VectorAnalytics.kt b/vector/src/main/java/im/vector/app/features/analytics/VectorAnalytics.kt index ddab0412dd..95322412bd 100644 --- a/vector/src/main/java/im/vector/app/features/analytics/VectorAnalytics.kt +++ b/vector/src/main/java/im/vector/app/features/analytics/VectorAnalytics.kt @@ -16,7 +16,6 @@ package im.vector.app.features.analytics -import im.vector.app.features.analytics.plan.Identity import kotlinx.coroutines.flow.Flow interface VectorAnalytics : AnalyticsTracker { @@ -59,9 +58,4 @@ interface VectorAnalytics : AnalyticsTracker { * To be called when application is started */ fun init() - - /** - * Update user specific properties - */ - fun updateUserProperties(identity: Identity) } 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 7ab7662620..3e4ea68317 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 @@ -36,7 +36,7 @@ import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider import im.vector.app.core.utils.ensureTrailingSlash import im.vector.app.features.VectorFeatures -import im.vector.app.features.analytics.VectorAnalytics +import im.vector.app.features.analytics.AnalyticsTracker import im.vector.app.features.analytics.plan.Identity import im.vector.app.features.login.HomeServerConnectionConfigFactory import im.vector.app.features.login.LoginConfig @@ -78,7 +78,7 @@ class OnboardingViewModel @AssistedInject constructor( private val homeServerHistoryService: HomeServerHistoryService, private val vectorFeatures: VectorFeatures, private val onboardingStore: OnboardingStore, - private val vectorAnalytics: VectorAnalytics + private val analyticsTracker: AnalyticsTracker ) : VectorViewModel(initialState) { @AssistedFactory @@ -466,7 +466,7 @@ class OnboardingViewModel @AssistedInject constructor( private fun handleUpdateUseCase(action: OnboardingAction.UpdateUseCase) { viewModelScope.launch { onboardingStore.setUseCase(action.useCase) - vectorAnalytics.updateUserProperties( + analyticsTracker.updateUserProperties( Identity(ftueUseCaseSelection = action.useCase.toTrackingValue()) ) } @@ -476,7 +476,7 @@ class OnboardingViewModel @AssistedInject constructor( private fun resetUseCase() { viewModelScope.launch { onboardingStore.resetUseCase() - vectorAnalytics.updateUserProperties( + analyticsTracker.updateUserProperties( Identity(ftueUseCaseSelection = null) ) } From c4ac03949c34f778aa79fe062029693357bc6b19 Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Fri, 21 Jan 2022 13:07:57 +0000 Subject: [PATCH 05/10] persisting the use case at the point of session creation - this ensures we have a unique session or account id to store the selection against in case we support multi account in the future --- .../onboarding/OnboardingViewModel.kt | 19 +++++++------------ .../onboarding/OnboardingViewState.kt | 2 ++ 2 files changed, 9 insertions(+), 12 deletions(-) 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 3e4ea68317..07a7933d4b 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 @@ -464,22 +464,14 @@ class OnboardingViewModel @AssistedInject constructor( } private fun handleUpdateUseCase(action: OnboardingAction.UpdateUseCase) { - viewModelScope.launch { - onboardingStore.setUseCase(action.useCase) - analyticsTracker.updateUserProperties( - Identity(ftueUseCaseSelection = action.useCase.toTrackingValue()) - ) - } + setState { copy(useCase = action.useCase) } + analyticsTracker.updateUserProperties(Identity(ftueUseCaseSelection = action.useCase.toTrackingValue())) _viewEvents.post(OnboardingViewEvents.OpenServerSelection) } private fun resetUseCase() { - viewModelScope.launch { - onboardingStore.resetUseCase() - analyticsTracker.updateUserProperties( - Identity(ftueUseCaseSelection = null) - ) - } + setState { copy(useCase = null) } + analyticsTracker.updateUserProperties(Identity(ftueUseCaseSelection = null)) } private fun handleUpdateServerType(action: OnboardingAction.UpdateServerType) { @@ -760,6 +752,9 @@ class OnboardingViewModel @AssistedInject constructor( } private suspend fun onSessionCreated(session: Session) { + awaitState().useCase?.let { useCase -> + onboardingStore.setUseCase(useCase) + } activeSessionHolder.setActiveSession(session) authenticationService.reset() 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 fd25f3901e..d05a8294f6 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 @@ -40,6 +40,8 @@ data class OnboardingViewState( @PersistState val serverType: ServerType = ServerType.Unknown, @PersistState + val useCase: FtueUseCase? = null, + @PersistState val signMode: SignMode = SignMode.Unknown, @PersistState val resetPasswordEmail: String? = null, From f44ccd739ebba3a8914fd44c53577ef9eff1577f Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Fri, 21 Jan 2022 13:50:51 +0000 Subject: [PATCH 06/10] storing an retrieving the use case selection by user - will enable multi account support in the future --- .../vector/app/core/extensions/DataStore.kt | 30 +++++++++++++++++++ .../im/vector/app/features/MainActivity.kt | 10 +++---- .../onboarding/OnboardingViewModel.kt | 2 +- .../onboarding/store/OnboardingStore.kt | 21 ++++++------- 4 files changed, 47 insertions(+), 16 deletions(-) create mode 100644 vector/src/main/java/im/vector/app/core/extensions/DataStore.kt diff --git a/vector/src/main/java/im/vector/app/core/extensions/DataStore.kt b/vector/src/main/java/im/vector/app/core/extensions/DataStore.kt new file mode 100644 index 0000000000..72e2408591 --- /dev/null +++ b/vector/src/main/java/im/vector/app/core/extensions/DataStore.kt @@ -0,0 +1,30 @@ +/* + * 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.core.extensions + +import androidx.datastore.core.DataStore +import androidx.datastore.preferences.core.Preferences +import androidx.datastore.preferences.core.edit + +suspend fun DataStore.removeKeysWithPrefix(prefix: String) { + edit { preferences -> + val keysToRemove = preferences.asMap().keys.filter { key -> key.name.startsWith(prefix) } + keysToRemove.forEach { + preferences.remove(it) + } + } +} diff --git a/vector/src/main/java/im/vector/app/features/MainActivity.kt b/vector/src/main/java/im/vector/app/features/MainActivity.kt index 89ce4a0545..98a967e925 100644 --- a/vector/src/main/java/im/vector/app/features/MainActivity.kt +++ b/vector/src/main/java/im/vector/app/features/MainActivity.kt @@ -151,7 +151,7 @@ class MainActivity : VectorBaseActivity(), UnlockedActivity // Just do the local cleanup Timber.w("Account deactivated, start app") sessionHolder.clearActiveSession() - doLocalCleanup(clearPreferences = true) + doLocalCleanup(clearPreferences = true, userId = session.myUserId) startNextActivityAndFinish() } } @@ -165,14 +165,14 @@ class MainActivity : VectorBaseActivity(), UnlockedActivity } Timber.w("SIGN_OUT: success, start app") sessionHolder.clearActiveSession() - doLocalCleanup(clearPreferences = true) + doLocalCleanup(clearPreferences = true, userId = session.myUserId) startNextActivityAndFinish() } } args.clearCache -> { lifecycleScope.launch { session.clearCache() - doLocalCleanup(clearPreferences = false) + doLocalCleanup(clearPreferences = false, userId = session.myUserId) session.startSyncing(applicationContext) startNextActivityAndFinish() } @@ -185,7 +185,7 @@ class MainActivity : VectorBaseActivity(), UnlockedActivity Timber.w("Ignoring invalid token global error") } - private suspend fun doLocalCleanup(clearPreferences: Boolean) { + private suspend fun doLocalCleanup(clearPreferences: Boolean, userId: String) { // On UI Thread Glide.get(this@MainActivity).clearMemory() @@ -195,7 +195,7 @@ class MainActivity : VectorBaseActivity(), UnlockedActivity pinLocker.unlock() pinCodeStore.deleteEncodedPin() vectorAnalytics.onSignOut() - onboardingStore.clear() + onboardingStore.clear(userId) } withContext(Dispatchers.IO) { // On BG thread 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 07a7933d4b..530f2e62a5 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 @@ -753,7 +753,7 @@ class OnboardingViewModel @AssistedInject constructor( private suspend fun onSessionCreated(session: Session) { awaitState().useCase?.let { useCase -> - onboardingStore.setUseCase(useCase) + onboardingStore.setUseCase(userId = session.myUserId, useCase) } activeSessionHolder.setActiveSession(session) diff --git a/vector/src/main/java/im/vector/app/features/onboarding/store/OnboardingStore.kt b/vector/src/main/java/im/vector/app/features/onboarding/store/OnboardingStore.kt index cc4d324aa0..96ad6ec050 100644 --- a/vector/src/main/java/im/vector/app/features/onboarding/store/OnboardingStore.kt +++ b/vector/src/main/java/im/vector/app/features/onboarding/store/OnboardingStore.kt @@ -22,6 +22,7 @@ import androidx.datastore.preferences.core.Preferences import androidx.datastore.preferences.core.edit import androidx.datastore.preferences.core.stringPreferencesKey import androidx.datastore.preferences.preferencesDataStore +import im.vector.app.core.extensions.removeKeysWithPrefix import im.vector.app.features.onboarding.FtueUseCase import kotlinx.coroutines.flow.first import javax.inject.Inject @@ -35,25 +36,25 @@ private val Context.dataStore: DataStore by preferencesDataStore(na class OnboardingStore @Inject constructor( private val context: Context ) { - private val useCaseKey = stringPreferencesKey("use_case") - - suspend fun readUseCase() = context.dataStore.data.first().let { preferences -> - preferences[useCaseKey]?.let { FtueUseCase.from(it) } + suspend fun readUseCase(userId: String) = context.dataStore.data.first().let { preferences -> + preferences[userId.toUseCaseKey()]?.let { FtueUseCase.from(it) } } - suspend fun setUseCase(useCase: FtueUseCase) { + suspend fun setUseCase(userId: String, useCase: FtueUseCase) { context.dataStore.edit { settings -> - settings[useCaseKey] = useCase.persistableValue + settings[userId.toUseCaseKey()] = useCase.persistableValue } } - suspend fun resetUseCase() { + suspend fun resetUseCase(userId: String) { context.dataStore.edit { settings -> - settings.remove(useCaseKey) + settings.remove(userId.toUseCaseKey()) } } - suspend fun clear() { - context.dataStore.edit { it.clear() } + suspend fun clear(userId: String) { + context.dataStore.removeKeysWithPrefix(userId) } + + private fun String.toUseCaseKey() = stringPreferencesKey("$this-use_case") } From b91b9cb973a3c603c05bdf9fc59e8a22dc4d8326 Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Fri, 21 Jan 2022 14:25:17 +0000 Subject: [PATCH 07/10] lifting analytics extension to the common package --- .../analytics/extensions/IdentityExt.kt | 29 +++++++++++++++++++ .../app/features/onboarding/FtueUseCase.kt | 11 ------- .../onboarding/OnboardingViewModel.kt | 1 + 3 files changed, 30 insertions(+), 11 deletions(-) create mode 100644 vector/src/main/java/im/vector/app/features/analytics/extensions/IdentityExt.kt diff --git a/vector/src/main/java/im/vector/app/features/analytics/extensions/IdentityExt.kt b/vector/src/main/java/im/vector/app/features/analytics/extensions/IdentityExt.kt new file mode 100644 index 0000000000..d87769fd36 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/analytics/extensions/IdentityExt.kt @@ -0,0 +1,29 @@ +/* + * 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.Identity +import im.vector.app.features.onboarding.FtueUseCase + +fun FtueUseCase.toTrackingValue(): Identity.FtueUseCaseSelection { + return when (this) { + FtueUseCase.FRIENDS_FAMILY -> Identity.FtueUseCaseSelection.PersonalMessaging + FtueUseCase.TEAMS -> Identity.FtueUseCaseSelection.WorkMessaging + FtueUseCase.COMMUNITIES -> Identity.FtueUseCaseSelection.CommunityMessaging + FtueUseCase.SKIP -> Identity.FtueUseCaseSelection.Skip + } +} diff --git a/vector/src/main/java/im/vector/app/features/onboarding/FtueUseCase.kt b/vector/src/main/java/im/vector/app/features/onboarding/FtueUseCase.kt index 23bea3eb99..952612b43f 100644 --- a/vector/src/main/java/im/vector/app/features/onboarding/FtueUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/onboarding/FtueUseCase.kt @@ -16,8 +16,6 @@ package im.vector.app.features.onboarding -import im.vector.app.features.analytics.plan.Identity - enum class FtueUseCase(val persistableValue: String) { FRIENDS_FAMILY("friends_family"), TEAMS("teams"), @@ -28,12 +26,3 @@ enum class FtueUseCase(val persistableValue: String) { fun from(persistedValue: String) = values().first { it.persistableValue == persistedValue } } } - -fun FtueUseCase.toTrackingValue(): Identity.FtueUseCaseSelection { - return when (this) { - FtueUseCase.FRIENDS_FAMILY -> Identity.FtueUseCaseSelection.PersonalMessaging - FtueUseCase.TEAMS -> Identity.FtueUseCaseSelection.WorkMessaging - FtueUseCase.COMMUNITIES -> Identity.FtueUseCaseSelection.CommunityMessaging - FtueUseCase.SKIP -> Identity.FtueUseCaseSelection.Skip - } -} 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 530f2e62a5..9c3743f372 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 @@ -37,6 +37,7 @@ import im.vector.app.core.resources.StringProvider import im.vector.app.core.utils.ensureTrailingSlash import im.vector.app.features.VectorFeatures import im.vector.app.features.analytics.AnalyticsTracker +import im.vector.app.features.analytics.extensions.toTrackingValue import im.vector.app.features.analytics.plan.Identity import im.vector.app.features.login.HomeServerConnectionConfigFactory import im.vector.app.features.login.LoginConfig From 73b80b1c7d7fb901ff91f87faafc11ab18b96515 Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Tue, 25 Jan 2022 17:03:13 +0000 Subject: [PATCH 08/10] providing the onboarding store via session extension - avoids needing to know about the user id for each read/write --- .../im/vector/app/core/extensions/Session.kt | 3 +++ .../im/vector/app/features/MainActivity.kt | 14 ++++++----- .../onboarding/OnboardingViewModel.kt | 5 ++-- .../onboarding/store/OnboardingStore.kt | 23 ++++++++++--------- 4 files changed, 25 insertions(+), 20 deletions(-) diff --git a/vector/src/main/java/im/vector/app/core/extensions/Session.kt b/vector/src/main/java/im/vector/app/core/extensions/Session.kt index 90b08ef92b..b350e0041c 100644 --- a/vector/src/main/java/im/vector/app/core/extensions/Session.kt +++ b/vector/src/main/java/im/vector/app/core/extensions/Session.kt @@ -21,6 +21,7 @@ import androidx.core.content.ContextCompat import androidx.lifecycle.Lifecycle import androidx.lifecycle.ProcessLifecycleOwner import im.vector.app.core.services.VectorSyncService +import im.vector.app.features.onboarding.store.OnboardingStore import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysBackupState import org.matrix.android.sdk.api.session.sync.FilterService @@ -76,3 +77,5 @@ fun Session.cannotLogoutSafely(): Boolean { // That are not backed up !sharedSecretStorageService.isRecoverySetup()) } + +fun Session.onboardingStore(context: Context) = OnboardingStore(context, myUserId) diff --git a/vector/src/main/java/im/vector/app/features/MainActivity.kt b/vector/src/main/java/im/vector/app/features/MainActivity.kt index 98a967e925..25d0323577 100644 --- a/vector/src/main/java/im/vector/app/features/MainActivity.kt +++ b/vector/src/main/java/im/vector/app/features/MainActivity.kt @@ -28,6 +28,7 @@ import dagger.hilt.android.AndroidEntryPoint import im.vector.app.R import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.error.ErrorFormatter +import im.vector.app.core.extensions.onboardingStore import im.vector.app.core.extensions.startSyncing import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.core.utils.deleteAllFiles @@ -99,7 +100,6 @@ class MainActivity : VectorBaseActivity(), UnlockedActivity @Inject lateinit var pinLocker: PinLocker @Inject lateinit var popupAlertManager: PopupAlertManager @Inject lateinit var vectorAnalytics: VectorAnalytics - @Inject lateinit var onboardingStore: OnboardingStore override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -145,13 +145,15 @@ class MainActivity : VectorBaseActivity(), UnlockedActivity startNextActivityAndFinish() return } + + val onboardingStore = session.onboardingStore(this) when { args.isAccountDeactivated -> { lifecycleScope.launch { // Just do the local cleanup Timber.w("Account deactivated, start app") sessionHolder.clearActiveSession() - doLocalCleanup(clearPreferences = true, userId = session.myUserId) + doLocalCleanup(clearPreferences = true, onboardingStore) startNextActivityAndFinish() } } @@ -165,14 +167,14 @@ class MainActivity : VectorBaseActivity(), UnlockedActivity } Timber.w("SIGN_OUT: success, start app") sessionHolder.clearActiveSession() - doLocalCleanup(clearPreferences = true, userId = session.myUserId) + doLocalCleanup(clearPreferences = true, onboardingStore) startNextActivityAndFinish() } } args.clearCache -> { lifecycleScope.launch { session.clearCache() - doLocalCleanup(clearPreferences = false, userId = session.myUserId) + doLocalCleanup(clearPreferences = false, onboardingStore) session.startSyncing(applicationContext) startNextActivityAndFinish() } @@ -185,7 +187,7 @@ class MainActivity : VectorBaseActivity(), UnlockedActivity Timber.w("Ignoring invalid token global error") } - private suspend fun doLocalCleanup(clearPreferences: Boolean, userId: String) { + private suspend fun doLocalCleanup(clearPreferences: Boolean, onboardingStore: OnboardingStore) { // On UI Thread Glide.get(this@MainActivity).clearMemory() @@ -195,7 +197,7 @@ class MainActivity : VectorBaseActivity(), UnlockedActivity pinLocker.unlock() pinCodeStore.deleteEncodedPin() vectorAnalytics.onSignOut() - onboardingStore.clear(userId) + onboardingStore.clear() } withContext(Dispatchers.IO) { // On BG thread 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 9c3743f372..72379f17ea 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 @@ -32,6 +32,7 @@ import im.vector.app.core.di.MavericksAssistedViewModelFactory import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.extensions.configureAndStart import im.vector.app.core.extensions.exhaustive +import im.vector.app.core.extensions.onboardingStore import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider import im.vector.app.core.utils.ensureTrailingSlash @@ -45,7 +46,6 @@ import im.vector.app.features.login.LoginMode import im.vector.app.features.login.ReAuthHelper import im.vector.app.features.login.ServerType import im.vector.app.features.login.SignMode -import im.vector.app.features.onboarding.store.OnboardingStore import kotlinx.coroutines.Job import kotlinx.coroutines.launch import org.matrix.android.sdk.api.MatrixPatterns.getDomain @@ -78,7 +78,6 @@ class OnboardingViewModel @AssistedInject constructor( private val stringProvider: StringProvider, private val homeServerHistoryService: HomeServerHistoryService, private val vectorFeatures: VectorFeatures, - private val onboardingStore: OnboardingStore, private val analyticsTracker: AnalyticsTracker ) : VectorViewModel(initialState) { @@ -754,7 +753,7 @@ class OnboardingViewModel @AssistedInject constructor( private suspend fun onSessionCreated(session: Session) { awaitState().useCase?.let { useCase -> - onboardingStore.setUseCase(userId = session.myUserId, useCase) + session.onboardingStore(applicationContext).setUseCase(useCase) } activeSessionHolder.setActiveSession(session) diff --git a/vector/src/main/java/im/vector/app/features/onboarding/store/OnboardingStore.kt b/vector/src/main/java/im/vector/app/features/onboarding/store/OnboardingStore.kt index 96ad6ec050..3193c68f98 100644 --- a/vector/src/main/java/im/vector/app/features/onboarding/store/OnboardingStore.kt +++ b/vector/src/main/java/im/vector/app/features/onboarding/store/OnboardingStore.kt @@ -25,7 +25,6 @@ import androidx.datastore.preferences.preferencesDataStore import im.vector.app.core.extensions.removeKeysWithPrefix import im.vector.app.features.onboarding.FtueUseCase import kotlinx.coroutines.flow.first -import javax.inject.Inject private val Context.dataStore: DataStore by preferencesDataStore(name = "vector_onboarding") @@ -33,27 +32,29 @@ private val Context.dataStore: DataStore by preferencesDataStore(na * Local storage for: * - messaging use case (Enum/String) */ -class OnboardingStore @Inject constructor( - private val context: Context +class OnboardingStore constructor( + private val context: Context, + private val myUserId: String ) { - suspend fun readUseCase(userId: String) = context.dataStore.data.first().let { preferences -> - preferences[userId.toUseCaseKey()]?.let { FtueUseCase.from(it) } + + suspend fun readUseCase() = context.dataStore.data.first().let { preferences -> + preferences[myUserId.toUseCaseKey()]?.let { FtueUseCase.from(it) } } - suspend fun setUseCase(userId: String, useCase: FtueUseCase) { + suspend fun setUseCase(useCase: FtueUseCase) { context.dataStore.edit { settings -> - settings[userId.toUseCaseKey()] = useCase.persistableValue + settings[myUserId.toUseCaseKey()] = useCase.persistableValue } } - suspend fun resetUseCase(userId: String) { + suspend fun resetUseCase() { context.dataStore.edit { settings -> - settings.remove(userId.toUseCaseKey()) + settings.remove(myUserId.toUseCaseKey()) } } - suspend fun clear(userId: String) { - context.dataStore.removeKeysWithPrefix(userId) + suspend fun clear() { + context.dataStore.removeKeysWithPrefix(myUserId) } private fun String.toUseCaseKey() = stringPreferencesKey("$this-use_case") From bc373917b53dfebd0fde905dcd7916fd3e19ac53 Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Wed, 26 Jan 2022 09:15:44 +0000 Subject: [PATCH 09/10] making the onboarding store a userId scoped generic store instead --- .../vector/app/core/extensions/DataStore.kt | 30 ------------------- .../im/vector/app/core/extensions/Session.kt | 4 +-- .../im/vector/app/features/MainActivity.kt | 10 +++---- .../onboarding/OnboardingViewModel.kt | 4 +-- .../VectorSessionStore.kt} | 22 +++++++------- 5 files changed, 19 insertions(+), 51 deletions(-) delete mode 100644 vector/src/main/java/im/vector/app/core/extensions/DataStore.kt rename vector/src/main/java/im/vector/app/features/{onboarding/store/OnboardingStore.kt => session/VectorSessionStore.kt} (69%) diff --git a/vector/src/main/java/im/vector/app/core/extensions/DataStore.kt b/vector/src/main/java/im/vector/app/core/extensions/DataStore.kt deleted file mode 100644 index 72e2408591..0000000000 --- a/vector/src/main/java/im/vector/app/core/extensions/DataStore.kt +++ /dev/null @@ -1,30 +0,0 @@ -/* - * 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.core.extensions - -import androidx.datastore.core.DataStore -import androidx.datastore.preferences.core.Preferences -import androidx.datastore.preferences.core.edit - -suspend fun DataStore.removeKeysWithPrefix(prefix: String) { - edit { preferences -> - val keysToRemove = preferences.asMap().keys.filter { key -> key.name.startsWith(prefix) } - keysToRemove.forEach { - preferences.remove(it) - } - } -} diff --git a/vector/src/main/java/im/vector/app/core/extensions/Session.kt b/vector/src/main/java/im/vector/app/core/extensions/Session.kt index b350e0041c..87ed51522f 100644 --- a/vector/src/main/java/im/vector/app/core/extensions/Session.kt +++ b/vector/src/main/java/im/vector/app/core/extensions/Session.kt @@ -21,7 +21,7 @@ import androidx.core.content.ContextCompat import androidx.lifecycle.Lifecycle import androidx.lifecycle.ProcessLifecycleOwner import im.vector.app.core.services.VectorSyncService -import im.vector.app.features.onboarding.store.OnboardingStore +import im.vector.app.features.session.VectorSessionStore import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.crypto.keysbackup.KeysBackupState import org.matrix.android.sdk.api.session.sync.FilterService @@ -78,4 +78,4 @@ fun Session.cannotLogoutSafely(): Boolean { !sharedSecretStorageService.isRecoverySetup()) } -fun Session.onboardingStore(context: Context) = OnboardingStore(context, myUserId) +fun Session.vectorStore(context: Context) = VectorSessionStore(context, myUserId) diff --git a/vector/src/main/java/im/vector/app/features/MainActivity.kt b/vector/src/main/java/im/vector/app/features/MainActivity.kt index 25d0323577..33b735551c 100644 --- a/vector/src/main/java/im/vector/app/features/MainActivity.kt +++ b/vector/src/main/java/im/vector/app/features/MainActivity.kt @@ -28,8 +28,8 @@ import dagger.hilt.android.AndroidEntryPoint import im.vector.app.R import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.error.ErrorFormatter -import im.vector.app.core.extensions.onboardingStore import im.vector.app.core.extensions.startSyncing +import im.vector.app.core.extensions.vectorStore import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.core.utils.deleteAllFiles import im.vector.app.databinding.ActivityMainBinding @@ -37,11 +37,11 @@ import im.vector.app.features.analytics.VectorAnalytics import im.vector.app.features.home.HomeActivity import im.vector.app.features.home.ShortcutsHandler import im.vector.app.features.notifications.NotificationDrawerManager -import im.vector.app.features.onboarding.store.OnboardingStore import im.vector.app.features.pin.PinCodeStore import im.vector.app.features.pin.PinLocker import im.vector.app.features.pin.UnlockedActivity import im.vector.app.features.popup.PopupAlertManager +import im.vector.app.features.session.VectorSessionStore import im.vector.app.features.settings.VectorPreferences import im.vector.app.features.signout.hard.SignedOutActivity import im.vector.app.features.themes.ActivityOtherThemes @@ -146,7 +146,7 @@ class MainActivity : VectorBaseActivity(), UnlockedActivity return } - val onboardingStore = session.onboardingStore(this) + val onboardingStore = session.vectorStore(this) when { args.isAccountDeactivated -> { lifecycleScope.launch { @@ -187,7 +187,7 @@ class MainActivity : VectorBaseActivity(), UnlockedActivity Timber.w("Ignoring invalid token global error") } - private suspend fun doLocalCleanup(clearPreferences: Boolean, onboardingStore: OnboardingStore) { + private suspend fun doLocalCleanup(clearPreferences: Boolean, vectorSessionStore: VectorSessionStore) { // On UI Thread Glide.get(this@MainActivity).clearMemory() @@ -197,7 +197,7 @@ class MainActivity : VectorBaseActivity(), UnlockedActivity pinLocker.unlock() pinCodeStore.deleteEncodedPin() vectorAnalytics.onSignOut() - onboardingStore.clear() + vectorSessionStore.clear() } withContext(Dispatchers.IO) { // On BG thread 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 72379f17ea..8097e90206 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 @@ -32,7 +32,7 @@ import im.vector.app.core.di.MavericksAssistedViewModelFactory import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.extensions.configureAndStart import im.vector.app.core.extensions.exhaustive -import im.vector.app.core.extensions.onboardingStore +import im.vector.app.core.extensions.vectorStore import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.resources.StringProvider import im.vector.app.core.utils.ensureTrailingSlash @@ -753,7 +753,7 @@ class OnboardingViewModel @AssistedInject constructor( private suspend fun onSessionCreated(session: Session) { awaitState().useCase?.let { useCase -> - session.onboardingStore(applicationContext).setUseCase(useCase) + session.vectorStore(applicationContext).setUseCase(useCase) } activeSessionHolder.setActiveSession(session) diff --git a/vector/src/main/java/im/vector/app/features/onboarding/store/OnboardingStore.kt b/vector/src/main/java/im/vector/app/features/session/VectorSessionStore.kt similarity index 69% rename from vector/src/main/java/im/vector/app/features/onboarding/store/OnboardingStore.kt rename to vector/src/main/java/im/vector/app/features/session/VectorSessionStore.kt index 3193c68f98..bd0b7e2cca 100644 --- a/vector/src/main/java/im/vector/app/features/onboarding/store/OnboardingStore.kt +++ b/vector/src/main/java/im/vector/app/features/session/VectorSessionStore.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package im.vector.app.features.onboarding.store +package im.vector.app.features.session import android.content.Context import androidx.datastore.core.DataStore @@ -22,40 +22,38 @@ import androidx.datastore.preferences.core.Preferences import androidx.datastore.preferences.core.edit import androidx.datastore.preferences.core.stringPreferencesKey import androidx.datastore.preferences.preferencesDataStore -import im.vector.app.core.extensions.removeKeysWithPrefix import im.vector.app.features.onboarding.FtueUseCase import kotlinx.coroutines.flow.first -private val Context.dataStore: DataStore by preferencesDataStore(name = "vector_onboarding") - /** * Local storage for: * - messaging use case (Enum/String) */ -class OnboardingStore constructor( +class VectorSessionStore constructor( private val context: Context, - private val myUserId: String + myUserId: String ) { + private val Context.dataStore: DataStore by preferencesDataStore(name = "vector_session_store_$myUserId") + private val useCaseKey = stringPreferencesKey("use_case") + suspend fun readUseCase() = context.dataStore.data.first().let { preferences -> - preferences[myUserId.toUseCaseKey()]?.let { FtueUseCase.from(it) } + preferences[useCaseKey]?.let { FtueUseCase.from(it) } } suspend fun setUseCase(useCase: FtueUseCase) { context.dataStore.edit { settings -> - settings[myUserId.toUseCaseKey()] = useCase.persistableValue + settings[useCaseKey] = useCase.persistableValue } } suspend fun resetUseCase() { context.dataStore.edit { settings -> - settings.remove(myUserId.toUseCaseKey()) + settings.remove(useCaseKey) } } suspend fun clear() { - context.dataStore.removeKeysWithPrefix(myUserId) + context.dataStore.edit { settings -> settings.clear() } } - - private fun String.toUseCaseKey() = stringPreferencesKey("$this-use_case") } From cbdeb54fdbd687561f043ad807d0620c8a722523 Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Wed, 26 Jan 2022 18:43:09 +0000 Subject: [PATCH 10/10] using md5 hash of the userId to normalise the usage as a key --- .../java/im/vector/app/features/session/VectorSessionStore.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/vector/src/main/java/im/vector/app/features/session/VectorSessionStore.kt b/vector/src/main/java/im/vector/app/features/session/VectorSessionStore.kt index bd0b7e2cca..ce85eeeb98 100644 --- a/vector/src/main/java/im/vector/app/features/session/VectorSessionStore.kt +++ b/vector/src/main/java/im/vector/app/features/session/VectorSessionStore.kt @@ -24,6 +24,7 @@ import androidx.datastore.preferences.core.stringPreferencesKey import androidx.datastore.preferences.preferencesDataStore import im.vector.app.features.onboarding.FtueUseCase import kotlinx.coroutines.flow.first +import org.matrix.android.sdk.internal.util.md5 /** * Local storage for: @@ -34,7 +35,7 @@ class VectorSessionStore constructor( myUserId: String ) { - private val Context.dataStore: DataStore by preferencesDataStore(name = "vector_session_store_$myUserId") + private val Context.dataStore: DataStore by preferencesDataStore(name = "vector_session_store_${myUserId.md5()}") private val useCaseKey = stringPreferencesKey("use_case") suspend fun readUseCase() = context.dataStore.data.first().let { preferences ->