mirror of
https://github.com/kavishdevar/librepods.git
synced 2026-05-10 14:45:30 +00:00
android: add demo mode; fix issues on UI start
This commit is contained in:
@@ -21,6 +21,7 @@
|
|||||||
package me.kavishdevar.librepods.composables
|
package me.kavishdevar.librepods.composables
|
||||||
|
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
|
import android.util.Log
|
||||||
import androidx.compose.foundation.Image
|
import androidx.compose.foundation.Image
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.isSystemInDarkTheme
|
import androidx.compose.foundation.isSystemInDarkTheme
|
||||||
@@ -32,6 +33,7 @@ import androidx.compose.foundation.layout.Spacer
|
|||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
|
import androidx.compose.foundation.layout.widthIn
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
@@ -39,6 +41,7 @@ import androidx.compose.ui.Alignment
|
|||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.graphics.ImageBitmap
|
import androidx.compose.ui.graphics.ImageBitmap
|
||||||
|
import androidx.compose.ui.platform.LocalConfiguration
|
||||||
import androidx.compose.ui.res.imageResource
|
import androidx.compose.ui.res.imageResource
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
@@ -74,76 +77,84 @@ fun BatteryView(
|
|||||||
|
|
||||||
val singleDisplayed = remember { mutableStateOf(false) }
|
val singleDisplayed = remember { mutableStateOf(false) }
|
||||||
|
|
||||||
Row {
|
Box(
|
||||||
Column(
|
modifier = Modifier.fillMaxWidth(),
|
||||||
modifier = Modifier.fillMaxWidth(0.5f),
|
contentAlignment = Alignment.Center
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.widthIn(max = 500.dp),
|
||||||
|
horizontalArrangement = Arrangement.Center
|
||||||
) {
|
) {
|
||||||
Image(
|
Column(
|
||||||
bitmap = ImageBitmap.imageResource(budsRes),
|
modifier = Modifier.weight(1f),
|
||||||
contentDescription = stringResource(R.string.buds),
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.padding(8.dp)
|
|
||||||
)
|
|
||||||
|
|
||||||
if (
|
|
||||||
leftCharging == rightCharging &&
|
|
||||||
(leftLevel - rightLevel) in -3..3
|
|
||||||
) {
|
) {
|
||||||
BatteryIndicator(
|
Image(
|
||||||
leftLevel.coerceAtMost(rightLevel),
|
bitmap = ImageBitmap.imageResource(budsRes),
|
||||||
leftCharging
|
contentDescription = stringResource(R.string.buds),
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(8.dp)
|
||||||
)
|
)
|
||||||
singleDisplayed.value = true
|
|
||||||
} else {
|
|
||||||
singleDisplayed.value = false
|
|
||||||
|
|
||||||
Row(
|
if (
|
||||||
modifier = Modifier.fillMaxWidth(),
|
leftCharging == rightCharging &&
|
||||||
horizontalArrangement = Arrangement.Center
|
(leftLevel - rightLevel) in -3..3
|
||||||
) {
|
) {
|
||||||
if (leftLevel > 0 || left?.status != BatteryStatus.DISCONNECTED) {
|
BatteryIndicator(
|
||||||
BatteryIndicator(
|
leftLevel.coerceAtMost(rightLevel),
|
||||||
leftLevel,
|
leftCharging
|
||||||
leftCharging,
|
)
|
||||||
"\uDBC6\uDCE5"
|
singleDisplayed.value = true
|
||||||
)
|
} else {
|
||||||
}
|
singleDisplayed.value = false
|
||||||
|
|
||||||
if (leftLevel > 0 && rightLevel > 0) {
|
Row(
|
||||||
Spacer(modifier = Modifier.width(16.dp))
|
modifier = Modifier.fillMaxWidth(),
|
||||||
}
|
horizontalArrangement = Arrangement.Center
|
||||||
|
) {
|
||||||
|
if (leftLevel > 0 || left?.status != BatteryStatus.DISCONNECTED) {
|
||||||
|
BatteryIndicator(
|
||||||
|
leftLevel,
|
||||||
|
leftCharging,
|
||||||
|
"\uDBC6\uDCE5"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
if (rightLevel > 0 || right?.status != BatteryStatus.DISCONNECTED) {
|
if (leftLevel > 0 && rightLevel > 0) {
|
||||||
BatteryIndicator(
|
Spacer(modifier = Modifier.width(16.dp))
|
||||||
rightLevel,
|
}
|
||||||
rightCharging,
|
|
||||||
"\uDBC6\uDCE8"
|
if (rightLevel > 0 || right?.status != BatteryStatus.DISCONNECTED) {
|
||||||
)
|
BatteryIndicator(
|
||||||
|
rightLevel,
|
||||||
|
rightCharging,
|
||||||
|
"\uDBC6\uDCE8"
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.weight(1f),
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
) {
|
) {
|
||||||
Image(
|
Image(
|
||||||
bitmap = ImageBitmap.imageResource(caseRes),
|
bitmap = ImageBitmap.imageResource(caseRes),
|
||||||
contentDescription = stringResource(R.string.case_alt),
|
contentDescription = stringResource(R.string.case_alt),
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(8.dp)
|
.padding(8.dp)
|
||||||
)
|
|
||||||
|
|
||||||
if (caseLevel > 0 || case?.status != BatteryStatus.DISCONNECTED) {
|
|
||||||
BatteryIndicator(
|
|
||||||
caseLevel,
|
|
||||||
caseCharging,
|
|
||||||
prefix = if (!singleDisplayed.value) "\uDBC3\uDE6C" else ""
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (caseLevel > 0 || case?.status != BatteryStatus.DISCONNECTED) {
|
||||||
|
BatteryIndicator(
|
||||||
|
caseLevel,
|
||||||
|
caseCharging,
|
||||||
|
prefix = if (!singleDisplayed.value) "\uDBC3\uDE6C" else ""
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,7 +49,6 @@ import androidx.compose.ui.unit.Dp
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import androidx.compose.ui.zIndex
|
import androidx.compose.ui.zIndex
|
||||||
import androidx.navigation.NavController
|
|
||||||
import com.kyant.backdrop.backdrops.LayerBackdrop
|
import com.kyant.backdrop.backdrops.LayerBackdrop
|
||||||
import com.kyant.backdrop.backdrops.layerBackdrop
|
import com.kyant.backdrop.backdrops.layerBackdrop
|
||||||
import com.kyant.backdrop.backdrops.rememberLayerBackdrop
|
import com.kyant.backdrop.backdrops.rememberLayerBackdrop
|
||||||
@@ -57,7 +56,6 @@ import dev.chrisbanes.haze.HazeProgressive
|
|||||||
import dev.chrisbanes.haze.HazeState
|
import dev.chrisbanes.haze.HazeState
|
||||||
import dev.chrisbanes.haze.HazeTint
|
import dev.chrisbanes.haze.HazeTint
|
||||||
import dev.chrisbanes.haze.hazeEffect
|
import dev.chrisbanes.haze.hazeEffect
|
||||||
import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi
|
|
||||||
import dev.chrisbanes.haze.rememberHazeState
|
import dev.chrisbanes.haze.rememberHazeState
|
||||||
import me.kavishdevar.librepods.R
|
import me.kavishdevar.librepods.R
|
||||||
|
|
||||||
@@ -66,7 +64,7 @@ fun StyledScaffold(
|
|||||||
title: String,
|
title: String,
|
||||||
actionButtons: List<@Composable (backdrop: LayerBackdrop) -> Unit> = emptyList(),
|
actionButtons: List<@Composable (backdrop: LayerBackdrop) -> Unit> = emptyList(),
|
||||||
snackbarHostState: SnackbarHostState = remember { SnackbarHostState() },
|
snackbarHostState: SnackbarHostState = remember { SnackbarHostState() },
|
||||||
content: @Composable (spacerValue: Dp, hazeState: HazeState) -> Unit
|
content: @Composable (spacerValue: Dp, hazeState: HazeState, bottomPadding: Dp) -> Unit
|
||||||
) {
|
) {
|
||||||
val isDarkTheme = isSystemInDarkTheme()
|
val isDarkTheme = isSystemInDarkTheme()
|
||||||
val hazeState = rememberHazeState(blurEnabled = true)
|
val hazeState = rememberHazeState(blurEnabled = true)
|
||||||
@@ -86,7 +84,7 @@ fun StyledScaffold(
|
|||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.padding(start = startPadding, end = endPadding, bottom = bottomPadding)
|
.padding(start = startPadding, end = endPadding)
|
||||||
) {
|
) {
|
||||||
val backdrop = rememberLayerBackdrop()
|
val backdrop = rememberLayerBackdrop()
|
||||||
Box(
|
Box(
|
||||||
@@ -126,7 +124,7 @@ fun StyledScaffold(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
content(topPadding + 64.dp, hazeState)
|
content(topPadding + 64.dp, hazeState, bottomPadding + 12.dp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -143,7 +141,7 @@ fun StyledScaffold(
|
|||||||
title = title,
|
title = title,
|
||||||
actionButtons = actionButtons,
|
actionButtons = actionButtons,
|
||||||
snackbarHostState = snackbarHostState,
|
snackbarHostState = snackbarHostState,
|
||||||
) { _, _ ->
|
) { _, _, _->
|
||||||
content()
|
content()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -159,7 +157,7 @@ fun StyledScaffold(
|
|||||||
title = title,
|
title = title,
|
||||||
actionButtons = actionButtons,
|
actionButtons = actionButtons,
|
||||||
snackbarHostState = snackbarHostState,
|
snackbarHostState = snackbarHostState,
|
||||||
) { spacerValue, _ ->
|
) { spacerValue, _, _ ->
|
||||||
content(spacerValue)
|
content(spacerValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
package me.kavishdevar.librepods.data
|
package me.kavishdevar.librepods.data
|
||||||
|
|
||||||
import me.kavishdevar.librepods.utils.AACPManager
|
import me.kavishdevar.librepods.utils.AACPManager
|
||||||
|
import me.kavishdevar.librepods.utils.AACPManager.Companion.ControlCommandIdentifiers
|
||||||
|
|
||||||
class ControlCommandRepository(
|
class ControlCommandRepository(
|
||||||
private val aacpManager: AACPManager
|
private val aacpManager: AACPManager
|
||||||
@@ -60,4 +61,10 @@ class ControlCommandRepository(
|
|||||||
) {
|
) {
|
||||||
aacpManager.unregisterControlCommandListener(identifier, listener)
|
aacpManager.unregisterControlCommandListener(identifier, listener)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getMap(): Map<ControlCommandIdentifiers, ByteArray> {
|
||||||
|
return aacpManager.controlCommandStatusList.associate {
|
||||||
|
it.identifier to it.value
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ fun AccessibilitySettingsScreen(viewModel: AirPodsViewModel, navController: NavC
|
|||||||
|
|
||||||
StyledScaffold(
|
StyledScaffold(
|
||||||
title = stringResource(R.string.accessibility)
|
title = stringResource(R.string.accessibility)
|
||||||
) { spacerHeight, hazeState ->
|
) { topPadding, hazeState, bottomPadding ->
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
@@ -111,7 +111,7 @@ fun AccessibilitySettingsScreen(viewModel: AirPodsViewModel, navController: NavC
|
|||||||
.padding(horizontal = 16.dp),
|
.padding(horizontal = 16.dp),
|
||||||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||||
) {
|
) {
|
||||||
Spacer(modifier = Modifier.height(spacerHeight))
|
Spacer(modifier = Modifier.height(topPadding))
|
||||||
|
|
||||||
// val phoneMediaEQ = remember { mutableStateOf(FloatArray(8) { 0.5f }) }
|
// val phoneMediaEQ = remember { mutableStateOf(FloatArray(8) { 0.5f }) }
|
||||||
// val phoneEQEnabled = remember { mutableStateOf(false) }
|
// val phoneEQEnabled = remember { mutableStateOf(false) }
|
||||||
@@ -508,6 +508,7 @@ fun AccessibilitySettingsScreen(viewModel: AirPodsViewModel, navController: NavC
|
|||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
Spacer(modifier = Modifier.height(bottomPadding))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ package me.kavishdevar.librepods.screens
|
|||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.content.Context.MODE_PRIVATE
|
import android.content.Context.MODE_PRIVATE
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.compose.foundation.gestures.detectTapGestures
|
||||||
import androidx.compose.foundation.isSystemInDarkTheme
|
import androidx.compose.foundation.isSystemInDarkTheme
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
@@ -42,12 +44,16 @@ import androidx.compose.runtime.DisposableEffect
|
|||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableIntStateOf
|
||||||
|
import androidx.compose.runtime.mutableLongStateOf
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.input.pointer.PointerEventPass
|
||||||
|
import androidx.compose.ui.input.pointer.pointerInput
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.TextStyle
|
import androidx.compose.ui.text.TextStyle
|
||||||
@@ -56,7 +62,6 @@ import androidx.compose.ui.text.font.FontFamily
|
|||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.text.input.TextFieldValue
|
import androidx.compose.ui.text.input.TextFieldValue
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
@@ -66,6 +71,7 @@ import com.kyant.backdrop.highlight.Highlight
|
|||||||
import dev.chrisbanes.haze.HazeState
|
import dev.chrisbanes.haze.HazeState
|
||||||
import dev.chrisbanes.haze.hazeSource
|
import dev.chrisbanes.haze.hazeSource
|
||||||
import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi
|
import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
import me.kavishdevar.librepods.BuildConfig
|
import me.kavishdevar.librepods.BuildConfig
|
||||||
import me.kavishdevar.librepods.R
|
import me.kavishdevar.librepods.R
|
||||||
import me.kavishdevar.librepods.composables.AboutCard
|
import me.kavishdevar.librepods.composables.AboutCard
|
||||||
@@ -82,7 +88,6 @@ import me.kavishdevar.librepods.composables.StyledButton
|
|||||||
import me.kavishdevar.librepods.composables.StyledIconButton
|
import me.kavishdevar.librepods.composables.StyledIconButton
|
||||||
import me.kavishdevar.librepods.composables.StyledScaffold
|
import me.kavishdevar.librepods.composables.StyledScaffold
|
||||||
import me.kavishdevar.librepods.composables.StyledToggle
|
import me.kavishdevar.librepods.composables.StyledToggle
|
||||||
import me.kavishdevar.librepods.ui.theme.LibrePodsTheme
|
|
||||||
import me.kavishdevar.librepods.utils.AACPManager
|
import me.kavishdevar.librepods.utils.AACPManager
|
||||||
import me.kavishdevar.librepods.utils.ATTHandles
|
import me.kavishdevar.librepods.utils.ATTHandles
|
||||||
import me.kavishdevar.librepods.utils.AirPodsPro3
|
import me.kavishdevar.librepods.utils.AirPodsPro3
|
||||||
@@ -131,15 +136,24 @@ fun AirPodsSettingsScreen(viewModel: AirPodsViewModel, navController: NavControl
|
|||||||
|
|
||||||
StyledScaffold(
|
StyledScaffold(
|
||||||
title = deviceName.text, actionButtons = listOf(
|
title = deviceName.text, actionButtons = listOf(
|
||||||
{ scaffoldBackdrop ->
|
{ scaffoldBackdrop ->
|
||||||
StyledIconButton(
|
StyledIconButton(
|
||||||
onClick = { navController.navigate("app_settings") },
|
onClick = { navController.navigate("app_settings") },
|
||||||
icon = "",
|
icon = "",
|
||||||
backdrop = scaffoldBackdrop
|
backdrop = scaffoldBackdrop
|
||||||
)
|
)
|
||||||
}), snackbarHostState = snackbarHostState
|
}), snackbarHostState = snackbarHostState
|
||||||
) { spacerHeight, hazeState ->
|
) { topPadding, hazeState, bottomPadding ->
|
||||||
hazeStateS.value = hazeState
|
hazeStateS.value = hazeState
|
||||||
|
var blockTouches by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
viewModel.demoActivated.collect {
|
||||||
|
blockTouches = true
|
||||||
|
delay(1000)
|
||||||
|
blockTouches = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (state.isLocallyConnected) {
|
if (state.isLocallyConnected) {
|
||||||
val capabilities = state.capabilities
|
val capabilities = state.capabilities
|
||||||
@@ -148,8 +162,18 @@ fun AirPodsSettingsScreen(viewModel: AirPodsViewModel, navController: NavControl
|
|||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.hazeSource(hazeState)
|
.hazeSource(hazeState)
|
||||||
.padding(horizontal = 16.dp)
|
.padding(horizontal = 16.dp)
|
||||||
|
.then(
|
||||||
|
if (blockTouches) Modifier.pointerInput(Unit) {
|
||||||
|
awaitPointerEventScope {
|
||||||
|
while (true) {
|
||||||
|
val event = awaitPointerEvent(PointerEventPass.Initial)
|
||||||
|
event.changes.forEach { it.consume() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else Modifier
|
||||||
|
)
|
||||||
) {
|
) {
|
||||||
item(key = "spacer_top") { Spacer(modifier = Modifier.height(spacerHeight)) }
|
item(key = "spacer_top") { Spacer(modifier = Modifier.height(topPadding)) }
|
||||||
item(key = "battery") {
|
item(key = "battery") {
|
||||||
BatteryView(
|
BatteryView(
|
||||||
batteryList = state.battery,
|
batteryList = state.battery,
|
||||||
@@ -341,8 +365,7 @@ fun AirPodsSettingsScreen(viewModel: AirPodsViewModel, navController: NavControl
|
|||||||
viewModel.setAutomaticEarDetectionEnabled(it)
|
viewModel.setAutomaticEarDetectionEnabled(it)
|
||||||
},
|
},
|
||||||
automaticConnectionEnabled = state.automaticConnectionEnabled,
|
automaticConnectionEnabled = state.automaticConnectionEnabled,
|
||||||
onAutomaticConnectionChanged = { viewModel.setAutomaticConnectionEnabled(it) }
|
onAutomaticConnectionChanged = { viewModel.setAutomaticConnectionEnabled(it) })
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
item(key = "spacer_microphone") { Spacer(modifier = Modifier.height(16.dp)) }
|
item(key = "spacer_microphone") { Spacer(modifier = Modifier.height(16.dp)) }
|
||||||
@@ -420,7 +443,7 @@ fun AirPodsSettingsScreen(viewModel: AirPodsViewModel, navController: NavControl
|
|||||||
|
|
||||||
// item(key = "spacer_debug") { Spacer(modifier = Modifier.height(16.dp)) }
|
// item(key = "spacer_debug") { Spacer(modifier = Modifier.height(16.dp)) }
|
||||||
// item(key = "debug") { NavigationButton("debug", "Debug", navController) }
|
// item(key = "debug") { NavigationButton("debug", "Debug", navController) }
|
||||||
item(key = "spacer_bottom") { Spacer(Modifier.height(24.dp)) }
|
item(key = "spacer_bottom") { Spacer(Modifier.height(bottomPadding)) }
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val backdrop = rememberLayerBackdrop()
|
val backdrop = rememberLayerBackdrop()
|
||||||
@@ -440,26 +463,54 @@ fun AirPodsSettingsScreen(viewModel: AirPodsViewModel, navController: NavControl
|
|||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
verticalArrangement = Arrangement.Center
|
verticalArrangement = Arrangement.Center
|
||||||
) {
|
) {
|
||||||
Text(
|
val tapCount = remember { mutableIntStateOf(0) }
|
||||||
text = stringResource(R.string.airpods_not_connected), style = TextStyle(
|
val lastTapTime = remember { mutableLongStateOf(0L) }
|
||||||
fontSize = 24.sp,
|
Column(
|
||||||
fontWeight = FontWeight.Medium,
|
modifier = Modifier
|
||||||
color = if (isSystemInDarkTheme()) Color.White else Color.Black,
|
.fillMaxWidth()
|
||||||
fontFamily = FontFamily(Font(R.font.sf_pro))
|
.pointerInput(Unit) {
|
||||||
), textAlign = TextAlign.Center, modifier = Modifier.fillMaxWidth()
|
detectTapGestures(
|
||||||
)
|
onTap = {
|
||||||
Spacer(Modifier.height(24.dp))
|
val now = System.currentTimeMillis()
|
||||||
Text(
|
|
||||||
text = stringResource(R.string.airpods_not_connected_description),
|
if (now - lastTapTime.longValue > 400) {
|
||||||
style = TextStyle(
|
tapCount.intValue = 0
|
||||||
fontSize = 16.sp,
|
}
|
||||||
fontWeight = FontWeight.Light,
|
|
||||||
color = if (isSystemInDarkTheme()) Color.White else Color.Black,
|
tapCount.intValue++
|
||||||
fontFamily = FontFamily(Font(R.font.sf_pro))
|
lastTapTime.longValue = now
|
||||||
),
|
|
||||||
textAlign = TextAlign.Center,
|
if (tapCount.intValue >= 5) {
|
||||||
modifier = Modifier.fillMaxWidth()
|
tapCount.intValue = 0
|
||||||
|
viewModel.activateDemoMode()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
{
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.airpods_not_connected), style = TextStyle(
|
||||||
|
fontSize = 24.sp,
|
||||||
|
fontWeight = FontWeight.Medium,
|
||||||
|
color = if (isSystemInDarkTheme()) Color.White else Color.Black,
|
||||||
|
fontFamily = FontFamily(Font(R.font.sf_pro))
|
||||||
|
), textAlign = TextAlign.Center, modifier = Modifier.fillMaxWidth()
|
||||||
|
)
|
||||||
|
Spacer(Modifier.height(24.dp))
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.airpods_not_connected_description),
|
||||||
|
style = TextStyle(
|
||||||
|
fontSize = 16.sp,
|
||||||
|
fontWeight = FontWeight.Light,
|
||||||
|
color = if (isSystemInDarkTheme()) Color.White else Color.Black,
|
||||||
|
fontFamily = FontFamily(Font(R.font.sf_pro))
|
||||||
|
),
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
Spacer(Modifier.height(32.dp))
|
Spacer(Modifier.height(32.dp))
|
||||||
// StyledButton(
|
// StyledButton(
|
||||||
// onClick = { navController.navigate("troubleshooting") },
|
// onClick = { navController.navigate("troubleshooting") },
|
||||||
@@ -496,17 +547,3 @@ fun AirPodsSettingsScreen(viewModel: AirPodsViewModel, navController: NavControl
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Preview
|
|
||||||
@Composable
|
|
||||||
fun AirPodsSettingsScreenPreview() {
|
|
||||||
Column(
|
|
||||||
modifier = Modifier.height(2000.dp)
|
|
||||||
) {
|
|
||||||
LibrePodsTheme(
|
|
||||||
darkTheme = true
|
|
||||||
) {
|
|
||||||
// AirPodsSettingsScreen(dev = null, service = AirPodsService(), navController = rememberNavController(), isConnected = true, isRemotelyConnected = false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ fun AppSettingsScreen(
|
|||||||
|
|
||||||
StyledScaffold(
|
StyledScaffold(
|
||||||
title = stringResource(R.string.app_settings)
|
title = stringResource(R.string.app_settings)
|
||||||
) { spacerHeight, hazeState ->
|
) { topPadding, hazeState, bottomPadding ->
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
@@ -90,7 +90,7 @@ fun AppSettingsScreen(
|
|||||||
.verticalScroll(scrollState)
|
.verticalScroll(scrollState)
|
||||||
.padding(horizontal = 16.dp)
|
.padding(horizontal = 16.dp)
|
||||||
) {
|
) {
|
||||||
Spacer(modifier = Modifier.height(spacerHeight))
|
Spacer(modifier = Modifier.height(topPadding))
|
||||||
|
|
||||||
val isDarkTheme = isSystemInDarkTheme()
|
val isDarkTheme = isSystemInDarkTheme()
|
||||||
val backgroundColor = if (isDarkTheme) Color(0xFF1C1C1E) else Color(0xFFFFFFFF)
|
val backgroundColor = if (isDarkTheme) Color(0xFF1C1C1E) else Color(0xFFFFFFFF)
|
||||||
@@ -189,19 +189,21 @@ fun AppSettingsScreen(
|
|||||||
enabled = uiState.isPremium
|
enabled = uiState.isPremium
|
||||||
)
|
)
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
if (!BuildConfig.PLAY_BUILD) {
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|
||||||
NavigationButton(
|
NavigationButton(
|
||||||
to = "",
|
to = "",
|
||||||
title = stringResource(R.string.camera_control),
|
title = stringResource(R.string.camera_control),
|
||||||
name = stringResource(R.string.set_custom_camera_package),
|
name = stringResource(R.string.set_custom_camera_package),
|
||||||
navController = navController,
|
navController = navController,
|
||||||
onClick = {
|
onClick = {
|
||||||
if (uiState.isPremium) viewModel.setShowCameraDialog(true)
|
if (uiState.isPremium) viewModel.setShowCameraDialog(true)
|
||||||
},
|
},
|
||||||
independent = true,
|
independent = true,
|
||||||
description = stringResource(R.string.camera_control_app_description)
|
description = stringResource(R.string.camera_control_app_description)
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
if (BuildConfig.FLAVOR == "xposed") {
|
if (BuildConfig.FLAVOR == "xposed") {
|
||||||
@@ -437,6 +439,7 @@ fun AppSettingsScreen(
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
Spacer(modifier = Modifier.height(bottomPadding))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -339,7 +339,7 @@ fun DebugScreen(navController: NavController) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
) { spacerHeight, hazeState ->
|
) { topPadding, hazeState, bottomPadding ->
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
@@ -348,7 +348,7 @@ fun DebugScreen(navController: NavController) {
|
|||||||
.layerBackdrop(backdrop)
|
.layerBackdrop(backdrop)
|
||||||
.padding(horizontal = 16.dp)
|
.padding(horizontal = 16.dp)
|
||||||
) {
|
) {
|
||||||
Spacer(modifier = Modifier.height(spacerHeight))
|
Spacer(modifier = Modifier.height(topPadding))
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
state = listState,
|
state = listState,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
|||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material3.Card
|
import androidx.compose.material3.Card
|
||||||
import androidx.compose.material3.CardDefaults
|
import androidx.compose.material3.CardDefaults
|
||||||
@@ -139,7 +140,7 @@ fun HeadTrackingScreen(viewModel: AirPodsViewModel) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
) { spacerHeight, hazeState ->
|
) { topPadding, hazeState, _ ->
|
||||||
|
|
||||||
var gestureText by remember { mutableStateOf("") }
|
var gestureText by remember { mutableStateOf("") }
|
||||||
val coroutineScope = rememberCoroutineScope()
|
val coroutineScope = rememberCoroutineScope()
|
||||||
@@ -160,7 +161,7 @@ fun HeadTrackingScreen(viewModel: AirPodsViewModel) {
|
|||||||
.padding(horizontal = 16.dp)
|
.padding(horizontal = 16.dp)
|
||||||
.verticalScroll(scrollState)
|
.verticalScroll(scrollState)
|
||||||
) {
|
) {
|
||||||
Spacer(modifier = Modifier.height(spacerHeight))
|
Spacer(modifier = Modifier.height(topPadding))
|
||||||
|
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
|
|
||||||
@@ -404,7 +405,8 @@ private fun HeadVisualization() {
|
|||||||
.aspectRatio(2f),
|
.aspectRatio(2f),
|
||||||
colors = CardDefaults.cardColors(
|
colors = CardDefaults.cardColors(
|
||||||
containerColor = backgroundColor
|
containerColor = backgroundColor
|
||||||
)
|
),
|
||||||
|
shape = RoundedCornerShape(28.dp)
|
||||||
) {
|
) {
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize(),
|
||||||
@@ -655,7 +657,8 @@ private fun AccelerationPlot() {
|
|||||||
.height(300.dp),
|
.height(300.dp),
|
||||||
colors = CardDefaults.cardColors(
|
colors = CardDefaults.cardColors(
|
||||||
containerColor = if (darkTheme) Color(0xFF1C1C1E) else Color.White
|
containerColor = if (darkTheme) Color(0xFF1C1C1E) else Color.White
|
||||||
)
|
),
|
||||||
|
shape = RoundedCornerShape(28.dp)
|
||||||
) {
|
) {
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ fun HearingAidScreen(viewModel: AirPodsViewModel, navController: NavController)
|
|||||||
StyledScaffold(
|
StyledScaffold(
|
||||||
title = stringResource(R.string.hearing_aid),
|
title = stringResource(R.string.hearing_aid),
|
||||||
snackbarHostState = snackbarHostState,
|
snackbarHostState = snackbarHostState,
|
||||||
) { spacerHeight, hazeState ->
|
) { topPadding, hazeState, bottomPadding ->
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.layerBackdrop(backdrop)
|
.layerBackdrop(backdrop)
|
||||||
@@ -113,7 +113,7 @@ fun HearingAidScreen(viewModel: AirPodsViewModel, navController: NavController)
|
|||||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
) {
|
) {
|
||||||
hazeStateS.value = hazeState
|
hazeStateS.value = hazeState
|
||||||
Spacer(modifier = Modifier.height(spacerHeight))
|
Spacer(modifier = Modifier.height(topPadding))
|
||||||
|
|
||||||
// val mediaAssistEnabled = remember { mutableStateOf(false) }
|
// val mediaAssistEnabled = remember { mutableStateOf(false) }
|
||||||
// val adjustMediaEnabled = remember { mutableStateOf(false) }
|
// val adjustMediaEnabled = remember { mutableStateOf(false) }
|
||||||
@@ -234,6 +234,7 @@ fun HearingAidScreen(viewModel: AirPodsViewModel, navController: NavController)
|
|||||||
// independent = false
|
// independent = false
|
||||||
// )
|
// )
|
||||||
// }
|
// }
|
||||||
|
Spacer(modifier = Modifier.height(bottomPadding))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ fun TransparencySettingsScreen(viewModel: AirPodsViewModel) {
|
|||||||
|
|
||||||
StyledScaffold(
|
StyledScaffold(
|
||||||
title = stringResource(R.string.customize_transparency_mode)
|
title = stringResource(R.string.customize_transparency_mode)
|
||||||
){ spacerHeight, hazeState ->
|
){ topPadding, hazeState, bottomPadding ->
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.hazeSource(hazeState)
|
.hazeSource(hazeState)
|
||||||
@@ -114,7 +114,7 @@ fun TransparencySettingsScreen(viewModel: AirPodsViewModel) {
|
|||||||
.padding(horizontal = 16.dp),
|
.padding(horizontal = 16.dp),
|
||||||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||||
) {
|
) {
|
||||||
Spacer(modifier = Modifier.height(spacerHeight))
|
Spacer(modifier = Modifier.height(topPadding))
|
||||||
val backgroundColor = if (isDarkTheme) Color(0xFF1C1C1E) else Color(0xFFFFFFFF)
|
val backgroundColor = if (isDarkTheme) Color(0xFF1C1C1E) else Color(0xFFFFFFFF)
|
||||||
|
|
||||||
val enabled = remember { mutableStateOf(false) }
|
val enabled = remember { mutableStateOf(false) }
|
||||||
@@ -441,6 +441,8 @@ fun TransparencySettingsScreen(viewModel: AirPodsViewModel) {
|
|||||||
|
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(bottomPadding))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -216,7 +216,7 @@ fun TroubleshootingScreen(navController: NavController) {
|
|||||||
) {
|
) {
|
||||||
StyledScaffold(
|
StyledScaffold(
|
||||||
title = stringResource(R.string.troubleshooting)
|
title = stringResource(R.string.troubleshooting)
|
||||||
){ spacerHeight, hazeState ->
|
){ topPadding, hazeState, bottomPadding ->
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
@@ -225,7 +225,7 @@ fun TroubleshootingScreen(navController: NavController) {
|
|||||||
.verticalScroll(scrollState)
|
.verticalScroll(scrollState)
|
||||||
.padding(horizontal = 16.dp)
|
.padding(horizontal = 16.dp)
|
||||||
) {
|
) {
|
||||||
Spacer(modifier = Modifier.height(spacerHeight))
|
Spacer(modifier = Modifier.height(topPadding))
|
||||||
|
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(R.string.saved_logs),
|
text = stringResource(R.string.saved_logs),
|
||||||
|
|||||||
@@ -18,7 +18,6 @@
|
|||||||
|
|
||||||
package me.kavishdevar.librepods.screens
|
package me.kavishdevar.librepods.screens
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.compose.foundation.isSystemInDarkTheme
|
import androidx.compose.foundation.isSystemInDarkTheme
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
@@ -33,7 +32,6 @@ import androidx.compose.foundation.layout.width
|
|||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.text.KeyboardOptions
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
|
||||||
import androidx.compose.material3.OutlinedTextField
|
import androidx.compose.material3.OutlinedTextField
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
@@ -58,7 +56,6 @@ import androidx.compose.ui.unit.sp
|
|||||||
import com.kyant.backdrop.backdrops.layerBackdrop
|
import com.kyant.backdrop.backdrops.layerBackdrop
|
||||||
import com.kyant.backdrop.backdrops.rememberLayerBackdrop
|
import com.kyant.backdrop.backdrops.rememberLayerBackdrop
|
||||||
import dev.chrisbanes.haze.hazeSource
|
import dev.chrisbanes.haze.hazeSource
|
||||||
import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi
|
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import me.kavishdevar.librepods.R
|
import me.kavishdevar.librepods.R
|
||||||
@@ -69,7 +66,6 @@ import me.kavishdevar.librepods.utils.HearingAidSettings
|
|||||||
import me.kavishdevar.librepods.utils.parseHearingAidSettingsResponse
|
import me.kavishdevar.librepods.utils.parseHearingAidSettingsResponse
|
||||||
import me.kavishdevar.librepods.utils.sendHearingAidSettings
|
import me.kavishdevar.librepods.utils.sendHearingAidSettings
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import kotlin.io.encoding.ExperimentalEncodingApi
|
|
||||||
|
|
||||||
private var debounceJob: MutableState<Job?> = mutableStateOf(null)
|
private var debounceJob: MutableState<Job?> = mutableStateOf(null)
|
||||||
private const val TAG = "HearingAidAdjustments"
|
private const val TAG = "HearingAidAdjustments"
|
||||||
@@ -92,7 +88,7 @@ fun UpdateHearingTestScreen() {
|
|||||||
val backdrop = rememberLayerBackdrop()
|
val backdrop = rememberLayerBackdrop()
|
||||||
StyledScaffold(
|
StyledScaffold(
|
||||||
title = stringResource(R.string.hearing_test)
|
title = stringResource(R.string.hearing_test)
|
||||||
) { spacerHeight, hazeState ->
|
) { topPadding, hazeState, bottomPadding ->
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.hazeSource(hazeState)
|
.hazeSource(hazeState)
|
||||||
@@ -104,7 +100,7 @@ fun UpdateHearingTestScreen() {
|
|||||||
) {
|
) {
|
||||||
val textColor = if (isSystemInDarkTheme()) Color.White else Color.Black
|
val textColor = if (isSystemInDarkTheme()) Color.White else Color.Black
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(spacerHeight))
|
Spacer(modifier = Modifier.height(topPadding))
|
||||||
|
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(R.string.hearing_test_value_instruction),
|
text = stringResource(R.string.hearing_test_value_instruction),
|
||||||
@@ -356,6 +352,7 @@ fun UpdateHearingTestScreen() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Spacer(modifier = Modifier.height(bottomPadding))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import android.util.Log
|
|||||||
import androidx.core.content.edit
|
import androidx.core.content.edit
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
@@ -35,6 +36,8 @@ import kotlinx.coroutines.launch
|
|||||||
import me.kavishdevar.librepods.billing.BillingManager
|
import me.kavishdevar.librepods.billing.BillingManager
|
||||||
import me.kavishdevar.librepods.constants.AirPodsNotifications
|
import me.kavishdevar.librepods.constants.AirPodsNotifications
|
||||||
import me.kavishdevar.librepods.constants.Battery
|
import me.kavishdevar.librepods.constants.Battery
|
||||||
|
import me.kavishdevar.librepods.constants.BatteryComponent
|
||||||
|
import me.kavishdevar.librepods.constants.BatteryStatus
|
||||||
import me.kavishdevar.librepods.constants.StemAction
|
import me.kavishdevar.librepods.constants.StemAction
|
||||||
import me.kavishdevar.librepods.data.ControlCommandRepository
|
import me.kavishdevar.librepods.data.ControlCommandRepository
|
||||||
import me.kavishdevar.librepods.services.AirPodsService
|
import me.kavishdevar.librepods.services.AirPodsService
|
||||||
@@ -91,6 +94,11 @@ class AirPodsViewModel(
|
|||||||
private val _uiState = MutableStateFlow(AirPodsUiState(deviceName = sharedPreferences.getString("name", "AirPods Pro") ?: "AirPods Pro"))
|
private val _uiState = MutableStateFlow(AirPodsUiState(deviceName = sharedPreferences.getString("name", "AirPods Pro") ?: "AirPods Pro"))
|
||||||
val uiState: StateFlow<AirPodsUiState> = _uiState
|
val uiState: StateFlow<AirPodsUiState> = _uiState
|
||||||
|
|
||||||
|
private var isDemoMode = false
|
||||||
|
val demoActivated = MutableSharedFlow<Unit>()
|
||||||
|
|
||||||
|
private var billingFirstCollectDone = false
|
||||||
|
|
||||||
private val listeners = mutableMapOf<
|
private val listeners = mutableMapOf<
|
||||||
ControlCommandIdentifiers,
|
ControlCommandIdentifiers,
|
||||||
AACPManager.ControlCommandListener
|
AACPManager.ControlCommandListener
|
||||||
@@ -120,6 +128,8 @@ class AirPodsViewModel(
|
|||||||
loadSharedPreferences()
|
loadSharedPreferences()
|
||||||
setupControlObservers()
|
setupControlObservers()
|
||||||
observeBilling()
|
observeBilling()
|
||||||
|
loadControlList()
|
||||||
|
if (isDemoMode) activateDemoMode()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCleared() {
|
override fun onCleared() {
|
||||||
@@ -138,14 +148,19 @@ class AirPodsViewModel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun observeBilling() {
|
private fun observeBilling() {
|
||||||
viewModelScope.launch {
|
if (!isDemoMode) viewModelScope.launch {
|
||||||
BillingManager.provider.isPremium.collect { premium ->
|
BillingManager.provider.isPremium.collect { premium ->
|
||||||
|
if (!billingFirstCollectDone) {
|
||||||
|
billingFirstCollectDone = true
|
||||||
|
return@collect
|
||||||
|
}
|
||||||
if (!premium) {
|
if (!premium) {
|
||||||
|
Log.d("AirPodsViewModel", "we are not premium")
|
||||||
setControlCommandBoolean(ControlCommandIdentifiers.CONVERSATION_DETECT_CONFIG, false)
|
setControlCommandBoolean(ControlCommandIdentifiers.CONVERSATION_DETECT_CONFIG, false)
|
||||||
setHeadGesturesEnabled(false)
|
setHeadGesturesEnabled(false)
|
||||||
|
} else {
|
||||||
|
Log.d("AirPodsViewModel", "we are premium")
|
||||||
}
|
}
|
||||||
|
|
||||||
_uiState.update { it.copy(isPremium = premium) }
|
_uiState.update { it.copy(isPremium = premium) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -154,7 +169,7 @@ class AirPodsViewModel(
|
|||||||
private fun observeBroadcasts() {
|
private fun observeBroadcasts() {
|
||||||
broadcastReceiver = object : BroadcastReceiver() {
|
broadcastReceiver = object : BroadcastReceiver() {
|
||||||
override fun onReceive(context: Context?, intent: Intent?) {
|
override fun onReceive(context: Context?, intent: Intent?) {
|
||||||
when (intent?.action) {
|
if (!isDemoMode) when (intent?.action) {
|
||||||
AirPodsNotifications.AIRPODS_CONNECTED -> {
|
AirPodsNotifications.AIRPODS_CONNECTED -> {
|
||||||
_uiState.update {
|
_uiState.update {
|
||||||
it.copy(isLocallyConnected = true)
|
it.copy(isLocallyConnected = true)
|
||||||
@@ -208,7 +223,7 @@ class AirPodsViewModel(
|
|||||||
identifier: ControlCommandIdentifiers,
|
identifier: ControlCommandIdentifiers,
|
||||||
value: ByteArray
|
value: ByteArray
|
||||||
) {
|
) {
|
||||||
controlRepo.setValue(identifier, value)
|
if (!isDemoMode) controlRepo.setValue(identifier, value)
|
||||||
_uiState.update {
|
_uiState.update {
|
||||||
it.copy(
|
it.copy(
|
||||||
controlStates = it.controlStates + (identifier to value)
|
controlStates = it.controlStates + (identifier to value)
|
||||||
@@ -290,6 +305,7 @@ class AirPodsViewModel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun refreshInitialData() {
|
fun refreshInitialData() {
|
||||||
|
if (isDemoMode) return
|
||||||
service.let { service ->
|
service.let { service ->
|
||||||
_uiState.update {
|
_uiState.update {
|
||||||
it.copy(
|
it.copy(
|
||||||
@@ -336,6 +352,14 @@ class AirPodsViewModel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun loadControlList() {
|
||||||
|
_uiState.update {
|
||||||
|
it.copy(
|
||||||
|
controlStates = controlRepo.getMap()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun loadInstance() {
|
private fun loadInstance() {
|
||||||
val instance = service.airpodsInstance ?: AirPodsInstance(
|
val instance = service.airpodsInstance ?: AirPodsInstance(
|
||||||
name = "AirPods",
|
name = "AirPods",
|
||||||
@@ -414,4 +438,43 @@ class AirPodsViewModel(
|
|||||||
fun purchase(context: Context) {
|
fun purchase(context: Context) {
|
||||||
BillingManager.provider.purchase(context as Activity)
|
BillingManager.provider.purchase(context as Activity)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun activateDemoMode() {
|
||||||
|
isDemoMode = true
|
||||||
|
viewModelScope.launch {
|
||||||
|
demoActivated.emit(Unit)
|
||||||
|
}
|
||||||
|
val fakeInstance = AirPodsInstance(
|
||||||
|
name = "AirPods Pro (Demo)",
|
||||||
|
model = AirPodsModels.getModelByModelNumber("A3049")!!,
|
||||||
|
actualModelNumber = "A3049",
|
||||||
|
aacpManager = service.aacpManager,
|
||||||
|
serialNumber = "DEMO123",
|
||||||
|
leftSerialNumber = "L-DEMO",
|
||||||
|
rightSerialNumber = "R-DEMO",
|
||||||
|
version1 = "1.0",
|
||||||
|
version2 = "1.0",
|
||||||
|
version3 = "1.0",
|
||||||
|
attManager = null
|
||||||
|
)
|
||||||
|
|
||||||
|
_uiState.update {
|
||||||
|
it.copy(
|
||||||
|
isLocallyConnected = true,
|
||||||
|
instance = fakeInstance,
|
||||||
|
capabilities = fakeInstance.model.capabilities,
|
||||||
|
|
||||||
|
battery = listOf(
|
||||||
|
Battery(BatteryComponent.LEFT, 85, BatteryStatus.CHARGING),
|
||||||
|
Battery(BatteryComponent.RIGHT, 25, BatteryStatus.NOT_CHARGING),
|
||||||
|
Battery(BatteryComponent.CASE, 85, BatteryStatus.CHARGING),
|
||||||
|
),
|
||||||
|
|
||||||
|
modelName = fakeInstance.model.displayName,
|
||||||
|
actualModel = fakeInstance.actualModelNumber,
|
||||||
|
serialNumbers = listOf("DEMO", "DEMO", "DEMO"),
|
||||||
|
version3 = "Demo Firmware"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user