mirror of
https://github.com/d4rken-org/capod.git
synced 2026-09-16 11:16:12 -04:00
feat(overview): Gate dashboard device count for free users
Free users see only their highest-priority profiled device. Additional devices are hidden behind an upgrade card showing the count and a flavor-aware CTA (Upgrade/Donate).
This commit is contained in:
@@ -46,6 +46,7 @@ import eu.darken.capod.common.navigation.NavigationEventHandler
|
||||
import eu.darken.capod.common.permissions.Permission
|
||||
import eu.darken.capod.common.upgrade.UpgradeRepo
|
||||
import eu.darken.capod.main.ui.overview.cards.BluetoothDisabledCard
|
||||
import eu.darken.capod.main.ui.overview.cards.DeviceLimitUpgradeCard
|
||||
import eu.darken.capod.main.ui.overview.cards.DualPodsCard
|
||||
import eu.darken.capod.main.ui.overview.cards.MonitoringActiveCard
|
||||
import eu.darken.capod.main.ui.overview.cards.NoProfilesCard
|
||||
@@ -221,10 +222,10 @@ fun OverviewScreen(
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Profiled device cards
|
||||
// 4. Profiled device cards (limited to 1 for free users)
|
||||
if (!state.isScanBlocked && state.isBluetoothEnabled) {
|
||||
items(
|
||||
items = state.profiledDevices,
|
||||
items = state.visibleProfiledDevices,
|
||||
key = { it.identifier?.toString() ?: it.hashCode() },
|
||||
) { device ->
|
||||
PodDeviceCard(
|
||||
@@ -236,6 +237,17 @@ fun OverviewScreen(
|
||||
)
|
||||
}
|
||||
|
||||
// 4b. Upgrade card when additional devices are hidden
|
||||
if (state.hiddenProfiledDeviceCount > 0) {
|
||||
item(key = "device_limit_upgrade") {
|
||||
DeviceLimitUpgradeCard(
|
||||
hiddenCount = state.hiddenProfiledDeviceCount,
|
||||
upgradeType = state.upgradeInfo.type,
|
||||
onUpgrade = onUpgrade,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// 5. Monitoring active card
|
||||
if (state.profiles.isNotEmpty() && state.devices.isEmpty()) {
|
||||
item(key = "monitoring_active") {
|
||||
|
||||
@@ -135,6 +135,9 @@ class OverviewViewModel @Inject constructor(
|
||||
) {
|
||||
val isScanBlocked: Boolean get() = permissions.any { it.isScanBlocking }
|
||||
val profiledDevices: List<PodDevice> get() = devices.filter { it.profileId != null }
|
||||
val visibleProfiledDevices: List<PodDevice>
|
||||
get() = if (upgradeInfo.isPro) profiledDevices else profiledDevices.take(FREE_DEVICE_LIMIT)
|
||||
val hiddenProfiledDeviceCount: Int get() = profiledDevices.size - visibleProfiledDevices.size
|
||||
val unmatchedDevices: List<PodDevice> get() = devices.filter { it.profileId == null }
|
||||
}
|
||||
|
||||
@@ -187,6 +190,7 @@ class OverviewViewModel @Inject constructor(
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val FREE_DEVICE_LIMIT = 1
|
||||
private val TAG = logTag("Overview", "VM")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
package eu.darken.capod.main.ui.overview.cards
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.twotone.Stars
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.pluralStringResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import eu.darken.capod.R
|
||||
import eu.darken.capod.common.compose.Preview2
|
||||
import eu.darken.capod.common.compose.PreviewWrapper
|
||||
import eu.darken.capod.common.upgrade.UpgradeRepo
|
||||
|
||||
@Composable
|
||||
fun DeviceLimitUpgradeCard(
|
||||
hiddenCount: Int,
|
||||
upgradeType: UpgradeRepo.Type,
|
||||
onUpgrade: () -> Unit,
|
||||
) {
|
||||
Card(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(8.dp),
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.padding(16.dp),
|
||||
) {
|
||||
Text(
|
||||
text = pluralStringResource(R.plurals.overview_more_devices_upgrade, hiddenCount, hiddenCount),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
|
||||
Text(
|
||||
text = stringResource(R.string.upgrade_capod_description),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
Button(
|
||||
onClick = onUpgrade,
|
||||
modifier = Modifier.align(Alignment.End),
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.TwoTone.Stars,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.padding(end = 8.dp),
|
||||
)
|
||||
Text(
|
||||
text = stringResource(
|
||||
when (upgradeType) {
|
||||
UpgradeRepo.Type.FOSS -> R.string.general_donate_action
|
||||
UpgradeRepo.Type.GPLAY -> R.string.general_upgrade_action
|
||||
}
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview2
|
||||
@Composable
|
||||
private fun DeviceLimitUpgradeCardGplayPreview() = PreviewWrapper {
|
||||
DeviceLimitUpgradeCard(
|
||||
hiddenCount = 2,
|
||||
upgradeType = UpgradeRepo.Type.GPLAY,
|
||||
onUpgrade = {},
|
||||
)
|
||||
}
|
||||
|
||||
@Preview2
|
||||
@Composable
|
||||
private fun DeviceLimitUpgradeCardFossPreview() = PreviewWrapper {
|
||||
DeviceLimitUpgradeCard(
|
||||
hiddenCount = 1,
|
||||
upgradeType = UpgradeRepo.Type.FOSS,
|
||||
onUpgrade = {},
|
||||
)
|
||||
}
|
||||
@@ -210,6 +210,10 @@
|
||||
<item quantity="one">%d device without matching profile</item>
|
||||
<item quantity="other">%d devices without matching profile</item>
|
||||
</plurals>
|
||||
<plurals name="overview_more_devices_upgrade">
|
||||
<item quantity="one">%d more device</item>
|
||||
<item quantity="other">%d more devices</item>
|
||||
</plurals>
|
||||
|
||||
<string name="permission_bluetooth_connect_label">Bluetooth connect</string>
|
||||
<string name="permission_bluetooth_connect_description">This app requires the \"Bluetooth connect\" permission to interact with paired devices and initiate connections.</string>
|
||||
|
||||
Reference in New Issue
Block a user