Skip to content

Commit

Permalink
Implement turning on/off UI event logs
Browse files Browse the repository at this point in the history
  • Loading branch information
abdrasulov committed Jun 4, 2024
1 parent 3b00854 commit 9a3be26
Show file tree
Hide file tree
Showing 8 changed files with 166 additions and 14 deletions.
27 changes: 27 additions & 0 deletions app/src/main/java/io/horizontalsystems/bankwallet/core/App.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package io.horizontalsystems.bankwallet.core

import android.content.Context
import android.content.SharedPreferences
import android.content.pm.PackageManager
import android.content.res.Configuration
import android.os.Build
import android.util.Log
Expand Down Expand Up @@ -117,6 +118,7 @@ import io.reactivex.plugins.RxJavaPlugins
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import java.security.MessageDigest
import java.util.logging.Level
import java.util.logging.Logger
import androidx.work.Configuration as WorkConfiguration
Expand Down Expand Up @@ -515,6 +517,31 @@ class App : CoreApp(), WorkConfiguration.Provider, ImageLoaderFactory {
localeAwareContext(this)
}

override fun getApplicationSignatures() = try {
val signatureList = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
val signingInfo = packageManager.getPackageInfo(
packageName,
PackageManager.GET_SIGNING_CERTIFICATES
).signingInfo

when {
signingInfo.hasMultipleSigners() -> signingInfo.apkContentsSigners // Send all with apkContentsSigners
else -> signingInfo.signingCertificateHistory // Send one with signingCertificateHistory
}
} else {
packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES).signatures
}

signatureList.map {
val digest = MessageDigest.getInstance("SHA")
digest.update(it.toByteArray())
digest.digest()
}
} catch (e: Exception) {
// Handle error
emptyList()
}

private fun startTasks() {
coroutineScope.launch {
EthereumKit.init()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ interface ILocalStorage {
var utxoExpertModeEnabled: Boolean
var rbfEnabled: Boolean
var statsLastSyncTime: Long
var uiStatsEnabled: Boolean?

val utxoExpertModeEnabledFlow: StateFlow<Boolean>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ class LocalStorageManager(
private val RBF_ENABLED = "rbf_enabled"
private val STATS_SYNC_TIME = "stats_sync_time"
private val PRICE_CHANGE_INTERVAL = "price_change_interval"
private val UI_STATS_ENABLED = "ui_stats_enabled"

private val _utxoExpertModeEnabledFlow = MutableStateFlow(false)
override val utxoExpertModeEnabledFlow = _utxoExpertModeEnabledFlow
Expand Down Expand Up @@ -545,4 +546,20 @@ class LocalStorageManager(
}

override val priceChangeIntervalFlow = MutableStateFlow(priceChangeInterval)

override var uiStatsEnabled: Boolean?
get() = when {
preferences.contains(UI_STATS_ENABLED) -> {
preferences.getBoolean(UI_STATS_ENABLED, false)
}
else -> null
}
set(value) {
val editor = preferences.edit()
if (value == null) {
editor.remove(UI_STATS_ENABLED).apply()
} else {
editor.putBoolean(UI_STATS_ENABLED, value).apply()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,12 @@ import io.horizontalsystems.bankwallet.modules.metricchart.MetricsType
import io.horizontalsystems.bankwallet.modules.metricchart.ProChartModule
import io.horizontalsystems.bankwallet.modules.transactionInfo.options.SpeedUpCancelType
import io.horizontalsystems.bankwallet.modules.transactions.FilterTransactionType
import io.horizontalsystems.core.toHexString
import io.horizontalsystems.hdwalletkit.HDExtendedKey
import io.horizontalsystems.marketkit.models.HsTimePeriod
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import java.time.Instant
import java.util.concurrent.Executors

Expand All @@ -36,13 +40,36 @@ class StatsManager(
private val statsDao: StatsDao,
private val localStorage: ILocalStorage,
private val marketKit: MarketKitWrapper,
private val appConfigProvider: AppConfigProvider
private val appConfigProvider: AppConfigProvider,
) {
var uiStatsEnabled = getInitialUiStatsEnabled()
private set

private val _uiStatsEnabledFlow = MutableStateFlow(uiStatsEnabled)
val uiStatsEnabledFlow = _uiStatsEnabledFlow.asStateFlow()

private val gson by lazy { Gson() }
private val executor = Executors.newCachedThreadPool()
private val syncInterval = 0 //60 * 60 // 1H in seconds
private val syncInterval = 60 * 60 // 1H in seconds

private fun getInitialUiStatsEnabled(): Boolean {
val uiStatsEnabled = localStorage.uiStatsEnabled
if (uiStatsEnabled != null) return uiStatsEnabled

val signatures = listOf(
"b797339fb356afce5160fe49274ee17a1c1816db", // appcenter
"5afb2517b06caac7f108ba9d96ad826f1c4ba30c", // hs
)

val applicationSignatures = App.instance.getApplicationSignatures()
return applicationSignatures.any {
signatures.contains(it.toHexString())
}
}

fun logStat(eventPage: StatPage, eventSection: StatSection? = null, event: StatEvent) {
if (!uiStatsEnabled) return

executor.submit {
try {
val eventMap = buildMap {
Expand All @@ -65,6 +92,8 @@ class StatsManager(
}

fun sendStats() {
if (!uiStatsEnabled) return

executor.submit {
try {
val statLastSyncTime = localStorage.statsLastSyncTime
Expand All @@ -88,6 +117,12 @@ class StatsManager(
}
}

fun toggleUiStats(enabled: Boolean) {
localStorage.uiStatsEnabled = enabled
uiStatsEnabled = enabled
_uiStatsEnabledFlow.update { enabled }
}

}

val BalanceSortType.statSortType
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,23 +13,36 @@ import androidx.compose.foundation.layout.width
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.Divider
import androidx.compose.material.Icon
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
import io.horizontalsystems.bankwallet.R
import io.horizontalsystems.bankwallet.ui.compose.ComposeAppTheme
import io.horizontalsystems.bankwallet.ui.compose.components.AppBar
import io.horizontalsystems.bankwallet.ui.compose.components.HFillSpacer
import io.horizontalsystems.bankwallet.ui.compose.components.HSpacer
import io.horizontalsystems.bankwallet.ui.compose.components.HsBackButton
import io.horizontalsystems.bankwallet.ui.compose.components.HsSwitch
import io.horizontalsystems.bankwallet.ui.compose.components.InfoTextBody

import io.horizontalsystems.bankwallet.ui.compose.components.VSpacer
import io.horizontalsystems.bankwallet.ui.compose.components.body_leah
import io.horizontalsystems.bankwallet.ui.compose.components.cell.CellUniversal
import io.horizontalsystems.bankwallet.ui.compose.components.cell.SectionUniversalLawrence

@Composable
fun PrivacyScreen(navController: NavController) {
val viewModel = viewModel<PrivacyViewModel>(factory = PrivacyViewModel.Factory())

val uiState = viewModel.uiState

Column(
modifier = Modifier
.fillMaxSize()
Expand All @@ -46,16 +59,32 @@ fun PrivacyScreen(navController: NavController) {
.weight(1f)
.verticalScroll(rememberScrollState())
) {

InfoTextBody(
text = stringResource(R.string.Privacy_Information),
)

BulletedText(R.string.Privacy_BulletedText1)
BulletedText(R.string.Privacy_BulletedText2)
BulletedText(R.string.Privacy_BulletedText3)
BulletedText(R.string.Privacy_BulletedText4)
Spacer(modifier = Modifier.height(32.dp))
VSpacer(height = 16.dp)
SectionUniversalLawrence {
CellUniversal {
Icon(
painter = painterResource(id = R.drawable.ic_share_24px),
contentDescription = "Share",
tint = ComposeAppTheme.colors.grey
)
HSpacer(width = 16.dp)
body_leah(text = stringResource(R.string.ShareUiData))
HFillSpacer(minWidth = 8.dp)
HsSwitch(
checked = uiState.uiStatsEnabled,
onCheckedChange = {
viewModel.toggleUiStats(it)
}
)
}
}
}

Divider(
Expand All @@ -81,15 +110,17 @@ fun PrivacyScreen(navController: NavController) {

@Composable
private fun BulletedText(@StringRes text: Int) {
Row(Modifier.padding(vertical = 12.dp)) {
Row(
modifier = Modifier.padding(start = 24.dp, top = 12.dp, end = 32.dp, bottom = 12.dp)
) {
Text(
text = "\u2022 ",
style = ComposeAppTheme.typography.body,
color = ComposeAppTheme.colors.bran,
modifier = Modifier.width(32.dp),
modifier = Modifier.width(15.dp),
textAlign = TextAlign.Center
)

HSpacer(width = 8.dp)
Text(
text = stringResource(text),
style = ComposeAppTheme.typography.body,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package io.horizontalsystems.bankwallet.modules.settings.privacy

import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import io.horizontalsystems.bankwallet.core.App
import io.horizontalsystems.bankwallet.core.ViewModelUiState
import io.horizontalsystems.bankwallet.core.stats.StatsManager
import kotlinx.coroutines.launch

class PrivacyViewModel(private val statsManager: StatsManager) : ViewModelUiState<PrivacyUiState>() {
private var uiStatsEnabled = statsManager.uiStatsEnabled

init {
viewModelScope.launch {
statsManager.uiStatsEnabledFlow.collect {
uiStatsEnabled = it
emitState()
}
}
}

override fun createState() = PrivacyUiState(
uiStatsEnabled = uiStatsEnabled
)

fun toggleUiStats(enabled: Boolean) {
statsManager.toggleUiStats(enabled)
}

class Factory : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return PrivacyViewModel(App.statsManager) as T
}
}

}

data class PrivacyUiState(val uiStatsEnabled: Boolean)
10 changes: 5 additions & 5 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1056,11 +1056,11 @@

<!-- Privacy -->

<string name="Privacy_Information">Unstoppable doesn\'t collect any data or use analytics tools that may expose any data about its users. The wallet is designed to ensure a high level of privacy for its users.</string>
<string name="Privacy_BulletedText1">User data always remains on the user\'s device.</string>
<string name="Privacy_BulletedText2">The wallet doesn\'t collect any data about users.</string>
<string name="Privacy_BulletedText3">The wallet doesn\'t share any data about users.</string>
<string name="Privacy_BulletedText4">There are no user accounts or databases keeping user data elsewhere.</string>
<string name="Privacy_Information">Unstoppable doesn\'t collect personal data that expose your private information i.e. coin balances or addresses. While we gather some UI usage statistics, it\'s solely for understanding our user base and app usage trends. That can be disabled if you wish.</string>
<string name="Privacy_BulletedText1">The wallet does not gather any personal data.</string>
<string name="Privacy_BulletedText2">There are no user accounts or databases storing user data.</string>
<string name="Privacy_BulletedText3">If allowed the wallet will share app usage habits with Unstoppable team. This is to understand which features are being used (or not) by our users. Being a privacy focused app we need some way to evaluate our efforts and without this we have no idea whether the features we built are being used or not.</string>
<string name="ShareUiData">Share UI data</string>

<!-- Security Center -->

Expand Down
1 change: 1 addition & 0 deletions core/src/main/java/io/horizontalsystems/core/CoreApp.kt
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ abstract class CoreApp : Application() {
}

abstract fun localizedContext(): Context
abstract fun getApplicationSignatures(): List<ByteArray>

fun localeAwareContext(base: Context): Context {
return LocaleHelper.onAttach(base)
Expand Down

0 comments on commit 9a3be26

Please sign in to comment.