YACWC
This commit is contained in:
21
.idea/deploymentTargetSelector.xml
generated
21
.idea/deploymentTargetSelector.xml
generated
@@ -4,18 +4,29 @@
|
||||
<selectionStates>
|
||||
<SelectionState runConfigName="mobile">
|
||||
<option name="selectionMode" value="DROPDOWN" />
|
||||
</SelectionState>
|
||||
<SelectionState runConfigName="wear">
|
||||
<option name="selectionMode" value="DROPDOWN" />
|
||||
<DropdownSelection timestamp="2025-03-08T17:32:57.542834800Z">
|
||||
<DropdownSelection timestamp="2025-03-09T15:35:16.375287800Z">
|
||||
<Target type="DEFAULT_BOOT">
|
||||
<handle>
|
||||
<DeviceId pluginId="Default" identifier="serial=192.168.1.195:35507;connection=7a90992e" />
|
||||
<DeviceId pluginId="PhysicalDevice" identifier="serial=RFAX803LMFR" />
|
||||
</handle>
|
||||
</Target>
|
||||
</DropdownSelection>
|
||||
<DialogSelection />
|
||||
</SelectionState>
|
||||
<SelectionState runConfigName="wear">
|
||||
<option name="selectionMode" value="DROPDOWN" />
|
||||
<DropdownSelection timestamp="2025-03-29T14:49:45.062493500Z">
|
||||
<Target type="DEFAULT_BOOT">
|
||||
<handle>
|
||||
<DeviceId pluginId="LocalEmulator" identifier="path=C:\Users\thebears\.android\avd\Wear_OS_Large_Round_API_34.avd" />
|
||||
</handle>
|
||||
</Target>
|
||||
</DropdownSelection>
|
||||
<DialogSelection />
|
||||
</SelectionState>
|
||||
<SelectionState runConfigName="MainActivity">
|
||||
<option name="selectionMode" value="DROPDOWN" />
|
||||
</SelectionState>
|
||||
</selectionStates>
|
||||
</component>
|
||||
</project>
|
||||
2
.idea/kotlinc.xml
generated
2
.idea/kotlinc.xml
generated
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="KotlinJpsPluginSettings">
|
||||
<option name="version" value="2.0.20" />
|
||||
<option name="version" value="2.1.10" />
|
||||
</component>
|
||||
</project>
|
||||
254
.idea/workspace (SFConflict ispatel 2025-03-10-10-03-26).xml
generated
Normal file
254
.idea/workspace (SFConflict ispatel 2025-03-10-10-03-26).xml
generated
Normal file
@@ -0,0 +1,254 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="AndroidLayouts">
|
||||
<shared>
|
||||
<config />
|
||||
</shared>
|
||||
<layouts>
|
||||
<layout url="file://$PROJECT_DIR$/wear/src/main/res/layout/layout.xml">
|
||||
<config>
|
||||
<theme>@android:style/Theme.DeviceDefault</theme>
|
||||
</config>
|
||||
</layout>
|
||||
</layouts>
|
||||
</component>
|
||||
<component name="AutoImportSettings">
|
||||
<option name="autoReloadType" value="NONE" />
|
||||
</component>
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="e1c82fb5-3f9e-43af-a0be-9f1954a6c1b9" name="Changes" comment="">
|
||||
<change beforePath="$PROJECT_DIR$/mobile/src/main/java/com/birdsounds/identify/Downloader.java" beforeDir="false" afterPath="$PROJECT_DIR$/mobile/src/main/java/com/birdsounds/identify/Downloader.java" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/mobile/src/main/java/com/birdsounds/identify/SoundClassifier.kt" beforeDir="false" afterPath="$PROJECT_DIR$/mobile/src/main/java/com/birdsounds/identify/SoundClassifier.kt" afterDir="false" />
|
||||
</list>
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
||||
<option name="LAST_RESOLUTION" value="IGNORE" />
|
||||
</component>
|
||||
<component name="ClangdSettings">
|
||||
<option name="formatViaClangd" value="false" />
|
||||
</component>
|
||||
<component name="ExecutionTargetManager" SELECTED_TARGET="device_and_snapshot_combo_box_target[]" />
|
||||
<component name="ExternalProjectsData">
|
||||
<projectState path="$PROJECT_DIR$">
|
||||
<ProjectState />
|
||||
</projectState>
|
||||
</component>
|
||||
<component name="FileTemplateManagerImpl">
|
||||
<option name="RECENT_TEMPLATES">
|
||||
<list>
|
||||
<option value="Class" />
|
||||
<option value="Kotlin Class" />
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
<component name="Git.Settings">
|
||||
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
|
||||
</component>
|
||||
<component name="ProjectColorInfo">{
|
||||
"associatedIndex": 0
|
||||
}</component>
|
||||
<component name="ProjectId" id="2toSwb3riPZMH7ufWxT9aBYCKaO" />
|
||||
<component name="ProjectViewState">
|
||||
<option name="hideEmptyMiddlePackages" value="true" />
|
||||
<option name="showLibraryContents" value="true" />
|
||||
</component>
|
||||
<component name="PropertiesComponent">{
|
||||
"keyToString": {
|
||||
"Android App.mobile.executor": "Run",
|
||||
"Android App.wear.executor": "Run",
|
||||
"RunOnceActivity.ShowReadmeOnStart": "true",
|
||||
"RunOnceActivity.cidr.known.project.marker": "true",
|
||||
"RunOnceActivity.readMode.enableVisualFormatting": "true",
|
||||
"cf.first.check.clang-format": "false",
|
||||
"cidr.known.project.marker": "true",
|
||||
"com.google.services.firebase.aqiPopupShown": "true",
|
||||
"git-widget-placeholder": "master",
|
||||
"ignore.virus.scanning.warn.message": "true",
|
||||
"kotlin-language-version-configured": "true",
|
||||
"last_opened_file_path": "C:/Users/isp/Seafile/Designs/Android/bird_sound_identify_wearos",
|
||||
"project.structure.last.edited": "Modules",
|
||||
"project.structure.proportion": "0.17",
|
||||
"project.structure.side.proportion": "0.2"
|
||||
}
|
||||
}</component>
|
||||
<component name="RecentsManager">
|
||||
<key name="MoveFile.RECENT_KEYS">
|
||||
<recent name="C:\Users\isp\Seafile\Designs\Android\bird_sound_identify_wearos\mobile\src\main\assets\2024_08_16" />
|
||||
</key>
|
||||
<key name="MoveKotlinTopLevelDeclarationsDialog.RECENTS_KEY">
|
||||
<recent name="com.birdsounds.identify.presentation" />
|
||||
</key>
|
||||
</component>
|
||||
<component name="RunManager" selected="Android App.mobile">
|
||||
<configuration name="mobile" type="AndroidRunConfigurationType" factoryName="Android App" activateToolWindowBeforeRun="false">
|
||||
<module name="identify.mobile" />
|
||||
<option name="ANDROID_RUN_CONFIGURATION_SCHEMA_VERSION" value="1" />
|
||||
<option name="DEPLOY" value="true" />
|
||||
<option name="DEPLOY_APK_FROM_BUNDLE" value="false" />
|
||||
<option name="DEPLOY_AS_INSTANT" value="false" />
|
||||
<option name="ARTIFACT_NAME" value="" />
|
||||
<option name="PM_INSTALL_OPTIONS" value="" />
|
||||
<option name="ALL_USERS" value="false" />
|
||||
<option name="ALWAYS_INSTALL_WITH_PM" value="false" />
|
||||
<option name="ALLOW_ASSUME_VERIFIED" value="false" />
|
||||
<option name="CLEAR_APP_STORAGE" value="false" />
|
||||
<option name="DYNAMIC_FEATURES_DISABLED_LIST" value="" />
|
||||
<option name="ACTIVITY_EXTRA_FLAGS" value="" />
|
||||
<option name="MODE" value="default_activity" />
|
||||
<option name="RESTORE_ENABLED" value="false" />
|
||||
<option name="RESTORE_FILE" value="" />
|
||||
<option name="CLEAR_LOGCAT" value="false" />
|
||||
<option name="SHOW_LOGCAT_AUTOMATICALLY" value="false" />
|
||||
<option name="TARGET_SELECTION_MODE" value="DEVICE_AND_SNAPSHOT_COMBO_BOX" />
|
||||
<option name="SELECTED_CLOUD_MATRIX_CONFIGURATION_ID" value="-1" />
|
||||
<option name="SELECTED_CLOUD_MATRIX_PROJECT_ID" value="" />
|
||||
<option name="DEBUGGER_TYPE" value="Auto" />
|
||||
<Auto>
|
||||
<option name="USE_JAVA_AWARE_DEBUGGER" value="false" />
|
||||
<option name="SHOW_STATIC_VARS" value="true" />
|
||||
<option name="WORKING_DIR" value="" />
|
||||
<option name="TARGET_LOGGING_CHANNELS" value="lldb process:gdb-remote packets" />
|
||||
<option name="SHOW_OPTIMIZED_WARNING" value="true" />
|
||||
<option name="ATTACH_ON_WAIT_FOR_DEBUGGER" value="false" />
|
||||
<option name="DEBUG_SANDBOX_SDK" value="false" />
|
||||
</Auto>
|
||||
<Hybrid>
|
||||
<option name="USE_JAVA_AWARE_DEBUGGER" value="false" />
|
||||
<option name="SHOW_STATIC_VARS" value="true" />
|
||||
<option name="WORKING_DIR" value="" />
|
||||
<option name="TARGET_LOGGING_CHANNELS" value="lldb process:gdb-remote packets" />
|
||||
<option name="SHOW_OPTIMIZED_WARNING" value="true" />
|
||||
<option name="ATTACH_ON_WAIT_FOR_DEBUGGER" value="false" />
|
||||
<option name="DEBUG_SANDBOX_SDK" value="false" />
|
||||
</Hybrid>
|
||||
<Java>
|
||||
<option name="ATTACH_ON_WAIT_FOR_DEBUGGER" value="false" />
|
||||
<option name="DEBUG_SANDBOX_SDK" value="false" />
|
||||
</Java>
|
||||
<Native>
|
||||
<option name="USE_JAVA_AWARE_DEBUGGER" value="false" />
|
||||
<option name="SHOW_STATIC_VARS" value="true" />
|
||||
<option name="WORKING_DIR" value="" />
|
||||
<option name="TARGET_LOGGING_CHANNELS" value="lldb process:gdb-remote packets" />
|
||||
<option name="SHOW_OPTIMIZED_WARNING" value="true" />
|
||||
<option name="ATTACH_ON_WAIT_FOR_DEBUGGER" value="false" />
|
||||
<option name="DEBUG_SANDBOX_SDK" value="false" />
|
||||
</Native>
|
||||
<Profilers>
|
||||
<option name="ADVANCED_PROFILING_ENABLED" value="false" />
|
||||
<option name="STARTUP_PROFILING_ENABLED" value="false" />
|
||||
<option name="STARTUP_CPU_PROFILING_ENABLED" value="false" />
|
||||
<option name="STARTUP_CPU_PROFILING_CONFIGURATION_NAME" value="Java/Kotlin Method Sample (legacy)" />
|
||||
<option name="STARTUP_NATIVE_MEMORY_PROFILING_ENABLED" value="false" />
|
||||
<option name="NATIVE_MEMORY_SAMPLE_RATE_BYTES" value="2048" />
|
||||
</Profilers>
|
||||
<option name="DEEP_LINK" value="" />
|
||||
<option name="ACTIVITY" value="" />
|
||||
<option name="ACTIVITY_CLASS" value="" />
|
||||
<option name="SEARCH_ACTIVITY_IN_GLOBAL_SCOPE" value="false" />
|
||||
<option name="SKIP_ACTIVITY_VALIDATION" value="false" />
|
||||
<method v="2">
|
||||
<option name="Android.Gradle.BeforeRunTask" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
<configuration name="wear" type="AndroidRunConfigurationType" factoryName="Android App" activateToolWindowBeforeRun="false">
|
||||
<module name="identify.wear" />
|
||||
<option name="ANDROID_RUN_CONFIGURATION_SCHEMA_VERSION" value="1" />
|
||||
<option name="DEPLOY" value="true" />
|
||||
<option name="DEPLOY_APK_FROM_BUNDLE" value="false" />
|
||||
<option name="DEPLOY_AS_INSTANT" value="false" />
|
||||
<option name="ARTIFACT_NAME" value="" />
|
||||
<option name="PM_INSTALL_OPTIONS" value="" />
|
||||
<option name="ALL_USERS" value="false" />
|
||||
<option name="ALWAYS_INSTALL_WITH_PM" value="false" />
|
||||
<option name="ALLOW_ASSUME_VERIFIED" value="false" />
|
||||
<option name="CLEAR_APP_STORAGE" value="false" />
|
||||
<option name="DYNAMIC_FEATURES_DISABLED_LIST" value="" />
|
||||
<option name="ACTIVITY_EXTRA_FLAGS" value="" />
|
||||
<option name="MODE" value="default_activity" />
|
||||
<option name="RESTORE_ENABLED" value="false" />
|
||||
<option name="RESTORE_FILE" value="" />
|
||||
<option name="CLEAR_LOGCAT" value="false" />
|
||||
<option name="SHOW_LOGCAT_AUTOMATICALLY" value="false" />
|
||||
<option name="TARGET_SELECTION_MODE" value="DEVICE_AND_SNAPSHOT_COMBO_BOX" />
|
||||
<option name="SELECTED_CLOUD_MATRIX_CONFIGURATION_ID" value="-1" />
|
||||
<option name="SELECTED_CLOUD_MATRIX_PROJECT_ID" value="" />
|
||||
<option name="DEBUGGER_TYPE" value="Auto" />
|
||||
<Auto>
|
||||
<option name="USE_JAVA_AWARE_DEBUGGER" value="false" />
|
||||
<option name="SHOW_STATIC_VARS" value="true" />
|
||||
<option name="WORKING_DIR" value="" />
|
||||
<option name="TARGET_LOGGING_CHANNELS" value="lldb process:gdb-remote packets" />
|
||||
<option name="SHOW_OPTIMIZED_WARNING" value="true" />
|
||||
<option name="ATTACH_ON_WAIT_FOR_DEBUGGER" value="false" />
|
||||
<option name="DEBUG_SANDBOX_SDK" value="false" />
|
||||
</Auto>
|
||||
<Hybrid>
|
||||
<option name="USE_JAVA_AWARE_DEBUGGER" value="false" />
|
||||
<option name="SHOW_STATIC_VARS" value="true" />
|
||||
<option name="WORKING_DIR" value="" />
|
||||
<option name="TARGET_LOGGING_CHANNELS" value="lldb process:gdb-remote packets" />
|
||||
<option name="SHOW_OPTIMIZED_WARNING" value="true" />
|
||||
<option name="ATTACH_ON_WAIT_FOR_DEBUGGER" value="false" />
|
||||
<option name="DEBUG_SANDBOX_SDK" value="false" />
|
||||
</Hybrid>
|
||||
<Java>
|
||||
<option name="ATTACH_ON_WAIT_FOR_DEBUGGER" value="false" />
|
||||
<option name="DEBUG_SANDBOX_SDK" value="false" />
|
||||
</Java>
|
||||
<Native>
|
||||
<option name="USE_JAVA_AWARE_DEBUGGER" value="false" />
|
||||
<option name="SHOW_STATIC_VARS" value="true" />
|
||||
<option name="WORKING_DIR" value="" />
|
||||
<option name="TARGET_LOGGING_CHANNELS" value="lldb process:gdb-remote packets" />
|
||||
<option name="SHOW_OPTIMIZED_WARNING" value="true" />
|
||||
<option name="ATTACH_ON_WAIT_FOR_DEBUGGER" value="false" />
|
||||
<option name="DEBUG_SANDBOX_SDK" value="false" />
|
||||
</Native>
|
||||
<Profilers>
|
||||
<option name="ADVANCED_PROFILING_ENABLED" value="false" />
|
||||
<option name="STARTUP_PROFILING_ENABLED" value="false" />
|
||||
<option name="STARTUP_CPU_PROFILING_ENABLED" value="false" />
|
||||
<option name="STARTUP_CPU_PROFILING_CONFIGURATION_NAME" value="Java/Kotlin Method Sample (legacy)" />
|
||||
<option name="STARTUP_NATIVE_MEMORY_PROFILING_ENABLED" value="false" />
|
||||
<option name="NATIVE_MEMORY_SAMPLE_RATE_BYTES" value="2048" />
|
||||
</Profilers>
|
||||
<option name="DEEP_LINK" value="" />
|
||||
<option name="ACTIVITY" value="" />
|
||||
<option name="ACTIVITY_CLASS" value="" />
|
||||
<option name="SEARCH_ACTIVITY_IN_GLOBAL_SCOPE" value="false" />
|
||||
<option name="SKIP_ACTIVITY_VALIDATION" value="false" />
|
||||
<method v="2">
|
||||
<option name="Android.Gradle.BeforeRunTask" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
|
||||
<component name="TaskManager">
|
||||
<task active="true" id="Default" summary="Default task">
|
||||
<changelist id="e1c82fb5-3f9e-43af-a0be-9f1954a6c1b9" name="Changes" comment="" />
|
||||
<created>1741017174616</created>
|
||||
<option name="number" value="Default" />
|
||||
<option name="presentableId" value="Default" />
|
||||
<updated>1741017174616</updated>
|
||||
</task>
|
||||
<servers />
|
||||
</component>
|
||||
<component name="play_dynamic_filters_status">
|
||||
<option name="appIdToCheckInfo">
|
||||
<map>
|
||||
<entry key="com.birdsounds.identify">
|
||||
<value>
|
||||
<CheckInfo lastCheckTimestamp="1741381679027" />
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="com.birdsounds.identify.test">
|
||||
<value>
|
||||
<CheckInfo lastCheckTimestamp="1741381679027" />
|
||||
</value>
|
||||
</entry>
|
||||
</map>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
254
.idea/workspace (SFConflict ispatel@live.com 2025-03-08-01-11-41).xml
generated
Normal file
254
.idea/workspace (SFConflict ispatel@live.com 2025-03-08-01-11-41).xml
generated
Normal file
@@ -0,0 +1,254 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="AndroidLayouts">
|
||||
<shared>
|
||||
<config />
|
||||
</shared>
|
||||
<layouts>
|
||||
<layout url="file://$PROJECT_DIR$/wear/src/main/res/layout/layout.xml">
|
||||
<config>
|
||||
<theme>@android:style/Theme.DeviceDefault</theme>
|
||||
</config>
|
||||
</layout>
|
||||
</layouts>
|
||||
</component>
|
||||
<component name="AutoImportSettings">
|
||||
<option name="autoReloadType" value="NONE" />
|
||||
</component>
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="e1c82fb5-3f9e-43af-a0be-9f1954a6c1b9" name="Changes" comment="">
|
||||
<change beforePath="$PROJECT_DIR$/mobile/src/main/java/com/birdsounds/identify/Downloader.java" beforeDir="false" afterPath="$PROJECT_DIR$/mobile/src/main/java/com/birdsounds/identify/Downloader.java" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/mobile/src/main/java/com/birdsounds/identify/SoundClassifier.kt" beforeDir="false" afterPath="$PROJECT_DIR$/mobile/src/main/java/com/birdsounds/identify/SoundClassifier.kt" afterDir="false" />
|
||||
</list>
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
||||
<option name="LAST_RESOLUTION" value="IGNORE" />
|
||||
</component>
|
||||
<component name="ClangdSettings">
|
||||
<option name="formatViaClangd" value="false" />
|
||||
</component>
|
||||
<component name="ExecutionTargetManager" SELECTED_TARGET="device_and_snapshot_combo_box_target[]" />
|
||||
<component name="ExternalProjectsData">
|
||||
<projectState path="$PROJECT_DIR$">
|
||||
<ProjectState />
|
||||
</projectState>
|
||||
</component>
|
||||
<component name="FileTemplateManagerImpl">
|
||||
<option name="RECENT_TEMPLATES">
|
||||
<list>
|
||||
<option value="Class" />
|
||||
<option value="Kotlin Class" />
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
<component name="Git.Settings">
|
||||
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
|
||||
</component>
|
||||
<component name="ProjectColorInfo">{
|
||||
"associatedIndex": 0
|
||||
}</component>
|
||||
<component name="ProjectId" id="2toSwb3riPZMH7ufWxT9aBYCKaO" />
|
||||
<component name="ProjectViewState">
|
||||
<option name="hideEmptyMiddlePackages" value="true" />
|
||||
<option name="showLibraryContents" value="true" />
|
||||
</component>
|
||||
<component name="PropertiesComponent">{
|
||||
"keyToString": {
|
||||
"Android App.mobile.executor": "Run",
|
||||
"Android App.wear.executor": "Run",
|
||||
"RunOnceActivity.ShowReadmeOnStart": "true",
|
||||
"RunOnceActivity.cidr.known.project.marker": "true",
|
||||
"RunOnceActivity.readMode.enableVisualFormatting": "true",
|
||||
"cf.first.check.clang-format": "false",
|
||||
"cidr.known.project.marker": "true",
|
||||
"com.google.services.firebase.aqiPopupShown": "true",
|
||||
"git-widget-placeholder": "master",
|
||||
"ignore.virus.scanning.warn.message": "true",
|
||||
"kotlin-language-version-configured": "true",
|
||||
"last_opened_file_path": "C:/Users/isp/Seafile/Designs/Android/bird_sound_identify_wearos",
|
||||
"project.structure.last.edited": "Modules",
|
||||
"project.structure.proportion": "0.17",
|
||||
"project.structure.side.proportion": "0.2"
|
||||
}
|
||||
}</component>
|
||||
<component name="RecentsManager">
|
||||
<key name="MoveFile.RECENT_KEYS">
|
||||
<recent name="C:\Users\isp\Seafile\Designs\Android\bird_sound_identify_wearos\mobile\src\main\assets\2024_08_16" />
|
||||
</key>
|
||||
<key name="MoveKotlinTopLevelDeclarationsDialog.RECENTS_KEY">
|
||||
<recent name="com.birdsounds.identify.presentation" />
|
||||
</key>
|
||||
</component>
|
||||
<component name="RunManager" selected="Android App.mobile">
|
||||
<configuration name="mobile" type="AndroidRunConfigurationType" factoryName="Android App" activateToolWindowBeforeRun="false">
|
||||
<module name="identify.mobile" />
|
||||
<option name="ANDROID_RUN_CONFIGURATION_SCHEMA_VERSION" value="1" />
|
||||
<option name="DEPLOY" value="true" />
|
||||
<option name="DEPLOY_APK_FROM_BUNDLE" value="false" />
|
||||
<option name="DEPLOY_AS_INSTANT" value="false" />
|
||||
<option name="ARTIFACT_NAME" value="" />
|
||||
<option name="PM_INSTALL_OPTIONS" value="" />
|
||||
<option name="ALL_USERS" value="false" />
|
||||
<option name="ALWAYS_INSTALL_WITH_PM" value="false" />
|
||||
<option name="ALLOW_ASSUME_VERIFIED" value="false" />
|
||||
<option name="CLEAR_APP_STORAGE" value="false" />
|
||||
<option name="DYNAMIC_FEATURES_DISABLED_LIST" value="" />
|
||||
<option name="ACTIVITY_EXTRA_FLAGS" value="" />
|
||||
<option name="MODE" value="default_activity" />
|
||||
<option name="RESTORE_ENABLED" value="false" />
|
||||
<option name="RESTORE_FILE" value="" />
|
||||
<option name="CLEAR_LOGCAT" value="false" />
|
||||
<option name="SHOW_LOGCAT_AUTOMATICALLY" value="false" />
|
||||
<option name="TARGET_SELECTION_MODE" value="DEVICE_AND_SNAPSHOT_COMBO_BOX" />
|
||||
<option name="SELECTED_CLOUD_MATRIX_CONFIGURATION_ID" value="-1" />
|
||||
<option name="SELECTED_CLOUD_MATRIX_PROJECT_ID" value="" />
|
||||
<option name="DEBUGGER_TYPE" value="Auto" />
|
||||
<Auto>
|
||||
<option name="USE_JAVA_AWARE_DEBUGGER" value="false" />
|
||||
<option name="SHOW_STATIC_VARS" value="true" />
|
||||
<option name="WORKING_DIR" value="" />
|
||||
<option name="TARGET_LOGGING_CHANNELS" value="lldb process:gdb-remote packets" />
|
||||
<option name="SHOW_OPTIMIZED_WARNING" value="true" />
|
||||
<option name="ATTACH_ON_WAIT_FOR_DEBUGGER" value="false" />
|
||||
<option name="DEBUG_SANDBOX_SDK" value="false" />
|
||||
</Auto>
|
||||
<Hybrid>
|
||||
<option name="USE_JAVA_AWARE_DEBUGGER" value="false" />
|
||||
<option name="SHOW_STATIC_VARS" value="true" />
|
||||
<option name="WORKING_DIR" value="" />
|
||||
<option name="TARGET_LOGGING_CHANNELS" value="lldb process:gdb-remote packets" />
|
||||
<option name="SHOW_OPTIMIZED_WARNING" value="true" />
|
||||
<option name="ATTACH_ON_WAIT_FOR_DEBUGGER" value="false" />
|
||||
<option name="DEBUG_SANDBOX_SDK" value="false" />
|
||||
</Hybrid>
|
||||
<Java>
|
||||
<option name="ATTACH_ON_WAIT_FOR_DEBUGGER" value="false" />
|
||||
<option name="DEBUG_SANDBOX_SDK" value="false" />
|
||||
</Java>
|
||||
<Native>
|
||||
<option name="USE_JAVA_AWARE_DEBUGGER" value="false" />
|
||||
<option name="SHOW_STATIC_VARS" value="true" />
|
||||
<option name="WORKING_DIR" value="" />
|
||||
<option name="TARGET_LOGGING_CHANNELS" value="lldb process:gdb-remote packets" />
|
||||
<option name="SHOW_OPTIMIZED_WARNING" value="true" />
|
||||
<option name="ATTACH_ON_WAIT_FOR_DEBUGGER" value="false" />
|
||||
<option name="DEBUG_SANDBOX_SDK" value="false" />
|
||||
</Native>
|
||||
<Profilers>
|
||||
<option name="ADVANCED_PROFILING_ENABLED" value="false" />
|
||||
<option name="STARTUP_PROFILING_ENABLED" value="false" />
|
||||
<option name="STARTUP_CPU_PROFILING_ENABLED" value="false" />
|
||||
<option name="STARTUP_CPU_PROFILING_CONFIGURATION_NAME" value="Java/Kotlin Method Sample (legacy)" />
|
||||
<option name="STARTUP_NATIVE_MEMORY_PROFILING_ENABLED" value="false" />
|
||||
<option name="NATIVE_MEMORY_SAMPLE_RATE_BYTES" value="2048" />
|
||||
</Profilers>
|
||||
<option name="DEEP_LINK" value="" />
|
||||
<option name="ACTIVITY" value="" />
|
||||
<option name="ACTIVITY_CLASS" value="" />
|
||||
<option name="SEARCH_ACTIVITY_IN_GLOBAL_SCOPE" value="false" />
|
||||
<option name="SKIP_ACTIVITY_VALIDATION" value="false" />
|
||||
<method v="2">
|
||||
<option name="Android.Gradle.BeforeRunTask" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
<configuration name="wear" type="AndroidRunConfigurationType" factoryName="Android App" activateToolWindowBeforeRun="false">
|
||||
<module name="identify.wear" />
|
||||
<option name="ANDROID_RUN_CONFIGURATION_SCHEMA_VERSION" value="1" />
|
||||
<option name="DEPLOY" value="true" />
|
||||
<option name="DEPLOY_APK_FROM_BUNDLE" value="false" />
|
||||
<option name="DEPLOY_AS_INSTANT" value="false" />
|
||||
<option name="ARTIFACT_NAME" value="" />
|
||||
<option name="PM_INSTALL_OPTIONS" value="" />
|
||||
<option name="ALL_USERS" value="false" />
|
||||
<option name="ALWAYS_INSTALL_WITH_PM" value="false" />
|
||||
<option name="ALLOW_ASSUME_VERIFIED" value="false" />
|
||||
<option name="CLEAR_APP_STORAGE" value="false" />
|
||||
<option name="DYNAMIC_FEATURES_DISABLED_LIST" value="" />
|
||||
<option name="ACTIVITY_EXTRA_FLAGS" value="" />
|
||||
<option name="MODE" value="default_activity" />
|
||||
<option name="RESTORE_ENABLED" value="false" />
|
||||
<option name="RESTORE_FILE" value="" />
|
||||
<option name="CLEAR_LOGCAT" value="false" />
|
||||
<option name="SHOW_LOGCAT_AUTOMATICALLY" value="false" />
|
||||
<option name="TARGET_SELECTION_MODE" value="DEVICE_AND_SNAPSHOT_COMBO_BOX" />
|
||||
<option name="SELECTED_CLOUD_MATRIX_CONFIGURATION_ID" value="-1" />
|
||||
<option name="SELECTED_CLOUD_MATRIX_PROJECT_ID" value="" />
|
||||
<option name="DEBUGGER_TYPE" value="Auto" />
|
||||
<Auto>
|
||||
<option name="USE_JAVA_AWARE_DEBUGGER" value="false" />
|
||||
<option name="SHOW_STATIC_VARS" value="true" />
|
||||
<option name="WORKING_DIR" value="" />
|
||||
<option name="TARGET_LOGGING_CHANNELS" value="lldb process:gdb-remote packets" />
|
||||
<option name="SHOW_OPTIMIZED_WARNING" value="true" />
|
||||
<option name="ATTACH_ON_WAIT_FOR_DEBUGGER" value="false" />
|
||||
<option name="DEBUG_SANDBOX_SDK" value="false" />
|
||||
</Auto>
|
||||
<Hybrid>
|
||||
<option name="USE_JAVA_AWARE_DEBUGGER" value="false" />
|
||||
<option name="SHOW_STATIC_VARS" value="true" />
|
||||
<option name="WORKING_DIR" value="" />
|
||||
<option name="TARGET_LOGGING_CHANNELS" value="lldb process:gdb-remote packets" />
|
||||
<option name="SHOW_OPTIMIZED_WARNING" value="true" />
|
||||
<option name="ATTACH_ON_WAIT_FOR_DEBUGGER" value="false" />
|
||||
<option name="DEBUG_SANDBOX_SDK" value="false" />
|
||||
</Hybrid>
|
||||
<Java>
|
||||
<option name="ATTACH_ON_WAIT_FOR_DEBUGGER" value="false" />
|
||||
<option name="DEBUG_SANDBOX_SDK" value="false" />
|
||||
</Java>
|
||||
<Native>
|
||||
<option name="USE_JAVA_AWARE_DEBUGGER" value="false" />
|
||||
<option name="SHOW_STATIC_VARS" value="true" />
|
||||
<option name="WORKING_DIR" value="" />
|
||||
<option name="TARGET_LOGGING_CHANNELS" value="lldb process:gdb-remote packets" />
|
||||
<option name="SHOW_OPTIMIZED_WARNING" value="true" />
|
||||
<option name="ATTACH_ON_WAIT_FOR_DEBUGGER" value="false" />
|
||||
<option name="DEBUG_SANDBOX_SDK" value="false" />
|
||||
</Native>
|
||||
<Profilers>
|
||||
<option name="ADVANCED_PROFILING_ENABLED" value="false" />
|
||||
<option name="STARTUP_PROFILING_ENABLED" value="false" />
|
||||
<option name="STARTUP_CPU_PROFILING_ENABLED" value="false" />
|
||||
<option name="STARTUP_CPU_PROFILING_CONFIGURATION_NAME" value="Java/Kotlin Method Sample (legacy)" />
|
||||
<option name="STARTUP_NATIVE_MEMORY_PROFILING_ENABLED" value="false" />
|
||||
<option name="NATIVE_MEMORY_SAMPLE_RATE_BYTES" value="2048" />
|
||||
</Profilers>
|
||||
<option name="DEEP_LINK" value="" />
|
||||
<option name="ACTIVITY" value="" />
|
||||
<option name="ACTIVITY_CLASS" value="" />
|
||||
<option name="SEARCH_ACTIVITY_IN_GLOBAL_SCOPE" value="false" />
|
||||
<option name="SKIP_ACTIVITY_VALIDATION" value="false" />
|
||||
<method v="2">
|
||||
<option name="Android.Gradle.BeforeRunTask" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
|
||||
<component name="TaskManager">
|
||||
<task active="true" id="Default" summary="Default task">
|
||||
<changelist id="e1c82fb5-3f9e-43af-a0be-9f1954a6c1b9" name="Changes" comment="" />
|
||||
<created>1741017174616</created>
|
||||
<option name="number" value="Default" />
|
||||
<option name="presentableId" value="Default" />
|
||||
<updated>1741017174616</updated>
|
||||
</task>
|
||||
<servers />
|
||||
</component>
|
||||
<component name="play_dynamic_filters_status">
|
||||
<option name="appIdToCheckInfo">
|
||||
<map>
|
||||
<entry key="com.birdsounds.identify">
|
||||
<value>
|
||||
<CheckInfo lastCheckTimestamp="1741381679027" />
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="com.birdsounds.identify.test">
|
||||
<value>
|
||||
<CheckInfo lastCheckTimestamp="1741381679027" />
|
||||
</value>
|
||||
</entry>
|
||||
</map>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
@@ -1,11 +1,23 @@
|
||||
[versions]
|
||||
accompanistFlowlayout = "0.28.0"
|
||||
agp = "8.8.1"
|
||||
composeNavigationVersion = "1.3.1"
|
||||
horologistComposeTools = "0.6.18"
|
||||
horologistAudio = "0.6.18"
|
||||
horologistMediaData = "0.6.8"
|
||||
horologistMediaUi = "0.6.8"
|
||||
kotlin = "2.0.0"
|
||||
coreKtx = "1.13.1"
|
||||
junit = "4.13.2"
|
||||
junitVersion = "1.1.5"
|
||||
espressoCore = "3.5.1"
|
||||
appcompat = "1.6.1"
|
||||
kotlinxCoroutinesPlayServices = "1.8.1"
|
||||
lifecycleViewmodelCompose = "2.8.4"
|
||||
materialIconsCore = "1.6.8"
|
||||
materialIconsExtended = "1.7.8"
|
||||
media3Exoplayer = "1.4.0"
|
||||
navigationCompose = "2.8.0-rc01"
|
||||
playServicesWearable = "18.2.0"
|
||||
material = "1.10.0"
|
||||
activity = "1.9.1"
|
||||
@@ -13,6 +25,9 @@ constraintlayout = "2.1.4"
|
||||
composeBom = "2024.04.01"
|
||||
composeMaterial = "1.2.1"
|
||||
composeFoundation = "1.2.1"
|
||||
statelyConcurrentCollections = "2.0.0"
|
||||
uiTooling = "1.3.1"
|
||||
wearOngoing = "1.0.0"
|
||||
wearToolingPreview = "1.0.0"
|
||||
activityCompose = "1.9.1"
|
||||
coreSplashscreen = "1.0.1"
|
||||
@@ -22,14 +37,32 @@ composeMaterial3 = "1.0.0-alpha23"
|
||||
workRuntimeKtx = "2.9.1"
|
||||
lifecycleRuntimeKtx = "2.6.1"
|
||||
runtimeAndroid = "1.6.6"
|
||||
datastoreCoreAndroid = "1.1.3"
|
||||
coreKtxVersion = "1.13.0"
|
||||
animationCoreAndroid = "1.6.6"
|
||||
#litert = "1.0.1"
|
||||
|
||||
[libraries]
|
||||
accompanist-flowlayout = { module = "com.google.accompanist:accompanist-flowlayout", version.ref = "accompanistFlowlayout" }
|
||||
androidx-compose-navigation-v131 = { module = "androidx.wear.compose:compose-navigation", version.ref = "composeNavigationVersion" }
|
||||
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
|
||||
androidx-lifecycle-viewmodel-compose = { module = "androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "lifecycleViewmodelCompose" }
|
||||
androidx-material-icons-core = { module = "androidx.compose.material:material-icons-core", version.ref = "materialIconsCore" }
|
||||
androidx-material-icons-extended = { module = "androidx.compose.material:material-icons-extended", version.ref = "materialIconsExtended" }
|
||||
androidx-media3-exoplayer = { module = "androidx.media3:media3-exoplayer", version.ref = "media3Exoplayer" }
|
||||
androidx-navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "navigationCompose" }
|
||||
androidx-wear-ongoing = { module = "androidx.wear:wear-ongoing", version.ref = "wearOngoing" }
|
||||
horologist-audio-ui = { module = "com.google.android.horologist:horologist-audio-ui", version.ref = "horologistComposeTools" }
|
||||
horologist-audio = { module = "com.google.android.horologist:horologist-audio", version.ref = "horologistAudio" }
|
||||
horologist-compose-material = { module = "com.google.android.horologist:horologist-compose-material", version.ref = "horologistMediaData" }
|
||||
horologist-compose-tools = { module = "com.google.android.horologist:horologist-compose-tools", version.ref = "horologistComposeTools" }
|
||||
horologist-media-ui = { module = "com.google.android.horologist:horologist-media-ui", version.ref = "horologistMediaUi" }
|
||||
horologist-media-data = { module = "com.google.android.horologist:horologist-media-data", version.ref = "horologistMediaData" }
|
||||
junit = { group = "junit", name = "junit", version.ref = "junit" }
|
||||
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
|
||||
androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
|
||||
androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
|
||||
kotlinx-coroutines-play-services = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-play-services", version.ref = "kotlinxCoroutinesPlayServices" }
|
||||
play-services-wearable = { group = "com.google.android.gms", name = "play-services-wearable", version.ref = "playServicesWearable" }
|
||||
material = { group = "com.google.android.material", name = "material", version.ref = "material" }
|
||||
androidx-activity = { group = "androidx.activity", name = "activity", version.ref = "activity" }
|
||||
@@ -53,10 +86,15 @@ androidx-work-runtime-ktx = { group = "androidx.work", name = "work-runtime-ktx"
|
||||
androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" }
|
||||
androidx-material3 = { group = "androidx.compose.material3", name = "material3" }
|
||||
androidx-runtime-android = { group = "androidx.compose.runtime", name = "runtime-android", version.ref = "runtimeAndroid" }
|
||||
androidx-datastore-core-android = { group = "androidx.datastore", name = "datastore-core-android", version.ref = "datastoreCoreAndroid" }
|
||||
core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtxVersion" }
|
||||
stately-concurrent-collections = { module = "co.touchlab:stately-concurrent-collections", version.ref = "statelyConcurrentCollections" }
|
||||
ui-tooling = { module = "androidx.compose.ui:ui-tooling", version.ref = "uiTooling" }
|
||||
androidx-animation-core-android = { group = "androidx.compose.animation", name = "animation-core-android", version.ref = "animationCoreAndroid" }
|
||||
#litert = { group = "com.google.ai.edge.litert", name = "litert", version.ref = "litert" }
|
||||
|
||||
[plugins]
|
||||
android-application = { id = "com.android.application", version.ref = "agp" }
|
||||
kotlin-android = { id = "org.jetbrains.kotlin.android", version = "2.0.20" }
|
||||
kotlin-android = { id = "org.jetbrains.kotlin.android", version = "2.1.10" }
|
||||
kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
|
||||
|
||||
|
||||
@@ -55,12 +55,16 @@ dependencies {
|
||||
implementation(libs.androidx.activity.compose)
|
||||
implementation(platform(libs.androidx.compose.bom))
|
||||
implementation(libs.androidx.ui)
|
||||
|
||||
implementation("androidx.datastore:datastore-preferences:1.1.3")
|
||||
implementation("org.tensorflow:tensorflow-lite:2.6.0")
|
||||
// implementation("com.google.android.gms:play-services-tflite:20.0.0")
|
||||
implementation("uk.me.berndporr:iirj:1.7")
|
||||
implementation(libs.androidx.ui.graphics)
|
||||
implementation(libs.androidx.ui.tooling.preview)
|
||||
implementation(libs.androidx.material3)
|
||||
implementation(libs.androidx.datastore.core.android)
|
||||
implementation(libs.core.ktx)
|
||||
// implementation(libs.litert)
|
||||
testImplementation(libs.junit)
|
||||
androidTestImplementation(libs.androidx.junit)
|
||||
|
||||
@@ -6,7 +6,7 @@ import java.io.IOException
|
||||
|
||||
class Downloader(mainActivity: MainActivity) {
|
||||
|
||||
private val settings = Settings();
|
||||
private val settings = Settings;
|
||||
private var activity: MainActivity = mainActivity;
|
||||
private var context: Context = activity.applicationContext;
|
||||
|
||||
|
||||
@@ -1,15 +1,24 @@
|
||||
@file:Suppress("DEPRECATION")
|
||||
|
||||
package com.birdsounds.identify
|
||||
|
||||
import android.Manifest
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.pm.PackageManager
|
||||
import android.location.Address
|
||||
import android.location.Geocoder
|
||||
import android.location.Location
|
||||
import android.location.LocationListener
|
||||
import android.location.LocationManager
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.core.app.ActivityCompat
|
||||
import java.util.Locale
|
||||
|
||||
|
||||
object Location {
|
||||
private var locationListenerGPS: LocationListener? = null
|
||||
@@ -30,7 +39,10 @@ object Location {
|
||||
) {
|
||||
val locationManager =
|
||||
context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
|
||||
|
||||
|
||||
if (locationListenerGPS == null) locationListenerGPS = object : LocationListener {
|
||||
@SuppressLint("SetTextI18n")
|
||||
override fun onLocationChanged(location: Location) {
|
||||
Log.w(TAG, "Got location changed");
|
||||
while (!soundClassifier.is_model_ready()) {
|
||||
@@ -39,7 +51,35 @@ object Location {
|
||||
Log.w(TAG, "Sound classifier is ready");
|
||||
soundClassifier.runMetaInterpreter(location)
|
||||
|
||||
soundClassifier.runMetaInterpreter(location)
|
||||
val activity = context as? Activity;
|
||||
activity?.let {
|
||||
|
||||
val text_species: TextView = it.findViewById(R.id.local_species)
|
||||
text_species.text = local_species;
|
||||
|
||||
val loc_lon: TextView = it.findViewById(R.id.location_long)
|
||||
loc_lon.text =
|
||||
"Longitude: ${location.longitude}"
|
||||
|
||||
val loc_lat: TextView = it.findViewById(R.id.location_lat)
|
||||
loc_lat.text =
|
||||
"Latitude: ${location.latitude}"
|
||||
|
||||
val loc_string: TextView = it.findViewById(R.id.location_string);
|
||||
val geocoder = Geocoder(context, Locale.getDefault())
|
||||
val addresses: MutableList<Address>? =
|
||||
geocoder.getFromLocation(location.latitude, location.longitude, 1)
|
||||
if (addresses?.isNotEmpty() == true) {
|
||||
val address: Address = addresses[0]
|
||||
|
||||
// loc_string.text = address.locality.toString() + ", " + address.adminArea.toString() + " " + address.countryName.toString()
|
||||
loc_string.text = address.getAddressLine(0).toString()
|
||||
// Log.w(TAG, address.toString())
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Deprecated("")
|
||||
@@ -53,7 +93,7 @@ object Location {
|
||||
}
|
||||
}
|
||||
locationManager.requestLocationUpdates(
|
||||
LocationManager.GPS_PROVIDER, 60000, 0f,
|
||||
LocationManager.PASSIVE_PROVIDER, 60000, 0f,
|
||||
locationListenerGPS!!
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,16 +1,42 @@
|
||||
package com.birdsounds.identify
|
||||
|
||||
import android.Manifest
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Bundle
|
||||
import android.Manifest
|
||||
import android.preference.PreferenceManager
|
||||
import android.util.Log
|
||||
import android.widget.SeekBar
|
||||
import android.widget.TextView
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.datastore.core.DataStore
|
||||
import androidx.datastore.preferences.core.Preferences
|
||||
import androidx.datastore.preferences.core.edit
|
||||
import androidx.datastore.preferences.core.intPreferencesKey
|
||||
import androidx.datastore.preferences.preferencesDataStore
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.google.android.gms.wearable.ChannelClient
|
||||
import com.google.android.gms.wearable.Wearable
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import java.time.Instant
|
||||
|
||||
private var updateJob: Job? = null
|
||||
private var updateCounter = 0
|
||||
val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "settings")
|
||||
|
||||
|
||||
|
||||
val Any.TAG: String
|
||||
get() {
|
||||
@@ -19,11 +45,39 @@ val Any.TAG: String
|
||||
}
|
||||
|
||||
|
||||
class SynchronousDataStore(private val context: Context) {
|
||||
|
||||
// Define keys
|
||||
companion object {
|
||||
val THRESHOLD_KEY = intPreferencesKey("user_age")
|
||||
}
|
||||
|
||||
// Synchronous write operations
|
||||
fun saveThreshold(value: Int) {
|
||||
runBlocking {
|
||||
context.dataStore.edit { preferences ->
|
||||
preferences[THRESHOLD_KEY] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getThreshold(): Int {
|
||||
|
||||
return runBlocking {
|
||||
context.dataStore.data.first()[THRESHOLD_KEY] ?: 50
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
class MainActivity : AppCompatActivity() {
|
||||
// private lateinit var soundClassifier: SoundClassifier
|
||||
val REQUEST_PERMISSIONS = 1337
|
||||
|
||||
private lateinit var dataStore: SynchronousDataStore
|
||||
|
||||
private lateinit var last_message_delay: TextView
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
enableEdgeToEdge()
|
||||
@@ -49,6 +103,34 @@ class MainActivity : AppCompatActivity() {
|
||||
insets
|
||||
|
||||
}
|
||||
|
||||
val thresholdText = findViewById<TextView>(R.id.threshold_value_text)
|
||||
val seekBar = findViewById<SeekBar>(R.id.threshold_set_scale_bar)
|
||||
last_message_delay = findViewById<TextView>(R.id.last_message_delay)
|
||||
dataStore = SynchronousDataStore(applicationContext)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Settings.threshold = dataStore.getThreshold();
|
||||
seekBar.progress = Settings.threshold
|
||||
thresholdText.text = String.format("%.2f", Settings.threshold/100f)
|
||||
seekBar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
|
||||
@SuppressLint("DefaultLocale")
|
||||
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
|
||||
|
||||
var actualValue = progress
|
||||
dataStore.saveThreshold(actualValue);
|
||||
Settings.threshold = actualValue;
|
||||
thresholdText.text = String.format("%.2f", actualValue/100f)
|
||||
}
|
||||
|
||||
override fun onStartTrackingTouch(seekBar: SeekBar) {}
|
||||
|
||||
override fun onStopTrackingTouch(seekBar: SeekBar) {}
|
||||
})
|
||||
|
||||
}
|
||||
companion object {
|
||||
var soundClassifier: SoundClassifier? = null
|
||||
@@ -57,6 +139,34 @@ class MainActivity : AppCompatActivity() {
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
// Start periodic updates using coroutines
|
||||
updateJob = lifecycleScope.launch {
|
||||
while (isActive) { // isActive is a property of the coroutine scope
|
||||
updateCounter++
|
||||
if (last_message_time == 0.toLong())
|
||||
{
|
||||
last_message_delay.text = "No messages received"
|
||||
} else
|
||||
{
|
||||
last_message_delay.text = "Last message: ${(Instant.now().toEpochMilli() - last_message_time)/1000F.toInt()} seconds ago"
|
||||
}
|
||||
|
||||
// last_message_delay.text = "Update count: $updateCounter"
|
||||
delay(500) // Update every 1 second
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
// Cancel the coroutine when activity is not visible
|
||||
updateJob?.cancel()
|
||||
}
|
||||
|
||||
private fun requestPermissions() {
|
||||
val perms = mutableListOf<String>()
|
||||
|
||||
|
||||
@@ -10,6 +10,19 @@ import decodeAACToPCM
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.nio.ByteBuffer
|
||||
import java.nio.ByteOrder
|
||||
import java.time.Instant
|
||||
|
||||
|
||||
var last_message_time = 0L;
|
||||
fun ByteArray.toLong(): Long {
|
||||
require(size <= 8) { "ByteArray too large to fit in Long" }
|
||||
|
||||
var result = 0L
|
||||
for (byte in this) {
|
||||
result = (result shl 8) or (byte.toLong() and 0xFF)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
class MessageListenerService : WearableListenerService() {
|
||||
|
||||
@@ -30,6 +43,10 @@ class MessageListenerService : WearableListenerService() {
|
||||
}
|
||||
|
||||
var tstamp_bytes = p0.data.copyOfRange(0, Long.SIZE_BYTES)
|
||||
|
||||
last_message_time = tstamp_bytes.toLong()
|
||||
|
||||
|
||||
var audio_bytes_og = p0.data.copyOfRange(Long.SIZE_BYTES, p0.data.size)
|
||||
|
||||
|
||||
@@ -63,8 +80,12 @@ class MessageListenerService : WearableListenerService() {
|
||||
var sorted_list = soundclassifier.executeScoring(short_array)
|
||||
Log.w(TAG, "FINISHED SCORING");
|
||||
Log.w(TAG, "")
|
||||
for (i in 0 until 5) {
|
||||
val threshold = Settings.threshold/100f
|
||||
for (i in 0 until 10) {
|
||||
val score = sorted_list[i].value
|
||||
if (score < threshold) {
|
||||
continue
|
||||
}
|
||||
val index = sorted_list[i].index
|
||||
val species_name = soundclassifier.labelList[index]
|
||||
Log.w(TAG, species_name + ", " + score.toString())
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
package com.birdsounds.identify
|
||||
|
||||
class Settings {
|
||||
object Settings {
|
||||
var local_model_file: String = "2024_08_16_audio_model.tflite"
|
||||
var pkg_model_file: String = "2024_08_16/audio-model.tflite"
|
||||
|
||||
var local_meta_model_file: String = "2024_08_16_meta_model.tflite"
|
||||
var pkg_meta_model_file: String = "2024_08_16/meta-model.tflite"
|
||||
|
||||
var threshold: Int = 50;
|
||||
}
|
||||
|
||||
@@ -26,6 +26,8 @@ import uk.me.berndporr.iirj.Butterworth
|
||||
import kotlin.math.sin
|
||||
|
||||
|
||||
|
||||
var local_species = ""
|
||||
class SoundClassifier(
|
||||
context: Context,
|
||||
private val options: Options = Options()
|
||||
@@ -81,7 +83,7 @@ class SoundClassifier(
|
||||
/** Number of output classes of the TFLite model. */
|
||||
private var modelNumClasses = 0
|
||||
private var metaModelNumClasses = 0
|
||||
private var settings: Settings = Settings();
|
||||
private var settings = Settings;
|
||||
|
||||
/** Used to hold the real-time probabilities predicted by the model for the output classes. */
|
||||
private lateinit var predictionProbs: FloatArray
|
||||
@@ -274,6 +276,17 @@ class SoundClassifier(
|
||||
metaOutputBuffer.rewind()
|
||||
metaOutputBuffer.get(metaPredictionProbs) // Copy data to metaPredictionProbs.
|
||||
|
||||
var cloned = metaPredictionProbs.clone()
|
||||
|
||||
val sorted_indices = cloned.withIndex().sortedByDescending { it.value }.map{it.index}
|
||||
|
||||
local_species = "Most likely:\n"
|
||||
for (i in 0..5) {
|
||||
local_species += " " +labelList[sorted_indices[i]].split("_")[1]
|
||||
local_species += "\n";
|
||||
}
|
||||
|
||||
|
||||
|
||||
for (i in metaPredictionProbs.indices) {
|
||||
metaPredictionProbs[i] =
|
||||
|
||||
@@ -7,13 +7,89 @@
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".MainActivity">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:id="@+id/textView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Hello World!"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
android:paddingLeft="5dp"
|
||||
android:paddingTop="15dp"
|
||||
android:text="Threshold:"
|
||||
android:textSize="34sp" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="55dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/threshold_value_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="4"
|
||||
android:gravity="center"
|
||||
android:text="Threshold:"
|
||||
android:textAlignment="center"
|
||||
android:textSize="28sp" />
|
||||
|
||||
<SeekBar
|
||||
android:id="@+id/threshold_set_scale_bar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="center|fill_horizontal|fill_vertical"
|
||||
android:layout_weight="1"
|
||||
android:max="100"
|
||||
android:min="0" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/location_string"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingLeft="5dp"
|
||||
android:paddingTop="15dp"
|
||||
android:paddingRight="5dp"
|
||||
android:text="Location:"
|
||||
android:textSize="34sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/location_lat"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingLeft="5dp"
|
||||
android:paddingTop="15dp"
|
||||
android:paddingRight="5dp"
|
||||
android:text="Latitude:"
|
||||
android:textSize="20sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/location_long"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingLeft="5dp"
|
||||
android:paddingRight="5dp"
|
||||
android:text="Longitude:"
|
||||
android:textSize="20sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/local_species"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingLeft="5dp"
|
||||
android:text="Local Species:"
|
||||
android:textSize="20sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/last_message_delay"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Last Message" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -44,21 +44,20 @@ dependencies {
|
||||
include("*.jar")
|
||||
})
|
||||
api(files("libs/opus.aar"))
|
||||
implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.8.4")
|
||||
implementation("androidx.compose.ui:ui-tooling:1.3.1")
|
||||
implementation("androidx.navigation:navigation-compose:2.8.0-rc01")
|
||||
implementation("androidx.wear.compose:compose-navigation:1.3.1")
|
||||
implementation("com.google.android.horologist:horologist-audio-ui: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("androidx.compose.material:material-icons-core: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-media-ui: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("androidx.media3:media3-exoplayer:1.4.0")
|
||||
implementation(libs.androidx.lifecycle.viewmodel.compose)
|
||||
implementation(libs.ui.tooling)
|
||||
implementation(libs.androidx.navigation.compose)
|
||||
implementation(libs.androidx.compose.navigation.v131)
|
||||
implementation(libs.horologist.audio.ui)
|
||||
implementation(libs.horologist.audio)
|
||||
implementation(libs.horologist.compose.tools)
|
||||
implementation(libs.androidx.material.icons.core)
|
||||
implementation(libs.androidx.material.icons.extended)
|
||||
implementation(libs.horologist.compose.material)
|
||||
implementation(libs.horologist.media.ui)
|
||||
implementation(libs.horologist.media.data)
|
||||
implementation(libs.kotlinx.coroutines.play.services)
|
||||
implementation(libs.androidx.media3.exoplayer)
|
||||
implementation(libs.play.services.wearable)
|
||||
implementation(platform(libs.androidx.compose.bom))
|
||||
implementation(libs.androidx.ui)
|
||||
@@ -66,15 +65,20 @@ dependencies {
|
||||
implementation(libs.androidx.ui.tooling.preview)
|
||||
implementation(libs.androidx.compose.material)
|
||||
implementation(libs.androidx.compose.foundation)
|
||||
// implementation("androidx.compose.foundation:foundation:1.8.0-rc01")
|
||||
implementation(libs.androidx.wear.tooling.preview)
|
||||
implementation(libs.androidx.activity.compose)
|
||||
implementation(libs.androidx.core.splashscreen)
|
||||
implementation(libs.androidx.compose.navigation)
|
||||
implementation(libs.androidx.media3.common)
|
||||
implementation("co.touchlab:stately-concurrent-collections:2.0.0")
|
||||
implementation(libs.androidx.wear.ongoing)
|
||||
implementation(libs.accompanist.flowlayout)
|
||||
implementation(libs.stately.concurrent.collections)
|
||||
implementation(libs.androidx.compose.material3)
|
||||
implementation(libs.androidx.work.runtime.ktx)
|
||||
implementation(libs.androidx.runtime.android) // androidTestImplementation(platform(libs.androidx.compose.bom))
|
||||
implementation(libs.androidx.runtime.android)
|
||||
implementation(libs.androidx.animation.core.android)
|
||||
// androidTestImplementation(platform(libs.androidx.compose.bom))
|
||||
// androidTestImplementation(libs.androidx.ui.test.junit4)
|
||||
// debugImplementation(libs.androidx.ui.tooling)
|
||||
// debugImplementation(libs.androidx.ui.test.manifest)
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||||
<uses-feature android:name="android.hardware.type.watch" />
|
||||
|
||||
@@ -26,6 +28,8 @@
|
||||
<activity
|
||||
android:name=".presentation.MainActivity"
|
||||
android:exported="true"
|
||||
android:alwaysRetainTaskState="true"
|
||||
android:immersive="true"
|
||||
android:taskAffinity=""
|
||||
android:theme="@style/MainActivityTheme.Starting">
|
||||
<intent-filter>
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
package com.birdsounds.identify.presentation
|
||||
|
||||
import androidx.compose.animation.animateColorAsState
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.BoxWithConstraints
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontStyle
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.rememberTextMeasurer
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextDecoration
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.TextUnit
|
||||
import androidx.compose.ui.unit.TextUnitType
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.wear.compose.material.Text
|
||||
import kotlinx.coroutines.delay
|
||||
|
||||
|
||||
@Composable
|
||||
fun FlashingText(
|
||||
text: String,
|
||||
color: Color,
|
||||
textDecor: TextDecoration = TextDecoration.None,
|
||||
modifier: Modifier = Modifier,
|
||||
key: Long,
|
||||
fontStyle: FontStyle = FontStyle.Normal
|
||||
) {
|
||||
// Set up the flashing state
|
||||
var isFlashing by remember { mutableStateOf(false) }
|
||||
|
||||
// Create animation for the background color
|
||||
val backgroundColor by animateColorAsState(
|
||||
targetValue = if (isFlashing) Color.DarkGray else Color.Transparent,
|
||||
animationSpec = tween(durationMillis = 1000)
|
||||
)
|
||||
|
||||
// Trigger the flash effect once
|
||||
LaunchedEffect(key1 = key) {
|
||||
isFlashing = true
|
||||
delay(1500)
|
||||
isFlashing = false
|
||||
}
|
||||
|
||||
// Display the text with animated background
|
||||
AutoSizedText(
|
||||
text = text,
|
||||
color = color,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = modifier
|
||||
.background(color = backgroundColor),
|
||||
textDecor = textDecor,
|
||||
fontStyle = fontStyle
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun AutoSizedText(
|
||||
text: String,
|
||||
color: Color = Color.White,
|
||||
textAlign: TextAlign = TextAlign.Center,
|
||||
modifier: Modifier = Modifier,
|
||||
textDecor: TextDecoration = TextDecoration.None,
|
||||
minFontSize: TextUnit = 5.sp,
|
||||
targetFontSize: TextUnit = 24.sp,
|
||||
fontWeight: FontWeight = FontWeight.Normal,
|
||||
fontStyle: FontStyle = FontStyle.Normal,
|
||||
) {
|
||||
var display_text = text;
|
||||
val textMeasurer = rememberTextMeasurer()
|
||||
BoxWithConstraints(modifier) {
|
||||
val density = LocalDensity.current
|
||||
// val minFontSizePx = with(density) { minFontSize.toPx() }
|
||||
// val maxFontSizePx = with(density) { targetFontSize.toPx() }
|
||||
val availableWidthPx = with(density) { maxWidth.toPx() }
|
||||
|
||||
// Binary search to find the appropriate font size that fits the available width
|
||||
var lowPx: TextUnit = minFontSize
|
||||
var highPx: TextUnit = targetFontSize
|
||||
var bestFontSizePx = targetFontSize;
|
||||
val textStyle = TextStyle(fontWeight = fontWeight, color = color, fontSize=bestFontSizePx)
|
||||
while (lowPx <= highPx) {
|
||||
val midPx: TextUnit = TextUnit(value=lowPx.value/2 + highPx.value/2 , type= TextUnitType.Sp)
|
||||
val style = textStyle.copy(fontSize = midPx)
|
||||
|
||||
val textLayoutResult = textMeasurer.measure(
|
||||
text = display_text,
|
||||
style = style,
|
||||
maxLines = 1,
|
||||
softWrap = false,
|
||||
density = density,
|
||||
)
|
||||
|
||||
if (textLayoutResult.size.width <= availableWidthPx) {
|
||||
// // This font size fits, try a larger oneb
|
||||
bestFontSizePx = midPx;
|
||||
break;
|
||||
} else {
|
||||
// // This font size is too large, try a smaller one
|
||||
// highPx = (midPx - 1).toFloat()
|
||||
highPx = TextUnit(value=midPx.value - 1, type=TextUnitType.Sp)
|
||||
}
|
||||
}
|
||||
|
||||
// Convert the best font size back to sp and use it
|
||||
// val bestFontSize = with(density) { bestFontSizePx.toSp() }
|
||||
|
||||
|
||||
Text(
|
||||
text = display_text,
|
||||
color = color,
|
||||
fontSize = bestFontSizePx,
|
||||
fontStyle = fontStyle,
|
||||
fontWeight = fontWeight,
|
||||
maxLines = 1,
|
||||
textDecoration = textDecor,
|
||||
overflow = TextOverflow.Clip,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
textAlign = textAlign
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
package com.birdsounds.identify.presentation
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.navigation.NavHostController
|
||||
import androidx.wear.compose.material.CompactChip
|
||||
import androidx.wear.compose.material.MaterialTheme
|
||||
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) {
|
||||
"Stop"
|
||||
} else {
|
||||
"Start"
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Composable
|
||||
private fun ControlDashboardButton(buttonState: ControlDashboardButtonUiState,
|
||||
onClick: () -> Unit,
|
||||
labelText: String,
|
||||
modifier: Modifier = Modifier) {
|
||||
CompactChip(modifier = Modifier,
|
||||
onClick = onClick,
|
||||
enabled = true,
|
||||
contentPadding = PaddingValues(5.dp, 1.dp, 5.dp, 1.dp),
|
||||
shape = MaterialTheme.shapes.small,
|
||||
label = {
|
||||
Text(text = labelText)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// Button(modifier = modifier, enabled = buttonState.enabled && buttonState.visible, onClick = onClick) {
|
||||
// Text(contentDescription);
|
||||
//// Icon(imageVector = imageVector, contentDescription = contentDescription)
|
||||
// }
|
||||
//}
|
||||
|
||||
|
||||
data class ControlDashboardButtonUiState(val expanded: Boolean, val enabled: Boolean, val visible: Boolean)
|
||||
|
||||
|
||||
data class ControlDashboardUiState(val micState: ControlDashboardButtonUiState) {
|
||||
init { // Check that at most one of the buttons is expanded
|
||||
|
||||
}
|
||||
}
|
||||
@@ -6,56 +6,62 @@
|
||||
package com.birdsounds.identify.presentation
|
||||
|
||||
import android.Manifest
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.app.Notification
|
||||
import android.app.NotificationManager
|
||||
import android.app.PendingIntent
|
||||
import android.app.Service
|
||||
import android.content.Context
|
||||
import android.content.ContextWrapper
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.ManagedActivityResultLauncher
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.result.contract.ActivityResultContracts.RequestPermission
|
||||
import androidx.compose.animation.core.animateFloatAsState
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.interaction.collectIsPressedAsState
|
||||
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
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.scale
|
||||
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.app.NotificationCompat
|
||||
import androidx.core.app.ServiceCompat.startForeground
|
||||
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import androidx.navigation.NavHostController
|
||||
import androidx.wear.ambient.AmbientLifecycleObserver
|
||||
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
|
||||
import androidx.wear.compose.navigation.rememberSwipeDismissableNavController
|
||||
import androidx.wear.compose.ui.tooling.preview.WearPreviewDevices
|
||||
import androidx.wear.compose.ui.tooling.preview.WearPreviewFontScales
|
||||
import androidx.wear.ongoing.OngoingActivity
|
||||
import com.birdsounds.identify.R
|
||||
import com.birdsounds.identify.presentation.theme.IdentifyTheme
|
||||
import com.google.android.horologist.annotations.ExperimentalHorologistApi
|
||||
import com.google.android.horologist.audio.ui.VolumeViewModel
|
||||
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
|
||||
import com.google.android.horologist.compose.material.Chip
|
||||
import com.google.android.horologist.compose.material.ListHeaderDefaults.firstItemPadding
|
||||
import com.google.android.horologist.compose.material.ResponsiveListHeader
|
||||
import com.google.android.horologist.compose.rotaryinput.rotaryWithScroll
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
|
||||
val flow_stream = MutableStateFlow<String>("")
|
||||
|
||||
val Any.TAG: String
|
||||
@@ -64,26 +70,30 @@ val Any.TAG: String
|
||||
return if (tag.length <= 23) tag else tag.substring(0, 23)
|
||||
}
|
||||
|
||||
|
||||
class MainActivity : ComponentActivity() {
|
||||
|
||||
|
||||
lateinit var ambientObserver:AmbientLifecycleObserver
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
installSplashScreen()
|
||||
|
||||
ambientObserver= AmbientLifecycleObserver(this.findActivity(), ambientCallback)
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
lifecycle.addObserver(ambientObserver)
|
||||
setTheme(android.R.style.Theme_DeviceDefault)
|
||||
setContent {
|
||||
WearApp()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("ForegroundServiceType")
|
||||
@OptIn(ExperimentalHorologistApi::class)
|
||||
@Composable
|
||||
@Preview
|
||||
fun WearApp() {
|
||||
|
||||
IdentifyTheme {
|
||||
lateinit var requestPermissionLauncher: ManagedActivityResultLauncher<String, Boolean>
|
||||
val context = LocalContext.current
|
||||
@@ -91,6 +101,48 @@ fun WearApp() {
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
|
||||
val touchIntent =
|
||||
PendingIntent.getActivity(
|
||||
context,
|
||||
0,
|
||||
Intent(context, MainActivity::class.java),
|
||||
PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
|
||||
)
|
||||
|
||||
|
||||
val notificationBuilder =
|
||||
NotificationCompat.Builder(context, "Identify Watch App")
|
||||
.setOngoing(true)
|
||||
.setSmallIcon(com.birdsounds.identify.R.mipmap.ic_launcher)
|
||||
|
||||
|
||||
val ongoingActivity =
|
||||
OngoingActivity.Builder(
|
||||
context, 4, notificationBuilder
|
||||
).setTouchIntent(touchIntent)
|
||||
.setStaticIcon(
|
||||
com.birdsounds.identify.R.mipmap.ic_launcher)
|
||||
.build()
|
||||
|
||||
|
||||
ongoingActivity.apply(context)
|
||||
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||
notificationManager.notify(
|
||||
4,
|
||||
notificationBuilder.build()
|
||||
)
|
||||
|
||||
val notification: Notification = NotificationCompat.Builder(
|
||||
context, "Identify Watch App"
|
||||
)
|
||||
.setContentTitle("Always On App")
|
||||
.setContentText("Running in foreground")
|
||||
.setSmallIcon(com.birdsounds.identify.R.mipmap.ic_launcher)
|
||||
.setPriority(NotificationCompat.PRIORITY_LOW)
|
||||
.build()
|
||||
|
||||
|
||||
|
||||
val volumeViewModel: VolumeViewModel = viewModel(factory = VolumeViewModel.Factory)
|
||||
|
||||
val mainState = remember(activity) {
|
||||
@@ -99,7 +151,6 @@ fun WearApp() {
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
requestPermissionLauncher = rememberLauncherForActivityResult(RequestPermission()) {
|
||||
scope.launch {
|
||||
mainState.permissionResultReturned()
|
||||
@@ -108,41 +159,110 @@ fun WearApp() {
|
||||
|
||||
val navController: NavHostController = rememberSwipeDismissableNavController()
|
||||
mainState.setNavController(navController);
|
||||
SwipeDismissableNavHost(navController = navController, userSwipeEnabled = true, startDestination = "speaker") {
|
||||
|
||||
SwipeDismissableNavHost(
|
||||
navController = navController,
|
||||
userSwipeEnabled = true,
|
||||
startDestination = "speaker"
|
||||
) {
|
||||
composable("species_list") {
|
||||
ScreenScaffold {
|
||||
SpeciesListView(context = context)
|
||||
SpeciesListView(context = context, appState = mainState.appState)
|
||||
}
|
||||
}
|
||||
|
||||
composable("speaker") {
|
||||
ScreenScaffold {
|
||||
CompactChip(modifier = Modifier,
|
||||
onClick = { navController.navigate("species_list") },
|
||||
Column(
|
||||
verticalArrangement = Arrangement.Center,
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.align(Alignment.Center)
|
||||
) {
|
||||
Row() {
|
||||
val interactionSource = remember { MutableInteractionSource() }
|
||||
val isPressed by interactionSource.collectIsPressedAsState()
|
||||
|
||||
// Define the animation for the scale
|
||||
val scale by animateFloatAsState(
|
||||
targetValue = if (isPressed) 2.0f else 1.0f
|
||||
)
|
||||
|
||||
CompactChip(
|
||||
onClick = {
|
||||
vibrate(
|
||||
context,
|
||||
250
|
||||
);navController.navigate("species_list")
|
||||
},
|
||||
enabled = true,
|
||||
modifier = Modifier.scale(scale),
|
||||
interactionSource = interactionSource,
|
||||
contentPadding = PaddingValues(5.dp, 1.dp, 5.dp, 1.dp),
|
||||
shape = MaterialTheme.shapes.small,
|
||||
label = {
|
||||
Text(text = "View>")
|
||||
})
|
||||
Text(text = "Show List")
|
||||
},
|
||||
|
||||
StartRecordingScreen(
|
||||
appState = mainState.appState,
|
||||
onMicClicked = {
|
||||
)
|
||||
|
||||
|
||||
}
|
||||
Row() {
|
||||
val interactionSource = remember { MutableInteractionSource() }
|
||||
val isPressed by interactionSource.collectIsPressedAsState()
|
||||
|
||||
// Define the animation for the scale
|
||||
val scale by animateFloatAsState(
|
||||
targetValue = if (isPressed) 2.0f else 1.0f
|
||||
)
|
||||
CompactChip(
|
||||
onClick = {
|
||||
vibrate(context, 250);
|
||||
scope.launch {
|
||||
mainState.onMicClicked()
|
||||
}
|
||||
},
|
||||
onNavClicked = {
|
||||
navController.navigate("species_list")
|
||||
modifier = Modifier.scale(scale),
|
||||
interactionSource = interactionSource,
|
||||
enabled = true,
|
||||
contentPadding = PaddingValues(5.dp, 1.dp, 5.dp, 1.dp),
|
||||
shape = MaterialTheme.shapes.small,
|
||||
label = {
|
||||
Text(text = if (mainState.appState == AppState.Recording) "Stop Recording" else "Start Recording")
|
||||
})
|
||||
}
|
||||
Row() {
|
||||
val interactionSource = remember { MutableInteractionSource() }
|
||||
val isPressed by interactionSource.collectIsPressedAsState()
|
||||
|
||||
// Define the animation for the scale
|
||||
val scale by animateFloatAsState(
|
||||
targetValue = if (isPressed) 2.0f else 1.0f
|
||||
)
|
||||
CompactChip(
|
||||
onClick = {
|
||||
vibrate(context, 250);
|
||||
species_list_show.clear();
|
||||
internal_list.clear();
|
||||
},
|
||||
enabled = true,
|
||||
modifier = Modifier.scale(scale),
|
||||
interactionSource = interactionSource,
|
||||
contentPadding = PaddingValues(5.dp, 1.dp, 5.dp, 1.dp),
|
||||
shape = MaterialTheme.shapes.small,
|
||||
label = {
|
||||
Text(text = "Clear List")
|
||||
},
|
||||
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -31,6 +31,9 @@ class MainState(private val activity: Activity, private val requestPermission: (
|
||||
public fun setNavController(_navController: NavHostController) {
|
||||
navController = _navController
|
||||
}
|
||||
|
||||
|
||||
|
||||
suspend fun onMicClicked() {
|
||||
playbackStateMutatorMutex.mutate {
|
||||
when (appState) {
|
||||
|
||||
@@ -1,28 +1,58 @@
|
||||
package com.birdsounds.identify.presentation
|
||||
|
||||
import android.content.Context
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import com.google.android.gms.wearable.MessageEvent
|
||||
import com.google.android.gms.wearable.WearableListenerService
|
||||
import kotlinx.coroutines.delay
|
||||
import java.nio.ByteBuffer
|
||||
|
||||
var last_message_recv_tstamp: Long = 0L;
|
||||
|
||||
class MessageListenerService : WearableListenerService() {
|
||||
private val tag = "MessageListenerService"
|
||||
|
||||
lateinit var this_context: Context;
|
||||
fun set_context(context: Context)
|
||||
{
|
||||
this_context = context;
|
||||
}
|
||||
override fun onMessageReceived(p0: MessageEvent) {
|
||||
super.onMessageReceived(p0)
|
||||
val t_scored = ByteBuffer.wrap(p0.data).getLong()
|
||||
last_message_recv_tstamp = t_scored;
|
||||
|
||||
var byte_strings: ByteArray = p0.data.copyOfRange(8, p0.data.size)
|
||||
var score_species_string = byte_strings.decodeToString()
|
||||
var list_strings: List<String> = score_species_string.split(';')
|
||||
var do_vibrate = false;
|
||||
var did_add = false;
|
||||
var new_entries = 0;
|
||||
SpeciesList.clear_new_flags();
|
||||
list_strings.map({
|
||||
var split_str = it.split(',')
|
||||
if (split_str.size == 2) {
|
||||
var out = AScore(split_str[0], split_str[1].toFloat(), t_scored)
|
||||
if (out.score > 0.005) {
|
||||
SpeciesList.add_observation(out)
|
||||
new_entries+=1;
|
||||
var out = AScore(split_str[0], split_str[1].toFloat(), t_scored);
|
||||
out.new_entry = true;
|
||||
|
||||
if (SpeciesList.add_observation(out))
|
||||
{
|
||||
did_add = true;
|
||||
}
|
||||
do_vibrate = true;
|
||||
}
|
||||
})
|
||||
// SpeciesList.set_num_new_entries(new_entries);
|
||||
// SpeciesList.insert_new_entry_spacer(new_entries);
|
||||
if (did_add) {
|
||||
vibrateDouble(this, 100, 250, 100);
|
||||
} else if (do_vibrate) {
|
||||
vibrate(this,100)
|
||||
}
|
||||
// if (new_entries > 0) {
|
||||
SpeciesList.update_list_on_ui_thread();
|
||||
// }
|
||||
|
||||
MessageSender.messageLog.add(t_scored)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.birdsounds.identify.presentation
|
||||
|
||||
import androidx.wear.ambient.AmbientLifecycleObserver
|
||||
|
||||
val ambientCallback = object : AmbientLifecycleObserver.AmbientLifecycleCallback {
|
||||
override fun onEnterAmbient(ambientDetails: AmbientLifecycleObserver.AmbientDetails) {
|
||||
// ... Called when moving from interactive mode into ambient mode.
|
||||
}
|
||||
|
||||
override fun onExitAmbient() {
|
||||
// ... Called when leaving ambient mode, back into interactive mode.
|
||||
}
|
||||
|
||||
override fun onUpdateAmbient() {
|
||||
// ... Called by the system in order to allow the app to periodically
|
||||
// update the display while in ambient mode. Typically the system will
|
||||
// call this every 60 seconds.
|
||||
}
|
||||
}
|
||||
@@ -5,11 +5,13 @@ import com.theeasiestway.opus.Opus
|
||||
|
||||
import android.Manifest
|
||||
import android.content.Context
|
||||
|
||||
import android.media.AudioFormat
|
||||
import android.media.AudioRecord
|
||||
import android.media.MediaRecorder
|
||||
import android.media.audiofx.AutomaticGainControl
|
||||
import android.media.audiofx.NoiseSuppressor
|
||||
import android.os.Build
|
||||
import android.util.Log
|
||||
import androidx.annotation.RequiresPermission
|
||||
import encodePcmToAac
|
||||
@@ -21,6 +23,66 @@ import java.time.Instant
|
||||
/**
|
||||
* A helper class to provide methods to record audio input from the MIC to the internal storage.
|
||||
*/
|
||||
|
||||
import android.os.VibrationEffect
|
||||
import android.os.Vibrator
|
||||
import android.os.VibratorManager
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
|
||||
fun vibrateDouble(
|
||||
context: Context,
|
||||
firstDuration: Long = 100,
|
||||
pauseDuration: Long = 200,
|
||||
secondDuration: Long = 100
|
||||
) {
|
||||
// Get the vibrator service
|
||||
val vibrator = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
val vibratorManager = context.getSystemService(Context.VIBRATOR_MANAGER_SERVICE) as VibratorManager
|
||||
vibratorManager.defaultVibrator
|
||||
} else {
|
||||
@Suppress("DEPRECATION")
|
||||
context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
|
||||
}
|
||||
|
||||
// Create a pattern for double vibration: 0ms delay, firstDuration vibrate, pauseDuration pause, secondDuration vibrate
|
||||
val vibrationPattern = longArrayOf(0, firstDuration, pauseDuration, secondDuration)
|
||||
|
||||
// The -1 parameter means don't repeat the pattern
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
val vibrationEffect = VibrationEffect.createWaveform(vibrationPattern, -1)
|
||||
vibrator.vibrate(vibrationEffect)
|
||||
} else {
|
||||
// Deprecated method for older API levels
|
||||
@Suppress("DEPRECATION")
|
||||
vibrator.vibrate(vibrationPattern, -1)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun vibrate(context: Context, duration: Long = 500) {
|
||||
val vibrator = if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) {
|
||||
val vibratorManager = context.getSystemService(Context.VIBRATOR_MANAGER_SERVICE) as VibratorManager
|
||||
vibratorManager.defaultVibrator
|
||||
} else {
|
||||
@Suppress("DEPRECATION")
|
||||
context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
|
||||
}
|
||||
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
|
||||
val vibrationEffect = VibrationEffect.createOneShot(
|
||||
duration, // duration in milliseconds
|
||||
VibrationEffect.DEFAULT_AMPLITUDE // strength of vibration
|
||||
)
|
||||
vibrator.vibrate(vibrationEffect)
|
||||
} else {
|
||||
@Suppress("DEPRECATION")
|
||||
vibrator.vibrate(duration) // Vibration for 500ms
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
class SoundRecorder(
|
||||
context_in: Context,
|
||||
@@ -44,7 +106,7 @@ class SoundRecorder(
|
||||
var noiseSuppressor: NoiseSuppressor? = null
|
||||
var automaticGainControl: AutomaticGainControl? = null
|
||||
var chunk_index: Int = 0
|
||||
val audioSource = MediaRecorder.AudioSource.DEFAULT
|
||||
val audioSource = MediaRecorder.AudioSource.VOICE_RECOGNITION
|
||||
val sampleRateInHz = 48000
|
||||
val channelConfig = AudioFormat.CHANNEL_IN_MONO
|
||||
val audioFormat = AudioFormat.ENCODING_PCM_16BIT
|
||||
|
||||
@@ -2,8 +2,11 @@ package com.birdsounds.identify.presentation
|
||||
|
||||
import android.util.Log
|
||||
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 java.time.Instant
|
||||
|
||||
@@ -12,16 +15,24 @@ class AScore(
|
||||
_species: String,
|
||||
_score: Float,
|
||||
_timestamp: Long,
|
||||
_trigger: Boolean = false
|
||||
_trigger: Boolean = false,
|
||||
_new_entry: Boolean = false,
|
||||
_redraw_number: Long = 0,
|
||||
) {
|
||||
|
||||
var redraw_number = _redraw_number;
|
||||
val split_stuff: List<String> = _species.split("_");
|
||||
val species = split_stuff[0];
|
||||
val score = _score;
|
||||
var trigger = _trigger;
|
||||
var new_entry = _new_entry;
|
||||
|
||||
// var common_name = split_stuff[1];
|
||||
val common_name = if (split_stuff.size > 1) split_stuff[1] else "";
|
||||
val timestamp = _timestamp
|
||||
fun set_redraw_number(redraw_num: Long)
|
||||
{
|
||||
redraw_number = redraw_num
|
||||
}
|
||||
fun age(): Long {
|
||||
var tstamp: Long = Instant.now().toEpochMilli()
|
||||
return (tstamp - timestamp)
|
||||
@@ -34,20 +45,28 @@ class AScore(
|
||||
}
|
||||
}
|
||||
|
||||
var internal_list = mutableListOf<AScore>()
|
||||
|
||||
object SpeciesList {
|
||||
var internal_list = mutableListOf<AScore>()
|
||||
var trigger_redraw = 0;
|
||||
var num_new_entries = 0;
|
||||
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 clear_new_flags() {
|
||||
for (i in internal_list) {
|
||||
i.new_entry = false;
|
||||
}
|
||||
}
|
||||
|
||||
fun add_observation(species_in: AScore) {
|
||||
fun add_observation(species_in: AScore): Boolean {
|
||||
Log.w(TAG, "In add observation")
|
||||
var idx = 0
|
||||
var idx_replace = -1
|
||||
var added_new = false;
|
||||
for (i in internal_list) {
|
||||
if (i.species == species_in.species) {
|
||||
idx_replace = idx
|
||||
@@ -60,22 +79,31 @@ object SpeciesList {
|
||||
internal_list[idx_replace].trigger = true;
|
||||
} else {
|
||||
internal_list.add(species_in)
|
||||
added_new = true;
|
||||
}
|
||||
|
||||
// internal_list = internal_list.sortedBy({ (it.age()) }).toMutableList()
|
||||
internal_list = internal_list.sortedWith(compareBy({ it.age() }, { it.score})).toMutableList()
|
||||
internal_list =
|
||||
internal_list.sortedWith(compareBy({ it.age() }, { it.score })).toMutableList()
|
||||
|
||||
return added_new
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
fun update_list_on_ui_thread(
|
||||
) {
|
||||
while (_list_on_ui!!.size < internal_list.size) {
|
||||
_list_on_ui!!.add(mutableStateOf(AScore("", 0.0F, 0L)))
|
||||
}
|
||||
val time_stamp = System.currentTimeMillis()
|
||||
for ((index, value) in internal_list.withIndex()) {
|
||||
_list_on_ui?.get(index)?.value = value;
|
||||
}
|
||||
|
||||
Log.w(TAG, internal_list.size.toString())
|
||||
Log.w(TAG, _list_on_ui?.size.toString()) // internal_list.add(species_in)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -2,23 +2,22 @@ package com.birdsounds.identify.presentation
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.text.format.DateFormat
|
||||
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.Row
|
||||
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.foundation.layout.size
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.rounded.Mic
|
||||
import androidx.compose.material.icons.rounded.MicOff
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.key
|
||||
import androidx.compose.runtime.mutableStateListOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
@@ -27,23 +26,25 @@ 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.font.FontStyle
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.Constraints
|
||||
import androidx.compose.ui.text.style.TextDecoration
|
||||
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.curvedComposable
|
||||
import androidx.wear.compose.foundation.lazy.items
|
||||
import androidx.wear.compose.material.Text
|
||||
import androidx.wear.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.wear.compose.material.Icon
|
||||
import androidx.wear.compose.material.TimeTextDefaults
|
||||
import androidx.wear.compose.material.TimeText
|
||||
import androidx.wear.compose.material.curvedText
|
||||
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
|
||||
import java.time.Instant
|
||||
import java.util.Locale
|
||||
|
||||
|
||||
fun interpolateColor(value: Float): Color {
|
||||
@@ -63,88 +64,190 @@ fun interpolateColor(value: Float): Color {
|
||||
return Color(red.toInt(), green.toInt(), blue.toInt())
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun FlashingText(
|
||||
text: String,
|
||||
color: Color,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
// Set up the flashing state
|
||||
var isFlashing by remember { mutableStateOf(false) }
|
||||
|
||||
// Create animation for the background color
|
||||
val backgroundColor by animateColorAsState(
|
||||
targetValue = if (isFlashing) Color.Cyan else Color.Transparent,
|
||||
animationSpec = tween(durationMillis = 500)
|
||||
)
|
||||
|
||||
// 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 species_list_show = mutableStateListOf<MutableState<AScore>>()
|
||||
|
||||
@SuppressLint("UnrememberedMutableState")
|
||||
@OptIn(ExperimentalHorologistApi::class)
|
||||
@Composable
|
||||
fun SpeciesListView(context: Context,
|
||||
fun SpeciesListView(
|
||||
context: Context, appState: AppState,
|
||||
) {
|
||||
|
||||
val species_list_show = mutableStateListOf<MutableState<AScore>>()
|
||||
for (i in 1..3)
|
||||
{
|
||||
|
||||
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(
|
||||
var columnState = rememberResponsiveColumnState(
|
||||
contentPadding = ScalingLazyColumnDefaults.padding(
|
||||
first = ScalingLazyColumnDefaults.ItemType.Text,
|
||||
last = ScalingLazyColumnDefaults.ItemType.Chip
|
||||
),
|
||||
verticalArrangement = Arrangement.spacedBy(
|
||||
space = 1.dp,
|
||||
alignment = Alignment.Top,
|
||||
),)
|
||||
),
|
||||
)
|
||||
|
||||
val text_counter = remember { mutableStateOf("Initial text") }
|
||||
LaunchedEffect(key1 = true) {
|
||||
while (true) {
|
||||
// Update the text - you can put your custom logic here
|
||||
// For example, showing current time:
|
||||
var lag = ((Instant.now().toEpochMilli() - last_message_recv_tstamp) / 1000F).toInt();
|
||||
if (last_message_recv_tstamp == 0L) {
|
||||
text_counter.value = "No msg recv"
|
||||
} else {
|
||||
text_counter.value = "${lag}s"
|
||||
}
|
||||
|
||||
// Delay for 1 second
|
||||
delay(1000)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
ScreenScaffold(
|
||||
scrollState = columnState
|
||||
) {
|
||||
TimeText(
|
||||
timeSource =
|
||||
TimeTextDefaults.timeSource(
|
||||
DateFormat.getBestDateTimePattern(Locale.getDefault(), "hh:mm")
|
||||
),
|
||||
startCurvedContent = {
|
||||
curvedComposable {
|
||||
Icon(
|
||||
if (appState == AppState.Recording) Icons.Rounded.Mic else Icons.Rounded.MicOff,
|
||||
contentDescription = "",
|
||||
modifier = Modifier.size(16.dp)
|
||||
// tint = androidx.compose.ui.graphics.Color.Red
|
||||
)
|
||||
}
|
||||
|
||||
// curvedText(
|
||||
// text = if (appState == AppState.Recording) "" else "Inactive0",
|
||||
//// textAlign = TextAlign.Center,
|
||||
// )
|
||||
},
|
||||
endCurvedContent = {
|
||||
|
||||
curvedText(text = text_counter.value)//, textAlign = TextAlign.Center)
|
||||
|
||||
}
|
||||
|
||||
|
||||
)
|
||||
Column(
|
||||
verticalArrangement = Arrangement.Center,
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.align(Alignment.Center)
|
||||
) {
|
||||
|
||||
ScreenScaffold(scrollState = columnState) {
|
||||
ScalingLazyColumn(
|
||||
columnState = columnState,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
items(species_show) { aSpec ->
|
||||
if (aSpec.value.trigger)
|
||||
{
|
||||
itemsIndexed(
|
||||
species_show,
|
||||
) { index, aSpec ->
|
||||
var text_decor = TextDecoration.None
|
||||
var text_style = FontStyle.Normal;
|
||||
|
||||
val currentName = aSpec.value.common_name
|
||||
val currentScore = aSpec.value.score
|
||||
val isNewEntry = aSpec.value.new_entry
|
||||
val shouldTrigger = aSpec.value.trigger
|
||||
|
||||
var add_sep = false;
|
||||
if (aSpec.value.new_entry) {
|
||||
// add_sep = true;
|
||||
text_style = FontStyle.Italic;
|
||||
// aSpec.value.new_entry = false
|
||||
}
|
||||
Log.w(TAG, "Species " + currentName + " " + aSpec.value.new_entry.toString())
|
||||
LaunchedEffect(Unit) {
|
||||
delay(100);
|
||||
aSpec.value.new_entry = false
|
||||
}
|
||||
// if (index == (SpeciesList.num_new_entries-1)) {
|
||||
// add_sep = true;"
|
||||
// }
|
||||
Log.w(
|
||||
TAG,
|
||||
"Adding Separator: " + index.toString() + " " + SpeciesList.num_new_entries + " " + add_sep.toString()
|
||||
)
|
||||
aSpec.value.new_entry = false;
|
||||
|
||||
// var composables: List<@Composable () -> Unit> = listOf();
|
||||
|
||||
key(currentName, isNewEntry, shouldTrigger) {
|
||||
if (shouldTrigger) {
|
||||
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))
|
||||
FlashingText(
|
||||
text = aSpec.value.common_name,
|
||||
fontStyle = text_style,
|
||||
color = interpolateColor(aSpec.value.score),
|
||||
key = Instant.now().toEpochMilli(),
|
||||
textDecor = text_decor,
|
||||
modifier = Modifier.fillMaxSize()
|
||||
)
|
||||
LaunchedEffect(Unit) {
|
||||
delay(100);
|
||||
aSpec.value.trigger = false
|
||||
}
|
||||
Log.e("TAG", "BLINKING")
|
||||
} else {
|
||||
|
||||
AutoSizedText(
|
||||
text = aSpec.value.common_name,
|
||||
color = interpolateColor(aSpec.value.score),
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
textAlign = TextAlign.Center,
|
||||
textDecor = text_decor,
|
||||
fontStyle = text_style,
|
||||
)
|
||||
|
||||
Log.e("TAG", "Not blinking")
|
||||
}
|
||||
}
|
||||
|
||||
if (add_sep) {
|
||||
// composables += {
|
||||
AutoSizedText(
|
||||
text = "-------------------",
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
// }
|
||||
}
|
||||
|
||||
// Column {
|
||||
// composables.forEach { composable ->
|
||||
// Row {
|
||||
// composable()
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
}// Dynamically display the chips
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
// ScreenScaffold(scrollState = columnState) {
|
||||
// ScalingLazyColumn(columnState = columnState, modifier = Modifier.fillMaxSize()) {
|
||||
// items(species_show) { aSpec -> Text(text = aSpec.value.common_name)
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
package com.birdsounds.identify.presentation
|
||||
|
||||
|
||||
import android.content.Context
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
|
||||
import androidx.navigation.NavHostController
|
||||
import com.google.android.horologist.compose.layout.ScreenScaffold
|
||||
|
||||
@Composable
|
||||
fun StartRecordingScreen(
|
||||
appState: AppState,
|
||||
onMicClicked: () -> Unit,
|
||||
onNavClicked: () -> Unit
|
||||
) {
|
||||
ScreenScaffold {
|
||||
val controlDashboardUiState = computeControlDashboardUiState(
|
||||
appState = appState,
|
||||
)
|
||||
ControlDashboard(
|
||||
controlDashboardUiState = controlDashboardUiState,
|
||||
onMicClicked = onMicClicked,
|
||||
onNavClicked = onNavClicked
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun computeControlDashboardUiState(
|
||||
appState: AppState,
|
||||
): ControlDashboardUiState =
|
||||
when (appState) {
|
||||
AppState.Ready -> ControlDashboardUiState(
|
||||
micState = ControlDashboardButtonUiState(expanded = false, visible = true, enabled = true),
|
||||
)
|
||||
AppState.Recording -> ControlDashboardUiState(
|
||||
micState = ControlDashboardButtonUiState(expanded = true, visible = true, enabled = true),
|
||||
)
|
||||
}
|
||||
|
||||
private class PlaybackStatePreviewProvider : CollectionPreviewParameterProvider<AppState>(
|
||||
listOf(
|
||||
AppState.Recording,
|
||||
AppState.Ready
|
||||
)
|
||||
)
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
<style name="MainActivityTheme.Starting" parent="Theme.SplashScreen">
|
||||
<item name="windowSplashScreenBackground">@android:color/black</item>
|
||||
<item name="windowSplashScreenAnimatedIcon">@drawable/splash_icon</item>
|
||||
<item name="windowSplashScreenAnimatedIcon">@mipmap/ic_launcher</item>
|
||||
<item name="postSplashScreenTheme">@android:style/Theme.DeviceDefault</item>
|
||||
</style>
|
||||
</resources>
|
||||
Reference in New Issue
Block a user