Send analytics option

User can now decide whether or not the app will send analytics (enabled by default)
This commit is contained in:
Sad Ellie 2022-05-28 20:03:07 +03:00
parent 9d5eb22063
commit 8635b4f54c
8 changed files with 109 additions and 28 deletions

View File

@ -11,8 +11,8 @@
android:supportsRtl="true"
android:theme="@style/Theme.Unitto">
<meta-data
android:name="firebase_analytics_collection_deactivated"
android:value="${analytics_deactivated}" />
android:name="firebase_analytics_collection_enabled"
android:value="false" />
<meta-data
android:name="firebase_crashlytics_collection_enabled"
android:value="${crashlytics_enabled}" />

View File

@ -1,10 +1,7 @@
package com.sadellie.unitto.data.preferences
import android.content.Context
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.intPreferencesKey
import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.datastore.preferences.core.*
import androidx.datastore.preferences.preferencesDataStore
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.flow.Flow
@ -26,6 +23,7 @@ object UserPreferenceKeys {
val OUTPUT_FORMAT = intPreferencesKey("OUTPUT_FORMAT_PREF_KEY")
val LATEST_LEFT_SIDE = stringPreferencesKey("LATEST_LEFT_SIDE_PREF_KEY")
val LATEST_RIGHT_SIDE = stringPreferencesKey("LATEST_RIGHT_SIDE_PREF_KEY")
val ENABLE_ANALYTICS = booleanPreferencesKey("ENABLE_ANALYTICS_PREF_KEY")
}
/**
@ -56,10 +54,21 @@ class UserPreferences @Inject constructor(@ApplicationContext private val contex
}
}
/**
* Gets boolean from datastore
*
* @param[default] Value to return if didn't find anything on fresh install
*/
fun getItem(key: Preferences.Key<Boolean>, default: Boolean): Flow<Boolean> {
return context.settingsDataStore.data.map {
it[key] ?: default
}
}
/**
* Saves string value by key
*/
suspend fun saveString(key: Preferences.Key<String>, value: String) {
suspend fun saveItem(key: Preferences.Key<String>, value: String) {
context.settingsDataStore.edit {
it[key] = value
}
@ -68,7 +77,16 @@ class UserPreferences @Inject constructor(@ApplicationContext private val contex
/**
* Saves int value by key
*/
suspend fun saveInt(key: Preferences.Key<Int>, value: Int) {
suspend fun saveItem(key: Preferences.Key<Int>, value: Int) {
context.settingsDataStore.edit {
it[key] = value
}
}
/**
* Saves boolean value by key
*/
suspend fun saveItem(key: Preferences.Key<Boolean>, value: Boolean) {
context.settingsDataStore.edit {
it[key] = value
}

View File

@ -7,6 +7,7 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.google.firebase.analytics.FirebaseAnalytics
import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.sadellie.unitto.data.KEY_0
import com.sadellie.unitto.data.KEY_DOT
@ -45,7 +46,7 @@ class MainViewModel @Inject constructor(
fun saveCurrentAppTheme(value: Int) {
viewModelScope.launch {
mySettingsPrefs.saveInt(key = UserPreferenceKeys.CURRENT_APP_THEME, value)
mySettingsPrefs.saveItem(key = UserPreferenceKeys.CURRENT_APP_THEME, value)
}
}
@ -58,7 +59,7 @@ class MainViewModel @Inject constructor(
fun setPrecisionPref(value: Int) {
viewModelScope.launch {
precision = value
mySettingsPrefs.saveInt(UserPreferenceKeys.DIGITS_PRECISION, value)
mySettingsPrefs.saveItem(UserPreferenceKeys.DIGITS_PRECISION, value)
convertValue()
}
}
@ -73,7 +74,7 @@ class MainViewModel @Inject constructor(
separator = value
viewModelScope.launch {
Formatter.setSeparator(value)
mySettingsPrefs.saveInt(UserPreferenceKeys.SEPARATOR, value)
mySettingsPrefs.saveItem(UserPreferenceKeys.SEPARATOR, value)
convertValue()
}
}
@ -93,11 +94,24 @@ class MainViewModel @Inject constructor(
outputFormat = value
// Updating value on disk
viewModelScope.launch {
mySettingsPrefs.saveInt(UserPreferenceKeys.OUTPUT_FORMAT, value)
mySettingsPrefs.saveItem(UserPreferenceKeys.OUTPUT_FORMAT, value)
convertValue()
}
}
/**
* ANALYTICS
*/
var enableAnalytics: Boolean by mutableStateOf(false)
fun setAnalyticsPref(value: Boolean) {
enableAnalytics = value
viewModelScope.launch {
mySettingsPrefs.saveItem(UserPreferenceKeys.ENABLE_ANALYTICS, value)
FirebaseAnalytics.getInstance(application).setAnalyticsCollectionEnabled(enableAnalytics)
}
}
/**
* Unit we converting from (left side)
*/
@ -400,8 +414,8 @@ class MainViewModel @Inject constructor(
*/
fun saveMe() {
viewModelScope.launch {
mySettingsPrefs.saveString(UserPreferenceKeys.LATEST_LEFT_SIDE, unitFrom.unitId)
mySettingsPrefs.saveString(UserPreferenceKeys.LATEST_RIGHT_SIDE, unitTo.unitId)
mySettingsPrefs.saveItem(UserPreferenceKeys.LATEST_LEFT_SIDE, unitFrom.unitId)
mySettingsPrefs.saveItem(UserPreferenceKeys.LATEST_RIGHT_SIDE, unitTo.unitId)
}
}
@ -532,6 +546,9 @@ class MainViewModel @Inject constructor(
* He can choose another unit group and doesn't need to wait for network to appear.
* */
updateCurrenciesBasicUnits()
enableAnalytics = mySettingsPrefs.getItem(UserPreferenceKeys.ENABLE_ANALYTICS, true).first()
FirebaseAnalytics.getInstance(application).setAnalyticsCollectionEnabled(enableAnalytics)
}
}
}

View File

@ -2,12 +2,10 @@ package com.sadellie.unitto.screens.setttings
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.*
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Switch
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
@ -17,22 +15,24 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
/**
* Represents one item in list on Settings screen
* Basic list item for settings screen. By default only has label and support text, clickable.
* This component can be easily modified if you provide additional component to it,
* for example a switch or a checkbox
*
* @param modifier Modifier that is applied to a [Row]
* @param onClick Action to perform when clicking this item
* @param label Big text that is above support text
* @param supportText Smaller text that is below label
* @param label Main text
* @param supportText Text that is located below label
* @param onClick Action to perform when user clicks on this component (whole component is clickable)
* @param content Additional composable: buttons, switches, checkboxes or something else
*/
@Composable
fun SettingsListItem(
modifier: Modifier = Modifier,
onClick: () -> Unit,
private fun BasicSettingsListItem(
label: String,
supportText: String? = null,
onClick: () -> Unit = {},
content: @Composable RowScope.() -> Unit = {}
) {
Row(
modifier = modifier
modifier = Modifier
.fillMaxWidth()
.clickable(
interactionSource = remember { MutableInteractionSource() },
@ -43,7 +43,9 @@ fun SettingsListItem(
verticalAlignment = Alignment.CenterVertically,
) {
Column(
Modifier.padding(horizontal = 0.dp)
Modifier
.padding(horizontal = 0.dp)
.weight(1f) // This makes additional composable to be seen
) {
Text(
modifier = Modifier
@ -62,5 +64,38 @@ fun SettingsListItem(
)
}
}
content()
}
}
/**
* Represents one item in list on Settings screen
*
* @param label Main text
* @param supportText Text that is located below label
* @param onClick Action to perform when user clicks on this component (whole component is clickable)
*/
@Composable
fun SettingsListItem(
label: String,
supportText: String? = null,
onClick: () -> Unit,
) = BasicSettingsListItem(label, supportText, onClick)
/**
* Represents one item in list on Settings screen
*
* @param label Main text
* @param supportText Text that is located below label
* @param switchState Current switch state
* @param onSwitchChange Action to perform when user clicks on this component or just switch
*/
@Composable
fun SettingsListItem(
label: String,
supportText: String? = null,
switchState: Boolean,
onSwitchChange: (Boolean) -> Unit
) = BasicSettingsListItem(label, supportText, { onSwitchChange(switchState) }) {
Switch(checked = switchState, onCheckedChange = { onSwitchChange(!it) })
}

View File

@ -130,6 +130,11 @@ fun SettingsScreen(
)
}
)
SettingsListItem(
label = stringResource(id = R.string.send_usage_statistics),
supportText = stringResource(id = R.string.send_usage_statistics_support),
switchState = mainViewModel.enableAnalytics,
onSwitchChange = { mainViewModel.setAnalyticsPref(!it) })
SettingsListItem(
label = stringResource(R.string.third_party_licenses),
onClick = { navControllerAction(ABOUT_SCREEN) }

View File

@ -718,5 +718,7 @@
<string name="clear_input_description">Clear input</string>
<string name="favorite_button_description">Add or remove unit from favorites</string>
<string name="empty_search_result_description">Empty search result</string>
<string name="send_usage_statistics">Send usage statistics</string>
<string name="send_usage_statistics_support">All data is anonymous and encrypted</string>
</resources>

View File

@ -670,5 +670,7 @@
<string name="sun_mass_short">Солнце M</string>
<string name="pressure">Давление</string>
<string name="acceleration">Ускорение</string>
<string name="send_usage_statistics">Отправлять статистику использования</string>
<string name="send_usage_statistics_support">Все данные анонимны и зашифрованы</string>
</resources>

View File

@ -930,6 +930,8 @@
<string name="privacy_policy">Privacy Policy</string>
<string name="third_party_licenses">Third party licenses</string>
<string name="rate_this_app">Rate this app</string>
<string name="send_usage_statistics">Send usage statistics</string>
<string name="send_usage_statistics_support">All data is anonymous and encrypted</string>
<string name="general_settings_group">General</string>
<string name="additional_settings_group">Additional</string>