This commit is contained in:
2025-03-08 11:07:40 -05:00
parent 06b98f107e
commit bef71f8d00
12 changed files with 208 additions and 142 deletions

View File

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

View File

@@ -1,4 +1,4 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules. // Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins { plugins {
alias(libs.plugins.android.application) apply false alias(libs.plugins.android.application) apply false
alias(libs.plugins.kotlin.android) apply false alias(libs.plugins.kotlin.android) apply false

View File

@@ -21,6 +21,7 @@ media3Common = "1.4.0"
composeMaterial3 = "1.0.0-alpha23" composeMaterial3 = "1.0.0-alpha23"
workRuntimeKtx = "2.9.1" workRuntimeKtx = "2.9.1"
lifecycleRuntimeKtx = "2.6.1" lifecycleRuntimeKtx = "2.6.1"
runtimeAndroid = "1.6.6"
#litert = "1.0.1" #litert = "1.0.1"
[libraries] [libraries]
@@ -51,6 +52,7 @@ androidx-compose-material3 = { group = "androidx.wear.compose", name = "compose-
androidx-work-runtime-ktx = { group = "androidx.work", name = "work-runtime-ktx", version.ref = "workRuntimeKtx" } androidx-work-runtime-ktx = { group = "androidx.work", name = "work-runtime-ktx", version.ref = "workRuntimeKtx" }
androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" } androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" }
androidx-material3 = { group = "androidx.compose.material3", name = "material3" } androidx-material3 = { group = "androidx.compose.material3", name = "material3" }
androidx-runtime-android = { group = "androidx.compose.runtime", name = "runtime-android", version.ref = "runtimeAndroid" }
#litert = { group = "com.google.ai.edge.litert", name = "litert", version.ref = "litert" } #litert = { group = "com.google.ai.edge.litert", name = "litert", version.ref = "litert" }
[plugins] [plugins]

View File

@@ -4,7 +4,12 @@ package com.birdsounds.identify
import android.util.Log import android.util.Log
import com.google.android.gms.wearable.MessageEvent import com.google.android.gms.wearable.MessageEvent
import com.google.android.gms.wearable.WearableListenerService import com.google.android.gms.wearable.WearableListenerService
import com.theeasiestway.opus.Constants
import com.theeasiestway.opus.Opus
import decodeAACToPCM import decodeAACToPCM
import java.io.ByteArrayOutputStream
import java.nio.ByteBuffer
import java.nio.ByteOrder
class MessageListenerService : WearableListenerService() { class MessageListenerService : WearableListenerService() {
@@ -12,7 +17,8 @@ class MessageListenerService : WearableListenerService() {
// fun placeSoundClassifier(soundClassifier: SoundClassifier) // fun placeSoundClassifier(soundClassifier: SoundClassifier)
override fun onMessageReceived(p0: MessageEvent) { override fun onMessageReceived(p0: MessageEvent) {
super.onMessageReceived(p0) super.onMessageReceived(p0)
val codec_opus = Opus()
codec_opus.decoderInit(Constants.SampleRate._48000(), Constants.Channels.mono())
// MainActivity // MainActivity
Log.w(TAG, "Data recv: "+p0.data.size.toString() + " bytes") Log.w(TAG, "Data recv: "+p0.data.size.toString() + " bytes")
val soundclassifier = MainActivity.soundClassifier val soundclassifier = MainActivity.soundClassifier
@@ -24,31 +30,49 @@ class MessageListenerService : WearableListenerService() {
} }
var tstamp_bytes = p0.data.copyOfRange(0, Long.SIZE_BYTES) var tstamp_bytes = p0.data.copyOfRange(0, Long.SIZE_BYTES)
var audio_bytes = p0.data.copyOfRange(Long.SIZE_BYTES, p0.data.size) var audio_bytes_og = p0.data.copyOfRange(Long.SIZE_BYTES, p0.data.size)
var string_send: String = ""
val pcm_byte_array = decodeAACToPCM(audio_bytes)
Log.e(TAG,"Size of short array buffer: "+ pcm_byte_array.size.toString()); val buffer = ByteBuffer.wrap(audio_bytes_og)
// ByteBuffer.wrap(audio_bytes).order( val sound_a = ByteArrayOutputStream();
// ByteOrder.LITTLE_ENDIAN val byteArrayList = mutableListOf<ByteArray>()
// ).asShortBuffer().get(short_array) while (buffer.hasRemaining())
Log.e(TAG, pcm_byte_array.sum().toString()) {
val num_to_read = buffer.get().toInt()
val read_this = ByteArray(num_to_read)
buffer.get(read_this)
val decoded = codec_opus.decode(read_this, Constants.FrameSize._120())
sound_a.write(decoded);
// Log.e(TAG,"Decompressed ${read_this.size} to ${decoded?.size}")
}
val audio_bytes = sound_a.toByteArray()
codec_opus.decoderRelease();
val short_array = ShortArray(audio_bytes.size/2)
// Log.e(TAG,"Size of short array buffer: "+ decoded?.size.toString());
ByteBuffer.wrap(audio_bytes).order(
ByteOrder.LITTLE_ENDIAN
).asShortBuffer().get(short_array)
// Log.e(TAG, pcm_byte_array.sum().toString())
Log.e(TAG, "STARTING SCORING"); Log.e(TAG, "STARTING SCORING");
// var sorted_list = soundclassifier.executeScoring(short_array)
// Log.w(TAG, "FINISHED SCORING"); var string_send: String = ""
// Log.w(TAG, "") var sorted_list = soundclassifier.executeScoring(short_array)
// for (i in 0 until 5) { Log.w(TAG, "FINISHED SCORING");
// val score = sorted_list[i].value Log.w(TAG, "")
// val index = sorted_list[i].index for (i in 0 until 5) {
// val species_name = soundclassifier.labelList[index] val score = sorted_list[i].value
// Log.w(TAG, species_name + ", " + score.toString()) val index = sorted_list[i].index
// string_send+= species_name val species_name = soundclassifier.labelList[index]
// string_send+=',' Log.w(TAG, species_name + ", " + score.toString())
// string_send+=score.toString() string_send+= species_name
// string_send+=';' string_send+=','
// } string_send+=score.toString()
string_send+=';'
}
MessageSenderFromPhone.sendMessage("/audio", tstamp_bytes + string_send.toByteArray(), this) MessageSenderFromPhone.sendMessage("/audio", tstamp_bytes + string_send.toByteArray(), this)
} }

View File

@@ -52,14 +52,13 @@ dependencies {
implementation("com.google.android.horologist:horologist-audio:0.6.18") implementation("com.google.android.horologist:horologist-audio:0.6.18")
implementation("com.google.android.horologist:horologist-compose-tools:0.6.18") implementation("com.google.android.horologist:horologist-compose-tools:0.6.18")
implementation("com.google.android.horologist:horologist-compose-tools:0.6.18") implementation("com.google.android.horologist:horologist-compose-tools:0.6.18")
implementation("com.google.android.horologist:horolo+++_gist-compose-layout:0.6.18")
implementation("androidx.compose.material:material-icons-core:1.6.8") implementation("androidx.compose.material:material-icons-core:1.6.8")
implementation("androidx.compose.material:material-icons-extended:1.6.8") implementation("androidx.compose.material:material-icons-extended:1.6.8")
implementation("com.google.android.horologist:horologist-compose-material:0.6.8") implementation("com.google.android.horologist:horologist-compose-material:0.6.8")
implementation("com.google.android.horologist:horologist-media-ui:0.6.8") implementation("com.google.android.horologist:horologist-media-ui:0.6.8")
implementation("com.google.android.horologist:horologist-media-data:0.6.8") implementation("com.google.android.horologist:horologist-media-data:0.6.8")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.8.1") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.8.1")
implementation("androidx.media3:media3-exoplayer:1.4.0")z implementation("androidx.media3:media3-exoplayer:1.4.0")
implementation(libs.play.services.wearable) implementation(libs.play.services.wearable)
implementation(platform(libs.androidx.compose.bom)) implementation(platform(libs.androidx.compose.bom))
implementation(libs.androidx.ui) implementation(libs.androidx.ui)
@@ -74,7 +73,8 @@ dependencies {
implementation(libs.androidx.media3.common) implementation(libs.androidx.media3.common)
implementation("co.touchlab:stately-concurrent-collections:2.0.0") implementation("co.touchlab:stately-concurrent-collections:2.0.0")
implementation(libs.androidx.compose.material3) implementation(libs.androidx.compose.material3)
implementation(libs.androidx.work.runtime.ktx) // androidTestImplementation(platform(libs.androidx.compose.bom)) implementation(libs.androidx.work.runtime.ktx)
implementation(libs.androidx.runtime.android) // androidTestImplementation(platform(libs.androidx.compose.bom))
// androidTestImplementation(libs.androidx.ui.test.junit4) // androidTestImplementation(libs.androidx.ui.test.junit4)
// debugImplementation(libs.androidx.ui.tooling) // debugImplementation(libs.androidx.ui.tooling)
// debugImplementation(libs.androidx.ui.test.manifest) // debugImplementation(libs.androidx.ui.test.manifest)

View File

@@ -17,8 +17,7 @@ import androidx.wear.compose.material.Text
@Composable @Composable
fun ControlDashboard(controlDashboardUiState: ControlDashboardUiState, fun ControlDashboard(controlDashboardUiState: ControlDashboardUiState,
onMicClicked: () -> Unit, onMicClicked: () -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier) {
navController: NavHostController) {
Box(contentAlignment = Alignment.Center, modifier = modifier.fillMaxSize()) { Box(contentAlignment = Alignment.Center, modifier = modifier.fillMaxSize()) {
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) { Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {

View File

@@ -105,12 +105,15 @@ fun WearApp() {
mainState.setNavController(navController); mainState.setNavController(navController);
SwipeDismissableNavHost(navController = navController, startDestination = "speaker") { SwipeDismissableNavHost(navController = navController, startDestination = "speaker") {
composable("species_list") {
ScreenScaffold {
SpeciesListView(context = context)
}
}
composable("speaker") { composable("speaker") {
StartRecordingScreen( StartRecordingScreen(
context = context,
navController = navController,
appState = mainState.appState, appState = mainState.appState,
isPermissionDenied = mainState.isPermissionDenied,
onMicClicked = { onMicClicked = {
scope.launch { scope.launch {
mainState.onMicClicked() mainState.onMicClicked()
@@ -119,9 +122,7 @@ fun WearApp() {
) )
} }
composable("species_list") {
SpeciesListView(context = context)
}
} }

View File

@@ -2,6 +2,7 @@ package com.birdsounds.identify.presentation
import com.google.android.gms.wearable.MessageEvent import com.google.android.gms.wearable.MessageEvent
import com.google.android.gms.wearable.WearableListenerService import com.google.android.gms.wearable.WearableListenerService
import java.nio.ByteBuffer
class MessageListenerService : WearableListenerService() { class MessageListenerService : WearableListenerService() {
@@ -9,19 +10,19 @@ class MessageListenerService : WearableListenerService() {
override fun onMessageReceived(p0: MessageEvent) { override fun onMessageReceived(p0: MessageEvent) {
super.onMessageReceived(p0) super.onMessageReceived(p0)
// val t_scored = ByteBuffer.wrap(p0.data).getLong() val t_scored = ByteBuffer.wrap(p0.data).getLong()
// var byte_strings: ByteArray = p0.data.copyOfRange(8, p0.data.size) var byte_strings: ByteArray = p0.data.copyOfRange(8, p0.data.size)
// var score_species_string = byte_strings.decodeToString() var score_species_string = byte_strings.decodeToString()
// var list_strings: List<String> = score_species_string.split(';') var list_strings: List<String> = score_species_string.split(';')
// list_strings.map({ list_strings.map({
// var split_str = it.split(',') var split_str = it.split(',')
// if (split_str.size == 2) { if (split_str.size == 2) {
// var out = AScore(split_str[0], split_str[1].toFloat(), t_scored) var out = AScore(split_str[0], split_str[1].toFloat(), t_scored)
// if (out.score > 0.05) { if (out.score > 0.005) {
// SpeciesList.add_observation(out) SpeciesList.add_observation(out)
// } }
// } }
// }) })
// MessageSender.messageLog.add(t_scored) MessageSender.messageLog.add(t_scored)
} }
} }

View File

@@ -1,4 +1,5 @@
package com.birdsounds.identify.presentation package com.birdsounds.identify.presentation
import com.theeasiestway.opus.Constants import com.theeasiestway.opus.Constants
import com.theeasiestway.opus.Opus import com.theeasiestway.opus.Opus
@@ -7,10 +8,13 @@ import android.content.Context
import android.media.AudioFormat import android.media.AudioFormat
import android.media.AudioRecord import android.media.AudioRecord
import android.media.MediaRecorder import android.media.MediaRecorder
import android.media.audiofx.AutomaticGainControl
import android.media.audiofx.NoiseSuppressor
import android.util.Log import android.util.Log
import androidx.annotation.RequiresPermission import androidx.annotation.RequiresPermission
import encodePcmToAac import encodePcmToAac
import kotlinx.coroutines.suspendCancellableCoroutine import kotlinx.coroutines.suspendCancellableCoroutine
import java.io.ByteArrayOutputStream
import java.nio.ByteBuffer import java.nio.ByteBuffer
import java.time.Instant import java.time.Instant
@@ -23,7 +27,7 @@ class SoundRecorder(
outputFileName: String outputFileName: String
) { ) {
private val codec = Opus(); val codec_opus = Opus();
private var state = State.IDLE private var state = State.IDLE
private var context = context_in private var context = context_in
@@ -37,86 +41,97 @@ class SoundRecorder(
suspendCancellableCoroutine<Unit> { cont -> suspendCancellableCoroutine<Unit> { cont ->
var noiseSuppressor: NoiseSuppressor? = null
var automaticGainControl: AutomaticGainControl? = null
var chunk_index: Int = 0 var chunk_index: Int = 0
val audioSource = MediaRecorder.AudioSource.DEFAULT val audioSource = MediaRecorder.AudioSource.DEFAULT
val sampleRateInHz = 48000 val sampleRateInHz = 48000
val channelConfig = AudioFormat.CHANNEL_IN_MONO val channelConfig = AudioFormat.CHANNEL_IN_MONO
val audioFormat = AudioFormat.ENCODING_PCM_16BIT val audioFormat = AudioFormat.ENCODING_PCM_16BIT
val buffer_size =
4 * AudioRecord.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat)
// Log.w(TAG, buffer_size.toString())
val bufferSizeInBytes = val frameSize = Constants.FrameSize._120();
sampleRateInHz * 3 * 2 // 3 second sample, 2 bytes for each sample val chunkSize = frameSize.v * 2; // Mono * 2 bytes per sample
val counts_until_reset = 3 * sampleRateInHz / frameSize.v;
val bufferSize =
AudioRecord.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat);
val chunk_size = 2 * sampleRateInHz / 4 // 250ms segments, 2 bytes for each sample
val num_chunks: Int = bufferSizeInBytes / chunk_size
val chunked_audio_bytes = Array(num_chunks) { ByteArray(chunk_size) }
val audio_bytes_array = ByteArray(bufferSizeInBytes)
val audioRecord = AudioRecord( val audioRecord = AudioRecord(
/* audioSource = */ audioSource, /* audioSource = */ audioSource,
/* sampleRateInHz = */ sampleRateInHz, /* sampleRateInHz = */ sampleRateInHz,
/* channelConfig = */ channelConfig, /* channelConfig = */ channelConfig,
/* audioFormat = */ audioFormat, /* audioFormat = */ audioFormat,
/* bufferSizeInBytes = */ buffer_size /* bufferSizeInBytes = */ bufferSize
) )
audioRecord.startRecording()
val thread = Thread { val thread = Thread {
// var sent_first: Boolean = false // var sent_first: Boolean = false
val outputByteStream = ByteBuffer.allocate(1000000)
audioRecord.startRecording()
var last_tstamp: Long = Instant.now().toEpochMilli(); var last_tstamp: Long = Instant.now().toEpochMilli();
while (true) /**/{ var count: Int = 0;
while (true) /**/ {
if (Thread.interrupted()) { if (Thread.interrupted()) {
// check for the interrupted flag, reset it, and throw exception audioRecord.release();
Log.w(TAG, "Finished thread") Log.w(TAG, "Finished thread")
break break
} }
chunk_index = chunk_index.mod(num_chunks)
if (chunk_index == 0) { if (count == 0) {
codec.encoderInit(48000, 1, Constants.Application.audio()); codec_opus.encoderInit(
Constants.SampleRate._48000(),
Constants.Channels.mono(),
Constants.Application.audio()
);
outputByteStream.clear();
} }
val out = audioRecord.read( val frame = ByteArray(chunkSize)
/* audioData = */ chunked_audio_bytes[chunk_index], var offset = 0
/* offsetInBytes = */ 0, var remained = frame.size
/* sizeInBytes = */ chunk_size, while (remained > 0) {
/* readMode = */ AudioRecord.READ_BLOCKING val read = audioRecord.read(frame, offset, remained)
) offset += read
remained -= read
}
val encoded: ByteArray? =
codec_opus.encode(bytes = frame, frameSize = frameSize);
if (encoded != null) {
outputByteStream.put(encoded.size.toByte());
outputByteStream.put(encoded);
}
count += 1;
chunk_index += 1 if (count == counts_until_reset) {
Log.d(TAG, "At count reset at ${count}");
codec_opus.encoderRelease();
count = 0;
val writtenBytes = outputByteStream.position()
Log.e(TAG,"Wrote ${writtenBytes} bytes!")
val duplicate = outputByteStream.duplicate()
duplicate.flip() // Prepare for reading
// outputByteStream.flip()
val bytes = ByteArray(writtenBytes)
duplicate.get(bytes)
// val result = ByteArray(outputByteStream.remaining());
var tstamp: Long = Instant.now().toEpochMilli() var tstamp: Long = Instant.now().toEpochMilli()
val tstamp_buffer = ByteBuffer.allocate(Long.SIZE_BYTES) val tstamp_buffer = ByteBuffer.allocate(Long.SIZE_BYTES)
val tstamp_bytes = tstamp_buffer.putLong(tstamp).array() val tstamp_bytes = tstamp_buffer.putLong(tstamp).array()
var byte_send: ByteArray = ByteArray(0) var byte_send: ByteArray = tstamp_bytes + bytes;
var strr: String = "" Log.e(TAG, "Sending message of 3s with size ${byte_send.size} + ${byte_send[0].toString()}")
for (i in 0..(num_chunks - 1)) { MessageSender.sendMessage("/audio", byte_send, context)
var c_index = i + chunk_index
c_index = c_index.mod(num_chunks)
strr += c_index.toString()
strr += ' '
byte_send += chunked_audio_bytes[c_index]
}
// do_send_message = false;
// num_chunked_since_last_send = 0
// ignore_warmup = false;
MessageSender.messageLog.clear()
val compressed = encodePcmToAac(byte_send)
Log.i(TAG,"Size pre-compression "+byte_send.size.toString())
Log.i(TAG,"Size post-compression "+compressed.size.toString())
MessageSender.sendMessage("/audio", compressed, context)
last_tstamp = tstamp
} }
} }
} }
thread.start() thread.start()
// thread.join(); // thread.join();

View File

@@ -2,6 +2,7 @@ package com.birdsounds.identify.presentation
import android.util.Log import android.util.Log
import androidx.compose.runtime.MutableState import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.snapshots.SnapshotStateList import androidx.compose.runtime.snapshots.SnapshotStateList
import java.time.Instant import java.time.Instant
@@ -13,16 +14,18 @@ class AScore(
) { ) {
var split_stuff = _species.split("_") val split_stuff: List<String> = _species.split("_");
val species = split_stuff[0] val species = split_stuff[0];
val score = _score val score = _score;
val common_name = split_stuff[1] // var common_name = split_stuff[1];
val common_name = if (split_stuff.size > 1) split_stuff[1] else "";
val timestamp = _timestamp val timestamp = _timestamp
fun age(): Long { fun age(): Long {
var tstamp: Long = Instant.now().toEpochMilli() var tstamp: Long = Instant.now().toEpochMilli()
return (tstamp - timestamp) return (tstamp - timestamp)
} }
override fun toString(): String { override fun toString(): String {
var tstamp: Long = Instant.now().toEpochMilli() var tstamp: Long = Instant.now().toEpochMilli()
return common_name + "," + species + "," + score.toString() + ", " + (age() / 1000.0).toString() + "s ago" return common_name + "," + species + "," + score.toString() + ", " + (age() / 1000.0).toString() + "s ago"
@@ -31,20 +34,20 @@ class AScore(
object SpeciesList { object SpeciesList {
var internal_list = mutableListOf<AScore>() var internal_list = mutableListOf<MutableState<AScore>>()
var do_add_observation = false var do_add_observation = false
var _list_on_ui: SnapshotStateList<MutableState<String>>? = null var _list_on_ui: SnapshotStateList<MutableState<AScore>>? = null
fun setSpeciecsShow_list(list_in: SnapshotStateList<MutableState<String>>) { fun setSpeciecsShow_list(list_in: SnapshotStateList<MutableState<AScore>>) {
_list_on_ui = list_in; _list_on_ui = list_in;
} }
fun add_observation(species_in: AScore) { fun add_observation(species_in: AScore) {
Log.w(TAG,"In add obsergation") Log.w(TAG, "In add obervation")
do_add_observation = false do_add_observation = false
var idx = 0 var idx = 0
var idx_replace = -1 var idx_replace = -1
for (i in internal_list) { for (i in internal_list) {
if (i.species == species_in.species) { if (i.value.species == species_in.species) {
do_add_observation = false do_add_observation = false
idx_replace = idx idx_replace = idx
} }
@@ -52,7 +55,8 @@ object SpeciesList {
} }
if (idx_replace >= 0) { if (idx_replace >= 0) {
Log.w(TAG, "Replacing") Log.w(TAG, "Replacing")
internal_list[idx_replace] = species_in // _list_on_ui?.removeAt(idx_replace) internal_list[idx_replace].value =
species_in // _list_on_ui?.removeAt(idx_replace)
// _list_on_ui?.add(species_in); // _list_on_ui?.add(species_in);
// if (_list_on_ui != null) { // if (_list_on_ui != null) {
// _list_on_ui?.removeAt(idx_replace) // _list_on_ui?.removeAt(idx_replace)
@@ -60,13 +64,26 @@ object SpeciesList {
// } // }
} else { } else {
internal_list.add(species_in) internal_list.add(mutableStateOf(species_in))
} }
internal_list = internal_list.sortedBy({ (it.age()) }).toMutableList() internal_list = internal_list.sortedBy({ (it.value.age()) }).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");
}
Log.w(TAG, "Internal list " + internal_list.toString())
for ((index, value) in internal_list.withIndex()) { for ((index, value) in internal_list.withIndex()) {
_list_on_ui?.get(index)?.value = value.common_name; 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
}
} }
Log.w(TAG, internal_list.size.toString()) Log.w(TAG, internal_list.size.toString())

View File

@@ -1,48 +1,59 @@
package com.birdsounds.identify.presentation package com.birdsounds.identify.presentation
import android.content.Context import android.content.Context
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.snapshots.SnapshotStateList import androidx.compose.runtime.snapshots.SnapshotStateList
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.wear.compose.foundation.lazy.items
import androidx.wear.compose.material.Text import androidx.wear.compose.material.Text
import com.google.android.horologist.annotations.ExperimentalHorologistApi 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.ScreenScaffold
import com.google.android.horologist.compose.layout.rememberResponsiveColumnState
@OptIn(ExperimentalHorologistApi::class) @OptIn(ExperimentalHorologistApi::class)
@Composable @Composable
fun SpeciesListView(context: Context, fun SpeciesListView(
) { context: Context,
val text: MutableState<String> = mutableStateOf("text") ) {
//text.toString()
val species_list_show = mutableStateListOf<MutableState<String>>() val species_list_show = mutableStateListOf<MutableState<AScore>>()
for (i in 1..10) for (i in 1..10) {
{ species_list_show.add(mutableStateOf(AScore(i.toString()+"_"+i.toString(), 0.00F, 0L)));
val hi = mutableStateOf("hi")
species_list_show.add(hi);
} }
SpeciesList.setSpeciecsShow_list(species_list_show) SpeciesList.setSpeciecsShow_list(species_list_show)
val species_show: SnapshotStateList<MutableState<String>> = remember { species_list_show }
val columnState =
rememberResponsiveColumnState(contentPadding = ScalingLazyColumnDefaults.padding(first = ScalingLazyColumnDefaults.ItemType.Text,
last = ScalingLazyColumnDefaults.ItemType.Chip))
ScreenScaffold(scrollState = columnState) {
ScalingLazyColumn(columnState = columnState, modifier = Modifier.fillMaxSize()) {
items(species_show) { aSpec -> Text(text = aSpec.value)
} // Dynamically display the chips
} 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) }
} }
} }
// val columnState =
// rememberResponsiveColumnState(contentPadding = ScalingLazyColumnDefaults.padding(first = ScalingLazyColumnDefaults.ItemType.Text,
// last = ScalingLazyColumnDefaults.ItemType.Chip))
// ScreenScaffold(scrollState = columnState) {
// ScalingLazyColumn(columnState = columnState, modifier = Modifier.fillMaxSize()) {
// items(species_show) { aSpec -> Text(text = aSpec.value)
// } // Dynamically display the chips
//
// }
// }

View File

@@ -9,10 +9,7 @@ import com.google.android.horologist.compose.layout.ScreenScaffold
@Composable @Composable
fun StartRecordingScreen( fun StartRecordingScreen(
context: Context,
appState: AppState, appState: AppState,
navController: NavHostController,
isPermissionDenied: Boolean,
onMicClicked: () -> Unit onMicClicked: () -> Unit
) { ) {
ScreenScaffold { ScreenScaffold {
@@ -21,8 +18,7 @@ fun StartRecordingScreen(
) )
ControlDashboard( ControlDashboard(
controlDashboardUiState = controlDashboardUiState, controlDashboardUiState = controlDashboardUiState,
onMicClicked = onMicClicked, onMicClicked = onMicClicked
navController = navController
) )
} }