Analytics: Opt-in screen logic + remove from SplashScreen

This commit is contained in:
Benoit Marty 2021-12-06 10:52:08 +01:00
parent 73f5d77b05
commit 8c794b1059
12 changed files with 158 additions and 48 deletions

View file

@ -236,6 +236,7 @@ android {
// Analytics. Set to empty strings to just disable analytics
buildConfigField "String", "ANALYTICS_POSTHOG_HOST", "\"https://posthog-poc.lab.element.dev\""
buildConfigField "String", "ANALYTICS_POSTHOG_API_KEY", "\"rs-pJjsYJTuAkXJfhaMmPUNBhWliDyTKLOOxike6ck8\""
buildConfigField "String", "ANALYTICS_POLICY_URL", "\"https://element.io/cookie-policy\""
signingConfig signingConfigs.debug
}
@ -250,6 +251,7 @@ android {
// Analytics. Set to empty strings to just disable analytics
buildConfigField "String", "ANALYTICS_POSTHOG_HOST", "\"https://posthog.hss.element.io\""
buildConfigField "String", "ANALYTICS_POSTHOG_API_KEY", "\"phc_Jzsm6DTm6V2705zeU5dcNvQDlonOR68XvX2sh1sEOHO\""
buildConfigField "String", "ANALYTICS_POLICY_URL", "\"https://element.io/cookie-policy\""
postprocessing {
removeUnusedCode true

View file

@ -310,6 +310,7 @@
<activity android:name=".features.terms.ReviewTermsActivity" />
<activity android:name=".features.widgets.WidgetActivity" />
<activity android:name=".features.pin.PinActivity" />
<activity android:name=".features.analytics.ui.consent.AnalyticsOptInActivity" />
<activity android:name=".features.home.room.detail.search.SearchActivity" />
<activity android:name=".features.usercode.UserCodeActivity" />
<activity android:name=".features.call.transfer.CallTransferActivity" />

View file

@ -24,6 +24,7 @@ import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.android.components.ActivityComponent
import dagger.multibindings.IntoMap
import im.vector.app.features.analytics.ui.consent.AnalyticsOptInFragment
import im.vector.app.features.attachments.preview.AttachmentsPreviewFragment
import im.vector.app.features.contactsbook.ContactsBookFragment
import im.vector.app.features.crypto.keysbackup.settings.KeysBackupSettingsFragment
@ -519,6 +520,11 @@ interface FragmentModule {
@FragmentKey(BreadcrumbsFragment::class)
fun bindBreadcrumbsFragment(fragment: BreadcrumbsFragment): Fragment
@Binds
@IntoMap
@FragmentKey(AnalyticsOptInFragment::class)
fun bindAnalyticsOptInFragment(fragment: AnalyticsOptInFragment): Fragment
@Binds
@IntoMap
@FragmentKey(EmojiChooserFragment::class)

View file

@ -20,5 +20,4 @@ import im.vector.app.core.platform.VectorViewModelAction
sealed class AnalyticsConsentViewActions : VectorViewModelAction {
data class SetUserConsent(val userConsent: Boolean) : AnalyticsConsentViewActions()
object OnGetStarted : AnalyticsConsentViewActions()
}

View file

@ -23,7 +23,6 @@ import dagger.assisted.AssistedInject
import im.vector.app.core.di.MavericksAssistedViewModelFactory
import im.vector.app.core.di.hiltMavericksViewModelFactory
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.EmptyViewEvents
import im.vector.app.core.platform.VectorViewModel
import im.vector.app.features.analytics.VectorAnalytics
import kotlinx.coroutines.launch
@ -31,7 +30,7 @@ import kotlinx.coroutines.launch
class AnalyticsConsentViewModel @AssistedInject constructor(
@Assisted initialState: AnalyticsConsentViewState,
private val analytics: VectorAnalytics
) : VectorViewModel<AnalyticsConsentViewState, AnalyticsConsentViewActions, EmptyViewEvents>(initialState) {
) : VectorViewModel<AnalyticsConsentViewState, AnalyticsConsentViewActions, AnalyticsOptInViewEvents>(initialState) {
@AssistedFactory
interface Factory : MavericksAssistedViewModelFactory<AnalyticsConsentViewModel, AnalyticsConsentViewState> {
@ -56,24 +55,14 @@ class AnalyticsConsentViewModel @AssistedInject constructor(
override fun handle(action: AnalyticsConsentViewActions) {
when (action) {
is AnalyticsConsentViewActions.SetUserConsent -> handleSetUserConsent(action)
AnalyticsConsentViewActions.OnGetStarted -> handleOnScreenLeft()
}.exhaustive
}
private fun handleSetUserConsent(action: AnalyticsConsentViewActions.SetUserConsent) {
viewModelScope.launch {
analytics.setUserConsent(action.userConsent)
if (!action.userConsent) {
// User explicitly changed the default value, let's avoid reverting to the default value
analytics.setDidAskUserConsent()
}
}
}
private fun handleOnScreenLeft() {
// Whatever the state of the box, consider the user acknowledge it
viewModelScope.launch {
analytics.setDidAskUserConsent()
_viewEvents.post(AnalyticsOptInViewEvents.OnDataSaved)
}
}
}

View file

@ -0,0 +1,49 @@
/*
* Copyright (c) 2020 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.app.features.analytics.ui.consent
import com.airbnb.mvrx.viewModel
import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.R
import im.vector.app.core.extensions.addFragment
import im.vector.app.core.platform.VectorBaseActivity
import im.vector.app.databinding.ActivitySimpleBinding
/**
* Simple container for AnalyticsOptInFragment
*/
@AndroidEntryPoint
class AnalyticsOptInActivity : VectorBaseActivity<ActivitySimpleBinding>() {
private val viewModel: AnalyticsConsentViewModel by viewModel()
override fun getBinding() = ActivitySimpleBinding.inflate(layoutInflater)
override fun getCoordinatorLayout() = views.coordinatorLayout
override fun initUiAndData() {
if (isFirstCreation()) {
addFragment(R.id.simpleFragmentContainer, AnalyticsOptInFragment::class.java)
}
viewModel.observeViewEvents {
when (it) {
AnalyticsOptInViewEvents.OnDataSaved -> finish()
}
}
}
}

View file

@ -0,0 +1,67 @@
/*
* 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.ui.consent
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.airbnb.mvrx.activityViewModel
import im.vector.app.BuildConfig
import im.vector.app.R
import im.vector.app.core.extensions.setTextWithColoredPart
import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.core.utils.openUrlInChromeCustomTab
import im.vector.app.databinding.FragmentAnalyticsOptinBinding
import javax.inject.Inject
class AnalyticsOptInFragment @Inject constructor(
) : VectorBaseFragment<FragmentAnalyticsOptinBinding>() {
// Share the view model with the Activity so that the Activity
// can decide what to do when the data has been saved
private val viewModel: AnalyticsConsentViewModel by activityViewModel()
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentAnalyticsOptinBinding {
return FragmentAnalyticsOptinBinding.inflate(inflater, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setupLink()
setupListeners()
}
private fun setupListeners() {
views.submit.debouncedClicks {
viewModel.handle(AnalyticsConsentViewActions.SetUserConsent(userConsent = true))
}
views.later.debouncedClicks {
viewModel.handle(AnalyticsConsentViewActions.SetUserConsent(userConsent = false))
}
}
private fun setupLink() {
views.submit.setTextWithColoredPart(
fullTextRes = R.string.analytics_opt_in_content,
coloredTextRes = R.string.analytics_opt_in_content_link,
onClick = {
openUrlInChromeCustomTab(requireContext(), null, BuildConfig.ANALYTICS_POLICY_URL)
}
)
}
}

View file

@ -0,0 +1,23 @@
/*
* 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.ui.consent
import im.vector.app.core.platform.VectorViewEvents
sealed interface AnalyticsOptInViewEvents : VectorViewEvents {
object OnDataSaved : AnalyticsOptInViewEvents
}

View file

@ -22,15 +22,10 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.view.isVisible
import com.airbnb.mvrx.fragmentViewModel
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import im.vector.app.BuildConfig
import im.vector.app.R
import im.vector.app.databinding.FragmentLoginSplashBinding
import im.vector.app.features.analytics.AnalyticsConfig
import im.vector.app.features.analytics.ui.consent.AnalyticsConsentViewActions
import im.vector.app.features.analytics.ui.consent.AnalyticsConsentViewModel
import im.vector.app.features.analytics.ui.consent.AnalyticsConsentViewState
import im.vector.app.features.settings.VectorPreferences
import org.matrix.android.sdk.api.failure.Failure
import java.net.UnknownHostException
@ -43,8 +38,6 @@ class LoginSplashFragment @Inject constructor(
private val vectorPreferences: VectorPreferences
) : AbstractLoginFragment<FragmentLoginSplashBinding>() {
private val analyticsConsentViewModel: AnalyticsConsentViewModel by fragmentViewModel()
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentLoginSplashBinding {
return FragmentLoginSplashBinding.inflate(inflater, container, false)
}
@ -53,24 +46,10 @@ class LoginSplashFragment @Inject constructor(
super.onViewCreated(view, savedInstanceState)
setupViews()
observeAnalyticsState()
}
private fun observeAnalyticsState() {
analyticsConsentViewModel.onEach(AnalyticsConsentViewState::shouldCheckTheBox) {
views.loginSplashAnalyticsConsent.isChecked = it
}
}
private fun setupViews() {
views.loginSplashSubmit.debouncedClicks { getStarted() }
// setOnCheckedChangeListener is to annoying since it does not distinguish user changes and code changes
views.loginSplashAnalyticsConsent.isVisible = AnalyticsConfig.isAnalyticsEnabled()
views.loginSplashAnalyticsConsent.setOnClickListener {
analyticsConsentViewModel.handle(AnalyticsConsentViewActions.SetUserConsent(
views.loginSplashAnalyticsConsent.isChecked
))
}
if (BuildConfig.DEBUG || vectorPreferences.developerMode()) {
views.loginSplashVersion.isVisible = true
@ -82,7 +61,6 @@ class LoginSplashFragment @Inject constructor(
}
private fun getStarted() {
analyticsConsentViewModel.handle(AnalyticsConsentViewActions.OnGetStarted)
loginViewModel.handle(LoginAction.OnGetStarted(resetLoginConfig = false))
}

View file

@ -36,6 +36,7 @@ import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.error.fatalError
import im.vector.app.core.platform.VectorBaseActivity
import im.vector.app.core.utils.toast
import im.vector.app.features.analytics.ui.consent.AnalyticsOptInActivity
import im.vector.app.features.call.conference.JitsiCallViewModel
import im.vector.app.features.call.conference.VectorJitsiActivity
import im.vector.app.features.call.transfer.CallTransferActivity
@ -404,6 +405,10 @@ class DefaultNavigator @Inject constructor(
}
}
override fun openAnalyticsOptIn(context: Context) {
context.startActivity(Intent(context, AnalyticsOptInActivity::class.java))
}
override fun openTerms(context: Context,
activityResultLauncher: ActivityResultLauncher<Intent>,
serviceType: TermsService.ServiceType,

View file

@ -105,6 +105,8 @@ interface Navigator {
fun openBigImageViewer(activity: Activity, sharedElement: View?, mxcUrl: String?, title: String?)
fun openAnalyticsOptIn(context: Context)
fun openPinCode(context: Context,
activityResultLauncher: ActivityResultLauncher<Intent>,
pinMode: PinMode)

View file

@ -204,20 +204,9 @@
android:layout_height="wrap_content"
android:textColor="?vctr_content_secondary"
android:visibility="gone"
app:layout_constraintBottom_toTopOf="@id/loginSplashAnalyticsConsent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
tools:text="@string/settings_version"
tools:visibility="visible" />
<CheckBox
android:id="@+id/loginSplashAnalyticsConsent"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/analytics_consent_splash"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
tools:visibility="visible" />
</androidx.constraintlayout.widget.ConstraintLayout>