This commit is contained in:
2025-03-08 13:02:11 -05:00
parent bef71f8d00
commit 7ff235fb3f
6 changed files with 176 additions and 167 deletions

View File

@@ -7,10 +7,10 @@
</SelectionState>
<SelectionState runConfigName="wear">
<option name="selectionMode" value="DROPDOWN" />
<DropdownSelection timestamp="2025-03-08T02:48:22.663246800Z">
<DropdownSelection timestamp="2025-03-08T17:32:57.542834800Z">
<Target type="DEFAULT_BOOT">
<handle>
<DeviceId pluginId="Default" identifier="serial=192.168.1.195:42789;connection=6910cb2b" />
<DeviceId pluginId="Default" identifier="serial=192.168.1.195:35507;connection=7a90992e" />
</handle>
</Target>
</DropdownSelection>

View File

@@ -17,10 +17,10 @@ import androidx.wear.compose.material.Text
@Composable
fun ControlDashboard(controlDashboardUiState: ControlDashboardUiState,
onMicClicked: () -> Unit,
onNavClicked: () -> Unit,
modifier: Modifier = Modifier) {
Box(contentAlignment = Alignment.Center, modifier = modifier.fillMaxSize()) {
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
ControlDashboardButton(buttonState = controlDashboardUiState.micState,
onClick = onMicClicked,
labelText = if (controlDashboardUiState.micState.expanded) {
@@ -29,6 +29,8 @@ fun ControlDashboard(controlDashboardUiState: ControlDashboardUiState,
"Start"
})
}
}
}

View File

@@ -17,6 +17,8 @@ import androidx.activity.compose.setContent
import androidx.activity.result.contract.ActivityResultContracts.RequestPermission
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
@@ -28,9 +30,12 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavHostController
import androidx.wear.compose.material.CompactChip
import androidx.wear.compose.material.MaterialTheme
import androidx.wear.compose.material.Text
import androidx.wear.compose.navigation.SwipeDismissableNavHost
import androidx.wear.compose.navigation.composable
@@ -103,7 +108,7 @@ fun WearApp() {
val navController: NavHostController = rememberSwipeDismissableNavController()
mainState.setNavController(navController);
SwipeDismissableNavHost(navController = navController, startDestination = "speaker") {
SwipeDismissableNavHost(navController = navController, userSwipeEnabled = true, startDestination = "speaker") {
composable("species_list") {
ScreenScaffold {
@@ -112,6 +117,16 @@ fun WearApp() {
}
composable("speaker") {
ScreenScaffold {
CompactChip(modifier = Modifier,
onClick = { navController.navigate("species_list") },
enabled = true,
contentPadding = PaddingValues(5.dp, 1.dp, 5.dp, 1.dp),
shape = MaterialTheme.shapes.small,
label = {
Text(text = "View>")
})
StartRecordingScreen(
appState = mainState.appState,
onMicClicked = {
@@ -119,28 +134,18 @@ fun WearApp() {
mainState.onMicClicked()
}
},
onNavClicked = {
navController.navigate("species_list")
}
)
}
}
}
// var sdnv = SwipeDismissableNavHost(navController = navController, startDestination = "list") {
// composable("speaker") {
// StartRecordingScreen(
// context = context,
// appState = mainState.appState,
// isPermissionDenied = mainState.isPermissionDenied,
// onMicClicked = {
// scope.launch {
// mainState.onMicClicked()
// }
// },
// )
// }
}
}
@@ -151,85 +156,3 @@ tailrec fun Context.findActivity(): Activity = when (this) {
else -> throw IllegalStateException("findActivity should be called in the context of an Activity")
}
@OptIn(ExperimentalHorologistApi::class)
@Composable
fun MessageDetail(id: String) {
val scrollState = rememberScrollState()
ScreenScaffold(scrollState = scrollState) {
val padding =
ScalingLazyColumnDefaults.padding(first = ScalingLazyColumnDefaults.ItemType.Text, last = ScalingLazyColumnDefaults.ItemType.Text)()
Column(modifier = Modifier
.fillMaxSize()
.verticalScroll(scrollState)
.rotaryWithScroll(scrollState)
.padding(padding),
verticalArrangement = Arrangement.Center) {
Text(text = id, textAlign = TextAlign.Center, modifier = Modifier.fillMaxSize())
}
}
}
@OptIn(ExperimentalHorologistApi::class)
@Composable
fun MessageList(onMessageClick: (String) -> Unit) {
val columnState =
rememberResponsiveColumnState(contentPadding = ScalingLazyColumnDefaults.padding(first = ScalingLazyColumnDefaults.ItemType.Text,
last = ScalingLazyColumnDefaults.ItemType.Chip))
ScreenScaffold(scrollState = columnState) {
ScalingLazyColumn(columnState = columnState, modifier = Modifier.fillMaxSize()) {
item {
ResponsiveListHeader(contentPadding = firstItemPadding()) {
Text(text = "Hey hey hey")
}
}
item {
Chip(label = "Message 1", onClick = { onMessageClick("message1") })
}
item {
Chip(label = "Message 2", onClick = { onMessageClick("message2") })
}
}
}
}
@OptIn(ExperimentalHorologistApi::class)
@Composable
fun MessageList2(onMessageClick: (String) -> Unit) {
val columnState =
rememberResponsiveColumnState(contentPadding = ScalingLazyColumnDefaults.padding(first = ScalingLazyColumnDefaults.ItemType.Text,
last = ScalingLazyColumnDefaults.ItemType.Chip))
ScreenScaffold(scrollState = columnState) {
ScalingLazyColumn(columnState = columnState, modifier = Modifier.fillMaxSize()) {
item {
ResponsiveListHeader(contentPadding = firstItemPadding()) {
Text(text = "Hey hey hey")
}
}
item {
Chip(label = "Message 3", onClick = { onMessageClick("message3") })
}
item {
Chip(label = "Message 4", onClick = { onMessageClick("message4") })
}
}
}
}
@WearPreviewDevices
@WearPreviewFontScales
@Composable
fun MessageDetailPreview() {
MessageDetail("test")
}
@WearPreviewDevices
@WearPreviewFontScales
@Composable
fun MessageListPreview() {
MessageList(onMessageClick = {})
}

View File

@@ -2,6 +2,7 @@ package com.birdsounds.identify.presentation
import android.util.Log
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.snapshots.SnapshotStateList
import java.time.Instant
@@ -11,13 +12,14 @@ class AScore(
_species: String,
_score: Float,
_timestamp: Long,
_trigger: Boolean = false
) {
val split_stuff: List<String> = _species.split("_");
val species = split_stuff[0];
val score = _score;
// var common_name = split_stuff[1];
var trigger = _trigger;
// var common_name = split_stuff[1];
val common_name = if (split_stuff.size > 1) split_stuff[1] else "";
val timestamp = _timestamp
fun age(): Long {
@@ -34,56 +36,42 @@ class AScore(
object SpeciesList {
var internal_list = mutableListOf<MutableState<AScore>>()
var internal_list = mutableListOf<AScore>()
var do_add_observation = false
var _list_on_ui: SnapshotStateList<MutableState<AScore>>? = null
fun setSpeciecsShow_list(list_in: SnapshotStateList<MutableState<AScore>>) {
_list_on_ui = list_in;
}
fun add_observation(species_in: AScore) {
Log.w(TAG, "In add obervation")
do_add_observation = false
Log.w(TAG, "In add observation")
var idx = 0
var idx_replace = -1
for (i in internal_list) {
if (i.value.species == species_in.species) {
do_add_observation = false
if (i.species == species_in.species) {
idx_replace = idx
}
idx += 1
}
if (idx_replace >= 0) {
Log.w(TAG, "Replacing")
internal_list[idx_replace].value =
species_in // _list_on_ui?.removeAt(idx_replace)
// _list_on_ui?.add(species_in);
// if (_list_on_ui != null) {
// _list_on_ui?.removeAt(idx_replace)
// _list_on_ui.add(idx_replace, species_in);
// }
internal_list[idx_replace] = species_in
internal_list[idx_replace].trigger = true;
} else {
internal_list.add(mutableStateOf(species_in))
internal_list.add(species_in)
}
internal_list = internal_list.sortedBy({ (it.value.age()) }).toMutableList()
// internal_list = internal_list.sortedBy({ (it.age()) }).toMutableList()
internal_list = internal_list.sortedWith(compareBy({ it.age() }, { it.score})).toMutableList()
while (_list_on_ui!!.size < internal_list.size) {
_list_on_ui!!.add(mutableStateOf(AScore("", 0F, 0L)))
Log.w(TAG, "Adding stuff to UI list");
_list_on_ui!!.add(mutableStateOf(AScore("",0.0F,0L)))
}
Log.w(TAG, "Internal list " + internal_list.toString())
for ((index, value) in internal_list.withIndex()) {
Log.w(TAG, "Adding ${index}:${value} to list ${_list_on_ui!!.size}")
if (_list_on_ui!!.size > index)
{
Log.w(TAG,"Updating ${value}")
_list_on_ui!![index].value = value.value
}
_list_on_ui?.get(index)?.value = value;
}
Log.w(TAG, internal_list.size.toString())

View File

@@ -1,59 +1,154 @@
package com.birdsounds.identify.presentation
import android.annotation.SuppressLint
import android.content.Context
import android.util.Log
import androidx.compose.animation.animateColorAsState
import androidx.compose.animation.core.CubicBezierEasing
import androidx.compose.animation.core.Easing
import androidx.compose.animation.core.tween
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshots.SnapshotStateList
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.lerp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.util.lerp
import androidx.wear.compose.foundation.lazy.ScalingLazyColumnDefaults.scalingParams
import androidx.wear.compose.foundation.lazy.ScalingParams
import androidx.wear.compose.foundation.lazy.items
import androidx.wear.compose.material.Text
import com.google.android.horologist.annotations.ExperimentalHorologistApi
import com.google.android.horologist.compose.layout.ScalingLazyColumn
import com.google.android.horologist.compose.layout.ScalingLazyColumnDefaults
import com.google.android.horologist.compose.layout.ScalingLazyColumnState
import com.google.android.horologist.compose.layout.ScreenScaffold
import com.google.android.horologist.compose.layout.rememberResponsiveColumnState
import kotlinx.coroutines.delay
@OptIn(ExperimentalHorologistApi::class)
fun interpolateColor(value: Float): Color {
// Ensure that the input value is clamped between 0 and 1
val clampedValue = value.coerceIn(0f, 1f)
// Red component: starts at 255 (white) and decreases to 0
val red = (255 * (1 - clampedValue)).toInt()
// Green component: stays at 255 (since both white and green have full green)
val green = 255
// Blue component: starts at 255 (white) and decreases to 0
val blue = (255 * (1 - clampedValue)).toInt()
// Construct the color
return Color(red.toInt(), green.toInt(), blue.toInt())
}
@Composable
fun SpeciesListView(
context: Context,
fun FlashingText(
text: String,
color: Color,
modifier: Modifier = Modifier
) {
// Set up the flashing state
var isFlashing by remember { mutableStateOf(false) }
val species_list_show = mutableStateListOf<MutableState<AScore>>()
for (i in 1..10) {
species_list_show.add(mutableStateOf(AScore(i.toString()+"_"+i.toString(), 0.00F, 0L)));
}
SpeciesList.setSpeciecsShow_list(species_list_show)
// Create animation for the background color
val backgroundColor by animateColorAsState(
targetValue = if (isFlashing) Color.Cyan else Color.Transparent,
animationSpec = tween(durationMillis = 500)
)
val species_show: SnapshotStateList<MutableState<AScore>> = remember { species_list_show }
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier
.verticalScroll(rememberScrollState())
.fillMaxWidth()
) {
species_show.forEach { aSpec -> Text(text = aSpec.value.common_name) }
// Trigger the flash effect once
LaunchedEffect(key1 = true) {
isFlashing = true
delay(300)
isFlashing = false
}
// Display the text with animated background
Text(
text = text,
color = color,
modifier = modifier
.background(color = backgroundColor)
)
}
// val columnState =
// rememberResponsiveColumnState(contentPadding = ScalingLazyColumnDefaults.padding(first = ScalingLazyColumnDefaults.ItemType.Text,
// last = ScalingLazyColumnDefaults.ItemType.Chip))
@SuppressLint("UnrememberedMutableState")
@OptIn(ExperimentalHorologistApi::class)
@Composable
fun SpeciesListView(context: Context,
) {
val species_list_show = mutableStateListOf<MutableState<AScore>>()
for (i in 1..3)
{
val hi = mutableStateOf(AScore("",0.0F,0L))
species_list_show.add(hi);
}
SpeciesList.setSpeciecsShow_list(species_list_show)
val species_show: SnapshotStateList<MutableState<AScore>> = remember { species_list_show }
// var sP = scalingParams( maxTransitionArea= 0.25f, minTransitionArea = 0.05f)
// val columnState = ScalingLazyColumnState(scalingParams = sP);
var columnState = rememberResponsiveColumnState(contentPadding = ScalingLazyColumnDefaults.padding(first = ScalingLazyColumnDefaults.ItemType.Text,
last = ScalingLazyColumnDefaults.ItemType.Chip), verticalArrangement= Arrangement.spacedBy(
space = 1.dp,
alignment = Alignment.Top,
),)
ScreenScaffold(scrollState = columnState) {
ScalingLazyColumn(
columnState = columnState,
modifier = Modifier.fillMaxWidth()
) {
items(species_show) { aSpec ->
if (aSpec.value.trigger)
{
Log.w(TAG,"Trigger "+aSpec.toString())
FlashingText(text = aSpec.value.common_name, color = interpolateColor(aSpec.value.score))
aSpec.value.trigger = false;
} else
{
Text(text = aSpec.value.common_name, color = interpolateColor(aSpec.value.score))
}
}
} // Dynamically display the chips
}
}
// ScreenScaffold(scrollState = columnState) {
// ScalingLazyColumn(columnState = columnState, modifier = Modifier.fillMaxSize()) {
// items(species_show) { aSpec -> Text(text = aSpec.value)
// items(species_show) { aSpec -> Text(text = aSpec.value.common_name)
// } // Dynamically display the chips
//
// }
// }

View File

@@ -10,7 +10,8 @@ import com.google.android.horologist.compose.layout.ScreenScaffold
@Composable
fun StartRecordingScreen(
appState: AppState,
onMicClicked: () -> Unit
onMicClicked: () -> Unit,
onNavClicked: () -> Unit
) {
ScreenScaffold {
val controlDashboardUiState = computeControlDashboardUiState(
@@ -18,8 +19,8 @@ fun StartRecordingScreen(
)
ControlDashboard(
controlDashboardUiState = controlDashboardUiState,
onMicClicked = onMicClicked
onMicClicked = onMicClicked,
onNavClicked = onNavClicked
)
}
}