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:supportsRtl="true"
android:theme="@style/Theme.Unitto"> android:theme="@style/Theme.Unitto">
<meta-data <meta-data
android:name="firebase_analytics_collection_deactivated" android:name="firebase_analytics_collection_enabled"
android:value="${analytics_deactivated}" /> android:value="false" />
<meta-data <meta-data
android:name="firebase_crashlytics_collection_enabled" android:name="firebase_crashlytics_collection_enabled"
android:value="${crashlytics_enabled}" /> android:value="${crashlytics_enabled}" />

View File

@ -1,10 +1,7 @@
package com.sadellie.unitto.data.preferences package com.sadellie.unitto.data.preferences
import android.content.Context import android.content.Context
import androidx.datastore.preferences.core.Preferences import androidx.datastore.preferences.core.*
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.intPreferencesKey
import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.datastore.preferences.preferencesDataStore import androidx.datastore.preferences.preferencesDataStore
import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
@ -26,6 +23,7 @@ object UserPreferenceKeys {
val OUTPUT_FORMAT = intPreferencesKey("OUTPUT_FORMAT_PREF_KEY") val OUTPUT_FORMAT = intPreferencesKey("OUTPUT_FORMAT_PREF_KEY")
val LATEST_LEFT_SIDE = stringPreferencesKey("LATEST_LEFT_SIDE_PREF_KEY") val LATEST_LEFT_SIDE = stringPreferencesKey("LATEST_LEFT_SIDE_PREF_KEY")
val LATEST_RIGHT_SIDE = stringPreferencesKey("LATEST_RIGHT_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 * 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 { context.settingsDataStore.edit {
it[key] = value it[key] = value
} }
@ -68,7 +77,16 @@ class UserPreferences @Inject constructor(@ApplicationContext private val contex
/** /**
* Saves int value by key * 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 { context.settingsDataStore.edit {
it[key] = value it[key] = value
} }

View File

@ -7,6 +7,7 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.google.firebase.analytics.FirebaseAnalytics
import com.google.firebase.crashlytics.FirebaseCrashlytics import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.sadellie.unitto.data.KEY_0 import com.sadellie.unitto.data.KEY_0
import com.sadellie.unitto.data.KEY_DOT import com.sadellie.unitto.data.KEY_DOT
@ -45,7 +46,7 @@ class MainViewModel @Inject constructor(
fun saveCurrentAppTheme(value: Int) { fun saveCurrentAppTheme(value: Int) {
viewModelScope.launch { 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) { fun setPrecisionPref(value: Int) {
viewModelScope.launch { viewModelScope.launch {
precision = value precision = value
mySettingsPrefs.saveInt(UserPreferenceKeys.DIGITS_PRECISION, value) mySettingsPrefs.saveItem(UserPreferenceKeys.DIGITS_PRECISION, value)
convertValue() convertValue()
} }
} }
@ -73,7 +74,7 @@ class MainViewModel @Inject constructor(
separator = value separator = value
viewModelScope.launch { viewModelScope.launch {
Formatter.setSeparator(value) Formatter.setSeparator(value)
mySettingsPrefs.saveInt(UserPreferenceKeys.SEPARATOR, value) mySettingsPrefs.saveItem(UserPreferenceKeys.SEPARATOR, value)
convertValue() convertValue()
} }
} }
@ -93,11 +94,24 @@ class MainViewModel @Inject constructor(
outputFormat = value outputFormat = value
// Updating value on disk // Updating value on disk
viewModelScope.launch { viewModelScope.launch {
mySettingsPrefs.saveInt(UserPreferenceKeys.OUTPUT_FORMAT, value) mySettingsPrefs.saveItem(UserPreferenceKeys.OUTPUT_FORMAT, value)
convertValue() 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) * Unit we converting from (left side)
*/ */
@ -400,8 +414,8 @@ class MainViewModel @Inject constructor(
*/ */
fun saveMe() { fun saveMe() {
viewModelScope.launch { viewModelScope.launch {
mySettingsPrefs.saveString(UserPreferenceKeys.LATEST_LEFT_SIDE, unitFrom.unitId) mySettingsPrefs.saveItem(UserPreferenceKeys.LATEST_LEFT_SIDE, unitFrom.unitId)
mySettingsPrefs.saveString(UserPreferenceKeys.LATEST_RIGHT_SIDE, unitTo.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. * He can choose another unit group and doesn't need to wait for network to appear.
* */ * */
updateCurrenciesBasicUnits() 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.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.*
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material.ripple.rememberRipple import androidx.compose.material.ripple.rememberRipple
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Switch
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
@ -17,22 +15,24 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp 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 label Main text
* @param onClick Action to perform when clicking this item * @param supportText Text that is located below label
* @param label Big text that is above support text * @param onClick Action to perform when user clicks on this component (whole component is clickable)
* @param supportText Smaller text that is below label * @param content Additional composable: buttons, switches, checkboxes or something else
*/ */
@Composable @Composable
fun SettingsListItem( private fun BasicSettingsListItem(
modifier: Modifier = Modifier,
onClick: () -> Unit,
label: String, label: String,
supportText: String? = null, supportText: String? = null,
onClick: () -> Unit = {},
content: @Composable RowScope.() -> Unit = {}
) { ) {
Row( Row(
modifier = modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.clickable( .clickable(
interactionSource = remember { MutableInteractionSource() }, interactionSource = remember { MutableInteractionSource() },
@ -43,7 +43,9 @@ fun SettingsListItem(
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
) { ) {
Column( Column(
Modifier.padding(horizontal = 0.dp) Modifier
.padding(horizontal = 0.dp)
.weight(1f) // This makes additional composable to be seen
) { ) {
Text( Text(
modifier = Modifier 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( SettingsListItem(
label = stringResource(R.string.third_party_licenses), label = stringResource(R.string.third_party_licenses),
onClick = { navControllerAction(ABOUT_SCREEN) } onClick = { navControllerAction(ABOUT_SCREEN) }

View File

@ -718,5 +718,7 @@
<string name="clear_input_description">Clear input</string> <string name="clear_input_description">Clear input</string>
<string name="favorite_button_description">Add or remove unit from favorites</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="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> </resources>

View File

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

View File

@ -930,6 +930,8 @@
<string name="privacy_policy">Privacy Policy</string> <string name="privacy_policy">Privacy Policy</string>
<string name="third_party_licenses">Third party licenses</string> <string name="third_party_licenses">Third party licenses</string>
<string name="rate_this_app">Rate this app</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="general_settings_group">General</string>
<string name="additional_settings_group">Additional</string> <string name="additional_settings_group">Additional</string>