mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2025-02-03 12:37:31 +01:00
Analytics: ask user consent at startup (we may iterate later)
This commit is contained in:
parent
b68e9e1f7f
commit
5606a5bfe7
@ -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.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
|
||||||
import im.vector.app.features.call.conference.JitsiCallViewModel
|
import im.vector.app.features.call.conference.JitsiCallViewModel
|
||||||
@ -454,6 +455,11 @@ interface MavericksViewModelModule {
|
|||||||
@MavericksViewModelKey(LoginViewModel::class)
|
@MavericksViewModelKey(LoginViewModel::class)
|
||||||
fun loginViewModelFactory(factory: LoginViewModel.Factory): MavericksAssistedViewModelFactory<*, *>
|
fun loginViewModelFactory(factory: LoginViewModel.Factory): MavericksAssistedViewModelFactory<*, *>
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
@IntoMap
|
||||||
|
@MavericksViewModelKey(AnalyticsConsentViewModel::class)
|
||||||
|
fun analyticsConsentViewModelFactory(factory: AnalyticsConsentViewModel.Factory): MavericksAssistedViewModelFactory<*, *>
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
@IntoMap
|
@IntoMap
|
||||||
@MavericksViewModelKey(HomeServerCapabilitiesViewModel::class)
|
@MavericksViewModelKey(HomeServerCapabilitiesViewModel::class)
|
||||||
|
@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* 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.VectorViewModelAction
|
||||||
|
|
||||||
|
sealed class AnalyticsConsentViewActions : VectorViewModelAction {
|
||||||
|
data class SetUserConsent(val userConsent: Boolean) : AnalyticsConsentViewActions()
|
||||||
|
object OnGetStarted : AnalyticsConsentViewActions()
|
||||||
|
}
|
@ -0,0 +1,79 @@
|
|||||||
|
/*
|
||||||
|
* 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 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.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
|
||||||
|
|
||||||
|
class AnalyticsConsentViewModel @AssistedInject constructor(
|
||||||
|
@Assisted initialState: AnalyticsConsentViewState,
|
||||||
|
private val analytics: VectorAnalytics
|
||||||
|
) : VectorViewModel<AnalyticsConsentViewState, AnalyticsConsentViewActions, EmptyViewEvents>(initialState) {
|
||||||
|
|
||||||
|
@AssistedFactory
|
||||||
|
interface Factory : MavericksAssistedViewModelFactory<AnalyticsConsentViewModel, AnalyticsConsentViewState> {
|
||||||
|
override fun create(initialState: AnalyticsConsentViewState): AnalyticsConsentViewModel
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object : MavericksViewModelFactory<AnalyticsConsentViewModel, AnalyticsConsentViewState> by hiltMavericksViewModelFactory()
|
||||||
|
|
||||||
|
init {
|
||||||
|
observeAnalytics()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun observeAnalytics() {
|
||||||
|
analytics.didAskUserConsent().setOnEach {
|
||||||
|
copy(didAskUserConsent = it)
|
||||||
|
}
|
||||||
|
analytics.getUserConsent().setOnEach {
|
||||||
|
copy(userConsent = it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleOnScreenLeft() {
|
||||||
|
// Whatever the state of the box, consider the user acknowledge it
|
||||||
|
viewModelScope.launch {
|
||||||
|
analytics.setDidAskUserConsent(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* 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 com.airbnb.mvrx.MavericksState
|
||||||
|
|
||||||
|
data class AnalyticsConsentViewState(
|
||||||
|
val userConsent: Boolean = false,
|
||||||
|
val didAskUserConsent: Boolean = false
|
||||||
|
) : MavericksState {
|
||||||
|
val shouldCheckTheBox: Boolean =
|
||||||
|
if (didAskUserConsent) {
|
||||||
|
userConsent
|
||||||
|
} else {
|
||||||
|
// default value
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
@ -22,10 +22,14 @@ import android.view.LayoutInflater
|
|||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
|
import com.airbnb.mvrx.fragmentViewModel
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import im.vector.app.BuildConfig
|
import im.vector.app.BuildConfig
|
||||||
import im.vector.app.R
|
import im.vector.app.R
|
||||||
import im.vector.app.databinding.FragmentLoginSplashBinding
|
import im.vector.app.databinding.FragmentLoginSplashBinding
|
||||||
|
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 im.vector.app.features.settings.VectorPreferences
|
||||||
import org.matrix.android.sdk.api.failure.Failure
|
import org.matrix.android.sdk.api.failure.Failure
|
||||||
import java.net.UnknownHostException
|
import java.net.UnknownHostException
|
||||||
@ -38,6 +42,8 @@ class LoginSplashFragment @Inject constructor(
|
|||||||
private val vectorPreferences: VectorPreferences
|
private val vectorPreferences: VectorPreferences
|
||||||
) : AbstractLoginFragment<FragmentLoginSplashBinding>() {
|
) : AbstractLoginFragment<FragmentLoginSplashBinding>() {
|
||||||
|
|
||||||
|
private val analyticsConsentViewModel: AnalyticsConsentViewModel by fragmentViewModel()
|
||||||
|
|
||||||
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentLoginSplashBinding {
|
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentLoginSplashBinding {
|
||||||
return FragmentLoginSplashBinding.inflate(inflater, container, false)
|
return FragmentLoginSplashBinding.inflate(inflater, container, false)
|
||||||
}
|
}
|
||||||
@ -46,10 +52,23 @@ class LoginSplashFragment @Inject constructor(
|
|||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
setupViews()
|
setupViews()
|
||||||
|
observeAnalyticsState()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun observeAnalyticsState() {
|
||||||
|
analyticsConsentViewModel.onEach(AnalyticsConsentViewState::shouldCheckTheBox) {
|
||||||
|
views.loginSplashAnalyticsConsent.isChecked = it
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupViews() {
|
private fun setupViews() {
|
||||||
views.loginSplashSubmit.debouncedClicks { getStarted() }
|
views.loginSplashSubmit.debouncedClicks { getStarted() }
|
||||||
|
// setOnCheckedChangeListener is to annoying since it does not distinguish user changes and code changes
|
||||||
|
views.loginSplashAnalyticsConsent.setOnClickListener {
|
||||||
|
analyticsConsentViewModel.handle(AnalyticsConsentViewActions.SetUserConsent(
|
||||||
|
views.loginSplashAnalyticsConsent.isChecked
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
if (BuildConfig.DEBUG || vectorPreferences.developerMode()) {
|
if (BuildConfig.DEBUG || vectorPreferences.developerMode()) {
|
||||||
views.loginSplashVersion.isVisible = true
|
views.loginSplashVersion.isVisible = true
|
||||||
@ -61,6 +80,7 @@ class LoginSplashFragment @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun getStarted() {
|
private fun getStarted() {
|
||||||
|
analyticsConsentViewModel.handle(AnalyticsConsentViewActions.OnGetStarted)
|
||||||
loginViewModel.handle(LoginAction.OnGetStarted(resetLoginConfig = false))
|
loginViewModel.handle(LoginAction.OnGetStarted(resetLoginConfig = false))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -204,9 +204,18 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:textColor="?vctr_content_secondary"
|
android:textColor="?vctr_content_secondary"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toTopOf="@id/loginSplashAnalyticsConsent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
tools:text="@string/settings_version"
|
tools:text="@string/settings_version"
|
||||||
tools:visibility="visible" />
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
<CheckBox
|
||||||
|
android:id="@+id/loginSplashAnalyticsConsent"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/analytics_consent_splash"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent" />
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
@ -1377,6 +1377,9 @@
|
|||||||
<string name="template_settings_opt_in_of_analytics_prompt">Please enable analytics to help us improve ${app_name}.</string>
|
<string name="template_settings_opt_in_of_analytics_prompt">Please enable analytics to help us improve ${app_name}.</string>
|
||||||
<string name="settings_opt_in_of_analytics_ok">Yes, I want to help!</string>
|
<string name="settings_opt_in_of_analytics_ok">Yes, I want to help!</string>
|
||||||
|
|
||||||
|
<!-- analytics v2 -->
|
||||||
|
<string name="analytics_consent_splash">Send anonymous usage data to element.io</string>
|
||||||
|
|
||||||
<string name="settings_data_save_mode">Data save mode</string>
|
<string name="settings_data_save_mode">Data save mode</string>
|
||||||
<string name="settings_data_save_mode_summary">Data save mode applies a specific filter so presence updates and typing notifications are filtered out.</string>
|
<string name="settings_data_save_mode_summary">Data save mode applies a specific filter so presence updates and typing notifications are filtered out.</string>
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user