Analytics: manage account data
This commit is contained in:
parent
8752fe1e69
commit
a3173d89e5
|
@ -20,6 +20,7 @@ import dagger.Binds
|
||||||
import dagger.Module
|
import dagger.Module
|
||||||
import dagger.hilt.InstallIn
|
import dagger.hilt.InstallIn
|
||||||
import dagger.multibindings.IntoMap
|
import dagger.multibindings.IntoMap
|
||||||
|
import im.vector.app.features.analytics.accountdata.AnalyticsAccountDataViewModel
|
||||||
import im.vector.app.features.analytics.ui.consent.AnalyticsConsentViewModel
|
import im.vector.app.features.analytics.ui.consent.AnalyticsConsentViewModel
|
||||||
import im.vector.app.features.auth.ReAuthViewModel
|
import im.vector.app.features.auth.ReAuthViewModel
|
||||||
import im.vector.app.features.call.VectorCallViewModel
|
import im.vector.app.features.call.VectorCallViewModel
|
||||||
|
@ -460,6 +461,11 @@ interface MavericksViewModelModule {
|
||||||
@MavericksViewModelKey(AnalyticsConsentViewModel::class)
|
@MavericksViewModelKey(AnalyticsConsentViewModel::class)
|
||||||
fun analyticsConsentViewModelFactory(factory: AnalyticsConsentViewModel.Factory): MavericksAssistedViewModelFactory<*, *>
|
fun analyticsConsentViewModelFactory(factory: AnalyticsConsentViewModel.Factory): MavericksAssistedViewModelFactory<*, *>
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
@IntoMap
|
||||||
|
@MavericksViewModelKey(AnalyticsAccountDataViewModel::class)
|
||||||
|
fun analyticsAccountDataViewModelFactory(factory: AnalyticsAccountDataViewModel.Factory): MavericksAssistedViewModelFactory<*, *>
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
@IntoMap
|
@IntoMap
|
||||||
@MavericksViewModelKey(HomeServerCapabilitiesViewModel::class)
|
@MavericksViewModelKey(HomeServerCapabilitiesViewModel::class)
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
/*
|
||||||
|
* 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.analytics.accountdata
|
||||||
|
|
||||||
|
import com.squareup.moshi.Json
|
||||||
|
import com.squareup.moshi.JsonClass
|
||||||
|
|
||||||
|
@JsonClass(generateAdapter = true)
|
||||||
|
data class AnalyticsAccountDataContent(
|
||||||
|
// A randomly generated analytics token for this user.
|
||||||
|
// This is suggested to be a 128-bit hex encoded string.
|
||||||
|
@Json(name = "id")
|
||||||
|
val id: String? = null,
|
||||||
|
// Boolean indicating whether the user has opted in.
|
||||||
|
// If null or not set, the user hasn't yet given consent either way
|
||||||
|
@Json(name = "pseudonymousAnalyticsOptIn")
|
||||||
|
val pseudonymousAnalyticsOptIn: Boolean? = null,
|
||||||
|
// Boolean indicating whether to show the analytics opt-in prompt.
|
||||||
|
@Json(name = "showPseudonymousAnalyticsPrompt")
|
||||||
|
val showPseudonymousAnalyticsPrompt: Boolean? = null
|
||||||
|
)
|
|
@ -0,0 +1,118 @@
|
||||||
|
/*
|
||||||
|
* 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.analytics.accountdata
|
||||||
|
|
||||||
|
import androidx.lifecycle.asFlow
|
||||||
|
import com.airbnb.mvrx.MavericksState
|
||||||
|
import com.airbnb.mvrx.MavericksViewModelFactory
|
||||||
|
import dagger.assisted.Assisted
|
||||||
|
import dagger.assisted.AssistedFactory
|
||||||
|
import dagger.assisted.AssistedInject
|
||||||
|
import im.vector.app.core.di.MavericksAssistedViewModelFactory
|
||||||
|
import im.vector.app.core.di.hiltMavericksViewModelFactory
|
||||||
|
import im.vector.app.core.platform.EmptyAction
|
||||||
|
import im.vector.app.core.platform.EmptyViewEvents
|
||||||
|
import im.vector.app.core.platform.VectorViewModel
|
||||||
|
import im.vector.app.features.analytics.VectorAnalytics
|
||||||
|
import kotlinx.coroutines.flow.combine
|
||||||
|
import kotlinx.coroutines.flow.launchIn
|
||||||
|
import kotlinx.coroutines.flow.mapNotNull
|
||||||
|
import kotlinx.coroutines.flow.onEach
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import org.matrix.android.sdk.api.session.Session
|
||||||
|
import org.matrix.android.sdk.api.session.events.model.toContent
|
||||||
|
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||||
|
import org.matrix.android.sdk.api.session.initsync.SyncStatusService
|
||||||
|
import org.matrix.android.sdk.flow.flow
|
||||||
|
import timber.log.Timber
|
||||||
|
import java.util.UUID
|
||||||
|
|
||||||
|
data class DummyState(
|
||||||
|
val dummy: Boolean = false
|
||||||
|
) : MavericksState
|
||||||
|
|
||||||
|
class AnalyticsAccountDataViewModel @AssistedInject constructor(
|
||||||
|
@Assisted initialState: DummyState,
|
||||||
|
private val session: Session,
|
||||||
|
private val analytics: VectorAnalytics
|
||||||
|
) : VectorViewModel<DummyState, EmptyAction, EmptyViewEvents>(initialState) {
|
||||||
|
|
||||||
|
private var checkDone: Boolean = false
|
||||||
|
|
||||||
|
@AssistedFactory
|
||||||
|
interface Factory : MavericksAssistedViewModelFactory<AnalyticsAccountDataViewModel, DummyState> {
|
||||||
|
override fun create(initialState: DummyState): AnalyticsAccountDataViewModel
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object : MavericksViewModelFactory<AnalyticsAccountDataViewModel, DummyState> by hiltMavericksViewModelFactory() {
|
||||||
|
private const val ANALYTICS_EVENT_TYPE = "im.vector.analytics";
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
observeAccountData()
|
||||||
|
observeInitSync()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun observeInitSync() {
|
||||||
|
combine(
|
||||||
|
session.getSyncStatusLive().asFlow(),
|
||||||
|
analytics.getUserConsent(),
|
||||||
|
analytics.getAnalyticsId()
|
||||||
|
) { status, userConsent, analyticsId ->
|
||||||
|
if (status is SyncStatusService.Status.IncrementalSyncIdle &&
|
||||||
|
userConsent &&
|
||||||
|
analyticsId.isEmpty() &&
|
||||||
|
!checkDone) {
|
||||||
|
// Initial sync is over, analytics Id from account data is missing and user has given consent to use analytics
|
||||||
|
checkDone = true
|
||||||
|
createAnalyticsAccountData()
|
||||||
|
}
|
||||||
|
}.launchIn(viewModelScope)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun observeAccountData() {
|
||||||
|
session.flow()
|
||||||
|
.liveUserAccountData(setOf(ANALYTICS_EVENT_TYPE))
|
||||||
|
.mapNotNull { it.firstOrNull() }
|
||||||
|
.mapNotNull { it.content.toModel<AnalyticsAccountDataContent>() }
|
||||||
|
.onEach { analyticsAccountDataContent ->
|
||||||
|
if (analyticsAccountDataContent.id.isNullOrEmpty()) {
|
||||||
|
// Probably consent revoked from Element Web
|
||||||
|
// Ignore here
|
||||||
|
Timber.d("Consent revoked from Element Web?")
|
||||||
|
} else {
|
||||||
|
Timber.d("AnalyticsId has been retrieved")
|
||||||
|
analytics.setAnalyticsId(analyticsAccountDataContent.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.launchIn(viewModelScope)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun handle(action: EmptyAction) {
|
||||||
|
// No op
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createAnalyticsAccountData() {
|
||||||
|
val content = AnalyticsAccountDataContent(
|
||||||
|
id = UUID.randomUUID().toString()
|
||||||
|
)
|
||||||
|
|
||||||
|
viewModelScope.launch {
|
||||||
|
session.accountDataService().updateUserAccountData(ANALYTICS_EVENT_TYPE, content.toContent())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -48,6 +48,7 @@ import im.vector.app.core.pushers.PushersManager
|
||||||
import im.vector.app.databinding.ActivityHomeBinding
|
import im.vector.app.databinding.ActivityHomeBinding
|
||||||
import im.vector.app.features.MainActivity
|
import im.vector.app.features.MainActivity
|
||||||
import im.vector.app.features.MainActivityArgs
|
import im.vector.app.features.MainActivityArgs
|
||||||
|
import im.vector.app.features.analytics.accountdata.AnalyticsAccountDataViewModel
|
||||||
import im.vector.app.features.disclaimer.showDisclaimerDialog
|
import im.vector.app.features.disclaimer.showDisclaimerDialog
|
||||||
import im.vector.app.features.matrixto.MatrixToBottomSheet
|
import im.vector.app.features.matrixto.MatrixToBottomSheet
|
||||||
import im.vector.app.features.navigation.Navigator
|
import im.vector.app.features.navigation.Navigator
|
||||||
|
@ -103,6 +104,8 @@ class HomeActivity :
|
||||||
private lateinit var sharedActionViewModel: HomeSharedActionViewModel
|
private lateinit var sharedActionViewModel: HomeSharedActionViewModel
|
||||||
|
|
||||||
private val homeActivityViewModel: HomeActivityViewModel by viewModel()
|
private val homeActivityViewModel: HomeActivityViewModel by viewModel()
|
||||||
|
@Suppress("UNUSED")
|
||||||
|
private val analyticsAccountDataViewModel: AnalyticsAccountDataViewModel by viewModel()
|
||||||
|
|
||||||
private val serverBackupStatusViewModel: ServerBackupStatusViewModel by viewModel()
|
private val serverBackupStatusViewModel: ServerBackupStatusViewModel by viewModel()
|
||||||
private val promoteRestrictedViewModel: PromoteRestrictedViewModel by viewModel()
|
private val promoteRestrictedViewModel: PromoteRestrictedViewModel by viewModel()
|
||||||
|
|
Loading…
Reference in New Issue