diff --git a/app/src/main/java/net/helcel/cowspent/android/settings/SettingsScreen.kt b/app/src/main/java/net/helcel/cowspent/android/settings/SettingsScreen.kt index 9263187..b1c344c 100644 --- a/app/src/main/java/net/helcel/cowspent/android/settings/SettingsScreen.kt +++ b/app/src/main/java/net/helcel/cowspent/android/settings/SettingsScreen.kt @@ -31,6 +31,7 @@ import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material.icons.filled.AccountCircle import androidx.compose.material.icons.filled.Archive import androidx.compose.material.icons.filled.Brightness2 +import androidx.compose.material.icons.filled.Group import androidx.compose.material.icons.filled.Info import androidx.compose.material.icons.filled.Palette import androidx.compose.material.icons.filled.Sync @@ -84,6 +85,7 @@ fun SettingsScreen( val keyOfflineMode = stringResource(R.string.pref_key_offline_mode) val keyShowArchived = stringResource(R.string.pref_key_show_archived) val keyBetaFeatures = stringResource(R.string.pref_key_beta_features) + val keyStatsIncludeDeactivated = stringResource(R.string.pref_key_stats_include_deactivated) val keyAutoSyncOnOpen = stringResource(R.string.pref_key_auto_sync_on_open) val keyFillNewBillFromLast = stringResource(R.string.pref_key_fill_new_bill_from_last) val keyLastAccountSync = stringResource(R.string.pref_key_last_account_sync_timestamp) @@ -132,6 +134,9 @@ fun SettingsScreen( var fillNewBillFromLast by remember(keyFillNewBillFromLast) { mutableStateOf(sharedPreferences.getBoolean(keyFillNewBillFromLast, false)) } + var statsIncludeDeactivated by remember(keyStatsIncludeDeactivated) { + mutableStateOf(sharedPreferences.getBoolean(keyStatsIncludeDeactivated, false)) + } val syncIntervals = SyncSettings.INTERVAL_CHOICES_MINUTES val syncIntervalLabels = listOf( @@ -304,6 +309,18 @@ fun SettingsScreen( } ) + SettingsSwitchPreference( + title = stringResource(R.string.settings_stats_include_deactivated), + icon = Icons.Default.Group, + checked = statsIncludeDeactivated, + onCheckedChange = { + statsIncludeDeactivated = it + sharedPreferences.edit { + putBoolean(keyStatsIncludeDeactivated, it) + } + } + ) + SettingsSliderPreference( title = stringResource(R.string.settings_auto_sync_on_open), summary = stringResource(R.string.settings_auto_sync_on_open_summary), diff --git a/app/src/main/java/net/helcel/cowspent/android/statistics/ProjectSankeyDiagram.kt b/app/src/main/java/net/helcel/cowspent/android/statistics/ProjectSankeyDiagram.kt index 58f4d9e..509d614 100644 --- a/app/src/main/java/net/helcel/cowspent/android/statistics/ProjectSankeyDiagram.kt +++ b/app/src/main/java/net/helcel/cowspent/android/statistics/ProjectSankeyDiagram.kt @@ -121,17 +121,19 @@ fun ProjectSankeyDiagram( val catMap = mutableMapOf() activeBills.forEach { bill -> - val totalWeight = bill.billOwers.sumOf { membersMap[it.memberId]?.weight ?: 1.0 } + val totalWeight = bill.billOwers.sumOf { membersMap[it.memberId]?.weight ?: 0.0 } if (totalWeight > 0) { if (selectedMemberId == -1L) { catMap[bill.categoryId] = (catMap[bill.categoryId] ?: 0.0) + bill.amount bill.billOwers.forEach { bo -> - val weight = membersMap[bo.memberId]?.weight ?: 1.0 - spentMap[bo.memberId] = (spentMap[bo.memberId] ?: 0.0) + (bill.amount / totalWeight) * weight + if (membersMap.containsKey(bo.memberId)) { + val weight = membersMap[bo.memberId]?.weight ?: 0.0 + spentMap[bo.memberId] = (spentMap[bo.memberId] ?: 0.0) + (bill.amount / totalWeight) * weight + } } } else { bill.billOwers.find { it.memberId == selectedMemberId }?.let { bo -> - val weight = membersMap[bo.memberId]?.weight ?: 1.0 + val weight = membersMap[bo.memberId]?.weight ?: 0.0 catMap[bill.categoryId] = (catMap[bill.categoryId] ?: 0.0) + (bill.amount / totalWeight) * weight spentMap[selectedMemberId] = (spentMap[selectedMemberId] ?: 0.0) + (bill.amount / totalWeight) * weight } @@ -344,7 +346,9 @@ private fun SankeyContent( scope.launch { topFocalIndex.animateTo(index.toFloat(), spring(Spring.DampingRatioLowBouncy, Spring.StiffnessLow)) } } else Modifier), contentAlignment = Alignment.Center) { Column(modifier = Modifier.fillMaxSize().background(color = member?.let { Color(it.r ?: 128, it.g ?: 128, it.b ?: 128) } ?: Color.Gray, shape = RoundedCornerShape(10.dp)), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center) { - Text(text = member?.name ?: "???", color = Color.White, fontWeight = FontWeight.Bold, maxLines = 1, overflow = TextOverflow.Ellipsis, textAlign = TextAlign.Center, modifier = Modifier.wrapContentWidth(unbounded = true)) + if (wDp >= 32.dp) { + Text(text = member?.name ?: "???", color = Color.White, fontWeight = FontWeight.Bold, maxLines = 1, overflow = TextOverflow.Ellipsis, textAlign = TextAlign.Center, modifier = Modifier.wrapContentWidth(unbounded = true)) + } if (wDp >= 40.dp) Text(text = formatShortValue(amount), fontSize = 12.sp, color = Color.White.copy(alpha = 0.9f), textAlign = TextAlign.Center) } } @@ -382,7 +386,9 @@ private fun SankeyContent( scope.launch { bottomFocalIndex.animateTo(index.toFloat(), spring(Spring.DampingRatioLowBouncy, Spring.StiffnessLow)) } } else Modifier), contentAlignment = Alignment.Center) { Column(modifier = Modifier.fillMaxSize().background(color = category?.color?.let { try { Color(it.toColorInt()) } catch (_: Exception) { Color(0xFF999999) } } ?: Color(0xFF999999), shape = RoundedCornerShape(10.dp)), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center) { - Text(text = category?.icon ?: "❔", fontSize = 20.sp, textAlign = TextAlign.Center, modifier = Modifier.wrapContentWidth(unbounded = true)) + if (wDp >= 24.dp) { + Text(text = category?.icon ?: "❔", fontSize = 20.sp, textAlign = TextAlign.Center, modifier = Modifier.wrapContentWidth(unbounded = true)) + } if (wDp >= 40.dp) Text(text = formatShortValue(amount), fontSize = 12.sp, color = Color.White.copy(alpha = 0.9f), textAlign = TextAlign.Center) } } diff --git a/app/src/main/java/net/helcel/cowspent/android/statistics/ProjectStatisticsScreen.kt b/app/src/main/java/net/helcel/cowspent/android/statistics/ProjectStatisticsScreen.kt index b231c7c..a4c140a 100644 --- a/app/src/main/java/net/helcel/cowspent/android/statistics/ProjectStatisticsScreen.kt +++ b/app/src/main/java/net/helcel/cowspent/android/statistics/ProjectStatisticsScreen.kt @@ -40,6 +40,8 @@ fun ProjectStatisticsScreen( "Sankey" ) + val keyStatsIncludeDeactivated = stringResource(R.string.pref_key_stats_include_deactivated) + Scaffold( topBar = { TopAppBar( @@ -103,7 +105,11 @@ fun ProjectStatisticsScreen( val bills = db.getBillsOfProject(proj.id) val categories = db.getCategories(proj.id) val paymentModes = db.getPaymentModes(proj.id) - StatisticsData(members, bills, categories, paymentModes) + + val includeDeactivated = prefs.getBoolean(keyStatsIncludeDeactivated, false) + val filteredMembers = if (includeDeactivated) members else members.filter { it.isActivated } + + StatisticsData(filteredMembers, bills, categories, paymentModes) } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 9bdf3a8..0b8973e 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -143,6 +143,7 @@ Enable experimental features. Use at your own risk. Auto-fill from last bill Pre-fill payer, category, mode and owers from the last bill created in the project. + Include deactivated members in stats Sync interval How often to refresh the account and all projects when opening the app. 1 minute @@ -170,6 +171,7 @@ offlineMode showArchived betaFeatures + statsIncludeDeactivated autoSyncOnOpen lastAccountSyncTimestamp fillNewBillFromLast