Restore latest local work after Seafile git corruption repair
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TvDdasWXdhrvQriBCPfFn1
This commit is contained in:
-15
@@ -1,15 +0,0 @@
|
|||||||
*.iml
|
|
||||||
.gradle
|
|
||||||
/local.properties
|
|
||||||
/.idea/caches
|
|
||||||
/.idea/libraries
|
|
||||||
/.idea/modules.xml
|
|
||||||
/.idea/workspace.xml
|
|
||||||
/.idea/navEditor.xml
|
|
||||||
/.idea/assetWizardSettings.xml
|
|
||||||
.DS_Store
|
|
||||||
/build
|
|
||||||
/captures
|
|
||||||
.externalNativeBuild
|
|
||||||
.cxx
|
|
||||||
local.properties
|
|
||||||
|
|||||||
Generated
-1
@@ -1 +0,0 @@
|
|||||||
identify
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="deploymentTargetSelector">
|
|
||||||
<selectionStates>
|
|
||||||
<SelectionState runConfigName="wear">
|
|
||||||
<option name="selectionMode" value="DROPDOWN" />
|
|
||||||
<DropdownSelection timestamp="2025-03-03T16:57:28.073351600Z">
|
|
||||||
<Target type="DEFAULT_BOOT">
|
|
||||||
<handle>
|
|
||||||
<DeviceId pluginId="PhysicalDevice" identifier="serial=RFAX803LMFR" />
|
|
||||||
</handle>
|
|
||||||
</Target>
|
|
||||||
</DropdownSelection>
|
|
||||||
<DialogSelection />
|
|
||||||
</SelectionState>
|
|
||||||
<SelectionState runConfigName="mobile">
|
|
||||||
<option name="selectionMode" value="DROPDOWN" />
|
|
||||||
<DropdownSelection timestamp="2025-03-03T16:57:37.023647400Z">
|
|
||||||
<Target type="DEFAULT_BOOT">
|
|
||||||
<handle>
|
|
||||||
<DeviceId pluginId="PhysicalDevice" identifier="serial=569cc5b0" />
|
|
||||||
</handle>
|
|
||||||
</Target>
|
|
||||||
</DropdownSelection>
|
|
||||||
<DialogSelection />
|
|
||||||
</SelectionState>
|
|
||||||
</selectionStates>
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
Generated
+4
-4
@@ -4,10 +4,10 @@
|
|||||||
<selectionStates>
|
<selectionStates>
|
||||||
<SelectionState runConfigName="mobile">
|
<SelectionState runConfigName="mobile">
|
||||||
<option name="selectionMode" value="DROPDOWN" />
|
<option name="selectionMode" value="DROPDOWN" />
|
||||||
<DropdownSelection timestamp="2025-03-09T15:35:16.375287800Z">
|
<DropdownSelection timestamp="2025-03-30T04:58:13.296203100Z">
|
||||||
<Target type="DEFAULT_BOOT">
|
<Target type="DEFAULT_BOOT">
|
||||||
<handle>
|
<handle>
|
||||||
<DeviceId pluginId="PhysicalDevice" identifier="serial=RFAX803LMFR" />
|
<DeviceId pluginId="PhysicalDevice" identifier="serial=569cc5b0" />
|
||||||
</handle>
|
</handle>
|
||||||
</Target>
|
</Target>
|
||||||
</DropdownSelection>
|
</DropdownSelection>
|
||||||
@@ -15,10 +15,10 @@
|
|||||||
</SelectionState>
|
</SelectionState>
|
||||||
<SelectionState runConfigName="wear">
|
<SelectionState runConfigName="wear">
|
||||||
<option name="selectionMode" value="DROPDOWN" />
|
<option name="selectionMode" value="DROPDOWN" />
|
||||||
<DropdownSelection timestamp="2025-03-29T14:49:45.062493500Z">
|
<DropdownSelection timestamp="2025-04-24T15:34:17.184421300Z">
|
||||||
<Target type="DEFAULT_BOOT">
|
<Target type="DEFAULT_BOOT">
|
||||||
<handle>
|
<handle>
|
||||||
<DeviceId pluginId="LocalEmulator" identifier="path=C:\Users\thebears\.android\avd\Wear_OS_Large_Round_API_34.avd" />
|
<DeviceId pluginId="Default" identifier="serial=192.168.1.175:37625;connection=a5bdadc2" />
|
||||||
</handle>
|
</handle>
|
||||||
</Target>
|
</Target>
|
||||||
</DropdownSelection>
|
</DropdownSelection>
|
||||||
|
|||||||
@@ -1,260 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="AndroidLayouts">
|
|
||||||
<shared>
|
|
||||||
<config />
|
|
||||||
</shared>
|
|
||||||
</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 afterPath="$PROJECT_DIR$/mobile/src/main/ic_launcher-playstore.png" afterDir="false" />
|
|
||||||
<change afterPath="$PROJECT_DIR$/mobile/src/main/res/mipmap-anydpi-v26/ic_launcher.xml" afterDir="false" />
|
|
||||||
<change afterPath="$PROJECT_DIR$/mobile/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml" afterDir="false" />
|
|
||||||
<change afterPath="$PROJECT_DIR$/mobile/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp" afterDir="false" />
|
|
||||||
<change afterPath="$PROJECT_DIR$/mobile/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp" afterDir="false" />
|
|
||||||
<change afterPath="$PROJECT_DIR$/mobile/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp" afterDir="false" />
|
|
||||||
<change afterPath="$PROJECT_DIR$/mobile/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp" afterDir="false" />
|
|
||||||
<change afterPath="$PROJECT_DIR$/mobile/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/.idea/codeStyles/codeStyleConfig.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/codeStyles/codeStyleConfig.xml" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/.idea/deploymentTargetSelector.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/deploymentTargetSelector.xml" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/.idea/misc.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/misc.xml" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/gradle/libs.versions.toml" beforeDir="false" afterPath="$PROJECT_DIR$/gradle/libs.versions.toml" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/mobile/build.gradle.kts" beforeDir="false" afterPath="$PROJECT_DIR$/mobile/build.gradle.kts" afterDir="false" />
|
|
||||||
<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/res/drawable/ic_launcher_background.xml" beforeDir="false" afterPath="$PROJECT_DIR$/mobile/src/main/res/drawable/ic_launcher_background.xml" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/mobile/src/main/res/mipmap-anydpi/ic_launcher.xml" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/mobile/src/main/res/mipmap-anydpi/ic_launcher_round.xml" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/mobile/src/main/res/mipmap-hdpi/ic_launcher.webp" beforeDir="false" afterPath="$PROJECT_DIR$/mobile/src/main/res/mipmap-hdpi/ic_launcher.webp" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/mobile/src/main/res/mipmap-hdpi/ic_launcher_round.webp" beforeDir="false" afterPath="$PROJECT_DIR$/mobile/src/main/res/mipmap-hdpi/ic_launcher_round.webp" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/mobile/src/main/res/mipmap-mdpi/ic_launcher.webp" beforeDir="false" afterPath="$PROJECT_DIR$/mobile/src/main/res/mipmap-mdpi/ic_launcher.webp" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/mobile/src/main/res/mipmap-mdpi/ic_launcher_round.webp" beforeDir="false" afterPath="$PROJECT_DIR$/mobile/src/main/res/mipmap-mdpi/ic_launcher_round.webp" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/mobile/src/main/res/mipmap-xhdpi/ic_launcher.webp" beforeDir="false" afterPath="$PROJECT_DIR$/mobile/src/main/res/mipmap-xhdpi/ic_launcher.webp" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/mobile/src/main/res/mipmap-xhdpi/ic_launcher_round.webp" beforeDir="false" afterPath="$PROJECT_DIR$/mobile/src/main/res/mipmap-xhdpi/ic_launcher_round.webp" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/mobile/src/main/res/mipmap-xxhdpi/ic_launcher.webp" beforeDir="false" afterPath="$PROJECT_DIR$/mobile/src/main/res/mipmap-xxhdpi/ic_launcher.webp" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/mobile/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp" beforeDir="false" afterPath="$PROJECT_DIR$/mobile/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/mobile/src/main/res/mipmap-xxxhdpi/ic_launcher.webp" beforeDir="false" afterPath="$PROJECT_DIR$/mobile/src/main/res/mipmap-xxxhdpi/ic_launcher.webp" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/mobile/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp" beforeDir="false" afterPath="$PROJECT_DIR$/mobile/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/wear/src/main/res/mipmap-hdpi/ic_launcher.webp" beforeDir="false" afterPath="$PROJECT_DIR$/wear/src/main/res/mipmap-hdpi/ic_launcher.webp" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/wear/src/main/res/mipmap-mdpi/ic_launcher.webp" beforeDir="false" afterPath="$PROJECT_DIR$/wear/src/main/res/mipmap-mdpi/ic_launcher.webp" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/wear/src/main/res/mipmap-xhdpi/ic_launcher.webp" beforeDir="false" afterPath="$PROJECT_DIR$/wear/src/main/res/mipmap-xhdpi/ic_launcher.webp" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/wear/src/main/res/mipmap-xxhdpi/ic_launcher.webp" beforeDir="false" afterPath="$PROJECT_DIR$/wear/src/main/res/mipmap-xxhdpi/ic_launcher.webp" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/wear/src/main/res/mipmap-xxxhdpi/ic_launcher.webp" beforeDir="false" afterPath="$PROJECT_DIR$/wear/src/main/res/mipmap-xxxhdpi/ic_launcher.webp" 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[DeviceId(pluginId=PhysicalDevice, isTemplate=false, identifier=serial=569cc5b0)]" />
|
|
||||||
<component name="ExternalProjectsData">
|
|
||||||
<projectState path="$PROJECT_DIR$">
|
|
||||||
<ProjectState />
|
|
||||||
</projectState>
|
|
||||||
</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"><![CDATA[{
|
|
||||||
"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",
|
|
||||||
"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="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="1741019799252" />
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="com.birdsounds.identify.test">
|
|
||||||
<value>
|
|
||||||
<CheckInfo lastCheckTimestamp="1741019799252" />
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
</map>
|
|
||||||
</option>
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
@@ -1,254 +0,0 @@
|
|||||||
<?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,254 +0,0 @@
|
|||||||
<?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>
|
|
||||||
@@ -40,6 +40,8 @@ runtimeAndroid = "1.6.6"
|
|||||||
datastoreCoreAndroid = "1.1.3"
|
datastoreCoreAndroid = "1.1.3"
|
||||||
coreKtxVersion = "1.13.0"
|
coreKtxVersion = "1.13.0"
|
||||||
animationCoreAndroid = "1.6.6"
|
animationCoreAndroid = "1.6.6"
|
||||||
|
#litertSupportApi = "1.2.0"
|
||||||
|
#litert = "1.2.0"
|
||||||
#litert = "1.0.1"
|
#litert = "1.0.1"
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
@@ -91,6 +93,8 @@ core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtxV
|
|||||||
stately-concurrent-collections = { module = "co.touchlab:stately-concurrent-collections", version.ref = "statelyConcurrentCollections" }
|
stately-concurrent-collections = { module = "co.touchlab:stately-concurrent-collections", version.ref = "statelyConcurrentCollections" }
|
||||||
ui-tooling = { module = "androidx.compose.ui:ui-tooling", version.ref = "uiTooling" }
|
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" }
|
androidx-animation-core-android = { group = "androidx.compose.animation", name = "animation-core-android", version.ref = "animationCoreAndroid" }
|
||||||
|
#litert-support-api = { group = "com.google.ai.edge.litert", name = "litert-support-api", version.ref = "litertSupportApi" }
|
||||||
|
|
||||||
#litert = { group = "com.google.ai.edge.litert", name = "litert", version.ref = "litert" }
|
#litert = { group = "com.google.ai.edge.litert", name = "litert", version.ref = "litert" }
|
||||||
|
|
||||||
[plugins]
|
[plugins]
|
||||||
|
|||||||
@@ -1,73 +0,0 @@
|
|||||||
plugins {
|
|
||||||
alias(libs.plugins.android.application)
|
|
||||||
alias(libs.plugins.kotlin.android)
|
|
||||||
alias(libs.plugins.kotlin.compose)
|
|
||||||
}
|
|
||||||
|
|
||||||
android {
|
|
||||||
namespace = "com.birdsounds.identify"
|
|
||||||
compileSdk = 34
|
|
||||||
|
|
||||||
defaultConfig {
|
|
||||||
applicationId = "com.birdsounds.identify"
|
|
||||||
minSdk = 34
|
|
||||||
targetSdk = 34
|
|
||||||
versionCode = 1
|
|
||||||
versionName = "1.0"
|
|
||||||
|
|
||||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
|
||||||
}
|
|
||||||
|
|
||||||
buildTypes {
|
|
||||||
release {
|
|
||||||
isMinifyEnabled = false
|
|
||||||
proguardFiles(
|
|
||||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
|
||||||
"proguard-rules.pro"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
compileOptions {
|
|
||||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
|
||||||
targetCompatibility = JavaVersion.VERSION_1_8
|
|
||||||
}
|
|
||||||
kotlinOptions {
|
|
||||||
jvmTarget = "1.8"
|
|
||||||
}
|
|
||||||
buildFeatures {
|
|
||||||
compose = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
implementation(libs.androidx.core.ktx)
|
|
||||||
implementation(libs.androidx.appcompat)
|
|
||||||
implementation(libs.play.services.wearable)
|
|
||||||
implementation(libs.material)
|
|
||||||
implementation(libs.androidx.activity)
|
|
||||||
implementation(libs.androidx.constraintlayout)
|
|
||||||
implementation(libs.androidx.work.runtime.ktx)
|
|
||||||
implementation(libs.androidx.lifecycle.runtime.ktx)
|
|
||||||
implementation(libs.androidx.activity.compose)
|
|
||||||
implementation(platform(libs.androidx.compose.bom))
|
|
||||||
implementation(libs.androidx.ui)
|
|
||||||
implementation("uk.me.berndporr:iirj:1.7")
|
|
||||||
implementation(libs.androidx.ui.graphics)
|
|
||||||
implementation(libs.androidx.ui.tooling.preview)
|
|
||||||
implementation(libs.androidx.material3)
|
|
||||||
implementation(libs.litert)
|
|
||||||
testImplementation(libs.junit)
|
|
||||||
androidTestImplementation(libs.androidx.junit)
|
|
||||||
androidTestImplementation(libs.androidx.espresso.core)
|
|
||||||
androidTestImplementation(platform(libs.androidx.compose.bom))
|
|
||||||
androidTestImplementation(libs.androidx.ui.test.junit4)
|
|
||||||
debugImplementation(libs.androidx.ui.tooling)
|
|
||||||
debugImplementation(libs.androidx.ui.test.manifest)
|
|
||||||
implementation("org.tensorflow:tensorflow-lite-gpu:2.12.0")
|
|
||||||
implementation("org.tensorflow:tensorflow-lite-support:0.4.4")
|
|
||||||
implementation("org.tensorflow:tensorflow-lite-task-vision:0.4.4")
|
|
||||||
implementation("org.tensorflow:tensorflow-lite-task-text:0.4.4")
|
|
||||||
implementation("com.google.android.gms:play-services-tflite-gpu:16.2.0")
|
|
||||||
implementation("com.google.android.gms:play-services-tflite-java:16.0.0-beta01")
|
|
||||||
wearApp(project(":wear"))
|
|
||||||
}
|
|
||||||
@@ -51,13 +51,19 @@ dependencies {
|
|||||||
implementation(libs.androidx.activity)
|
implementation(libs.androidx.activity)
|
||||||
implementation(libs.androidx.constraintlayout)
|
implementation(libs.androidx.constraintlayout)
|
||||||
implementation(libs.androidx.work.runtime.ktx)
|
implementation(libs.androidx.work.runtime.ktx)
|
||||||
|
// implementation("org.tensorflow:tensorflow-lite-task-vision")
|
||||||
|
// implementation("org.tensorflow:tensorflow-lite-gpu-delegate-plugin")
|
||||||
implementation(libs.androidx.lifecycle.runtime.ktx)
|
implementation(libs.androidx.lifecycle.runtime.ktx)
|
||||||
implementation(libs.androidx.activity.compose)
|
implementation(libs.androidx.activity.compose)
|
||||||
implementation(platform(libs.androidx.compose.bom))
|
implementation(platform(libs.androidx.compose.bom))
|
||||||
implementation(libs.androidx.ui)
|
implementation(libs.androidx.ui)
|
||||||
|
implementation("io.github.nailik:androidresampler:0.1")
|
||||||
|
// implementation("org.tensorflow:tensorflow-lite:1.12.0")
|
||||||
|
implementation("org.tensorflow:tensorflow-lite:2.16.1")
|
||||||
|
implementation("org.tensorflow:tensorflow-lite-task-vision:0.4.4")
|
||||||
|
|
||||||
implementation("androidx.datastore:datastore-preferences:1.1.3")
|
implementation("androidx.datastore:datastore-preferences:1.1.3")
|
||||||
implementation("org.tensorflow:tensorflow-lite:2.6.0")
|
// implementation("org.tensorflow:tensorflow-lite:2.6.0")
|
||||||
// implementation("com.google.android.gms:play-services-tflite:20.0.0")
|
// implementation("com.google.android.gms:play-services-tflite:20.0.0")
|
||||||
implementation("uk.me.berndporr:iirj:1.7")
|
implementation("uk.me.berndporr:iirj:1.7")
|
||||||
implementation(libs.androidx.ui.graphics)
|
implementation(libs.androidx.ui.graphics)
|
||||||
@@ -65,6 +71,8 @@ dependencies {
|
|||||||
implementation(libs.androidx.material3)
|
implementation(libs.androidx.material3)
|
||||||
implementation(libs.androidx.datastore.core.android)
|
implementation(libs.androidx.datastore.core.android)
|
||||||
implementation(libs.core.ktx)
|
implementation(libs.core.ktx)
|
||||||
|
// implementation(libs.litert.support.api)
|
||||||
|
// implementation(libs.litert)
|
||||||
// implementation(libs.litert)
|
// implementation(libs.litert)
|
||||||
testImplementation(libs.junit)
|
testImplementation(libs.junit)
|
||||||
androidTestImplementation(libs.androidx.junit)
|
androidTestImplementation(libs.androidx.junit)
|
||||||
|
|||||||
Binary file not shown.
@@ -42,11 +42,13 @@ class Downloader(mainActivity: MainActivity) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun prepareModelFiles()
|
fun prepareModelFiles() {
|
||||||
{
|
|
||||||
copyAssetToFolder(settings.pkg_model_file, settings.local_model_file);
|
|
||||||
copyAssetToFolder(settings.pkg_meta_model_file, settings.local_meta_model_file);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
for (files_to_copy in Settings.all_files) {
|
||||||
|
copyAssetToFolder(
|
||||||
|
files_to_copy["pkg"].toString(),
|
||||||
|
files_to_copy["file"].toString()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -31,7 +31,11 @@ object Location {
|
|||||||
locationListenerGPS = null
|
locationListenerGPS = null
|
||||||
}
|
}
|
||||||
|
|
||||||
fun requestLocation(context: Context, soundClassifier: SoundClassifier) {
|
fun requestLocation(
|
||||||
|
context: Context,
|
||||||
|
soundClassifier: SoundClassifier,
|
||||||
|
soundClassifierSpectrogramBased: SoundClassifierSpectrogramBased
|
||||||
|
) {
|
||||||
if (ActivityCompat.checkSelfPermission(
|
if (ActivityCompat.checkSelfPermission(
|
||||||
context,
|
context,
|
||||||
Manifest.permission.ACCESS_COARSE_LOCATION
|
Manifest.permission.ACCESS_COARSE_LOCATION
|
||||||
@@ -44,6 +48,8 @@ object Location {
|
|||||||
if (locationListenerGPS == null) locationListenerGPS = object : LocationListener {
|
if (locationListenerGPS == null) locationListenerGPS = object : LocationListener {
|
||||||
@SuppressLint("SetTextI18n")
|
@SuppressLint("SetTextI18n")
|
||||||
override fun onLocationChanged(location: Location) {
|
override fun onLocationChanged(location: Location) {
|
||||||
|
|
||||||
|
|
||||||
Log.w(TAG, "Got location changed");
|
Log.w(TAG, "Got location changed");
|
||||||
while (!soundClassifier.is_model_ready()) {
|
while (!soundClassifier.is_model_ready()) {
|
||||||
Thread.sleep(50);
|
Thread.sleep(50);
|
||||||
@@ -51,11 +57,19 @@ object Location {
|
|||||||
Log.w(TAG, "Sound classifier is ready");
|
Log.w(TAG, "Sound classifier is ready");
|
||||||
soundClassifier.runMetaInterpreter(location)
|
soundClassifier.runMetaInterpreter(location)
|
||||||
|
|
||||||
|
Log.w(TAG, "Got location changed");
|
||||||
|
while (!soundClassifierSpectrogramBased.is_model_ready()) {
|
||||||
|
Thread.sleep(50);
|
||||||
|
}
|
||||||
|
Log.w(TAG, "Sound classifier is ready");
|
||||||
|
soundClassifierSpectrogramBased.runMetaInterpreter(location)
|
||||||
|
|
||||||
|
|
||||||
val activity = context as? Activity;
|
val activity = context as? Activity;
|
||||||
activity?.let {
|
activity?.let {
|
||||||
|
|
||||||
val text_species: TextView = it.findViewById(R.id.local_species)
|
val text_species: TextView = it.findViewById(R.id.local_species)
|
||||||
text_species.text = local_species;
|
text_species.text = local_species_spectrogram;
|
||||||
|
|
||||||
val loc_lon: TextView = it.findViewById(R.id.location_long)
|
val loc_lon: TextView = it.findViewById(R.id.location_long)
|
||||||
loc_lon.text =
|
loc_lon.text =
|
||||||
@@ -71,12 +85,7 @@ object Location {
|
|||||||
geocoder.getFromLocation(location.latitude, location.longitude, 1)
|
geocoder.getFromLocation(location.latitude, location.longitude, 1)
|
||||||
if (addresses?.isNotEmpty() == true) {
|
if (addresses?.isNotEmpty() == true) {
|
||||||
val address: Address = addresses[0]
|
val address: Address = addresses[0]
|
||||||
|
|
||||||
// loc_string.text = address.locality.toString() + ", " + address.adminArea.toString() + " " + address.countryName.toString()
|
|
||||||
loc_string.text = address.getAddressLine(0).toString()
|
loc_string.text = address.getAddressLine(0).toString()
|
||||||
// Log.w(TAG, address.toString())
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,9 +4,10 @@ import android.Manifest
|
|||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
|
import android.graphics.Bitmap
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.preference.PreferenceManager
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import android.widget.ImageView
|
||||||
import android.widget.SeekBar
|
import android.widget.SeekBar
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import androidx.activity.enableEdgeToEdge
|
import androidx.activity.enableEdgeToEdge
|
||||||
@@ -22,11 +23,13 @@ import androidx.datastore.preferences.preferencesDataStore
|
|||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import com.google.android.gms.wearable.ChannelClient
|
import com.google.android.gms.wearable.ChannelClient
|
||||||
import com.google.android.gms.wearable.Wearable
|
import com.google.android.gms.wearable.Wearable
|
||||||
|
import io.github.nailik.androidresampler.Resampler
|
||||||
|
import io.github.nailik.androidresampler.ResamplerConfiguration
|
||||||
|
import io.github.nailik.androidresampler.data.ResamplerChannel
|
||||||
|
import io.github.nailik.androidresampler.data.ResamplerQuality
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.flow.Flow
|
|
||||||
import kotlinx.coroutines.flow.first
|
import kotlinx.coroutines.flow.first
|
||||||
import kotlinx.coroutines.flow.map
|
|
||||||
import kotlinx.coroutines.isActive
|
import kotlinx.coroutines.isActive
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
@@ -36,8 +39,6 @@ private var updateJob: Job? = null
|
|||||||
private var updateCounter = 0
|
private var updateCounter = 0
|
||||||
val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "settings")
|
val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "settings")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
val Any.TAG: String
|
val Any.TAG: String
|
||||||
get() {
|
get() {
|
||||||
val tag = javaClass.simpleName
|
val tag = javaClass.simpleName
|
||||||
@@ -49,30 +50,64 @@ class SynchronousDataStore(private val context: Context) {
|
|||||||
|
|
||||||
// Define keys
|
// Define keys
|
||||||
companion object {
|
companion object {
|
||||||
val THRESHOLD_KEY = intPreferencesKey("user_age")
|
val THRESHOLD_SPECTROGRAM_KEY = intPreferencesKey("THRESHOLD_SPECTROGRAM")
|
||||||
|
val THRESHOLD_WAVEFORM_KEY = intPreferencesKey("THRESHOLD_WAVEFORM")
|
||||||
|
val THRESHOLD_BIRDHEARD_KEY = intPreferencesKey("THRESHOLD_BIRD_HEARD")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Synchronous write operations
|
fun saveThresholdSpectrogram(value: Int) {
|
||||||
fun saveThreshold(value: Int) {
|
|
||||||
runBlocking {
|
runBlocking {
|
||||||
context.dataStore.edit { preferences ->
|
context.dataStore.edit { preferences ->
|
||||||
preferences[THRESHOLD_KEY] = value;
|
preferences[THRESHOLD_SPECTROGRAM_KEY] = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getThreshold(): Int {
|
fun getThresholdSpectrogram(): Int {
|
||||||
|
|
||||||
return runBlocking {
|
return runBlocking {
|
||||||
context.dataStore.data.first()[THRESHOLD_KEY] ?: 50
|
context.dataStore.data.first()[THRESHOLD_SPECTROGRAM_KEY] ?: 50
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Synchronous write operations
|
||||||
|
fun saveThresholdWaveform(value: Int) {
|
||||||
|
runBlocking {
|
||||||
|
context.dataStore.edit { preferences ->
|
||||||
|
preferences[THRESHOLD_WAVEFORM_KEY] = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getThresholdWaveform(): Int {
|
||||||
|
|
||||||
|
return runBlocking {
|
||||||
|
context.dataStore.data.first()[THRESHOLD_WAVEFORM_KEY] ?: 50
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Synchronous write operations
|
||||||
|
fun saveBirdHeardThreshold(value: Int) {
|
||||||
|
runBlocking {
|
||||||
|
context.dataStore.edit { preferences ->
|
||||||
|
preferences[THRESHOLD_BIRDHEARD_KEY] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getBirdHeardThreshold(): Int {
|
||||||
|
|
||||||
|
return runBlocking {
|
||||||
|
context.dataStore.data.first()[THRESHOLD_BIRDHEARD_KEY] ?: 50
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class MainActivity : AppCompatActivity() {
|
class MainActivity : AppCompatActivity() {
|
||||||
// private lateinit var soundClassifier: SoundClassifier
|
|
||||||
val REQUEST_PERMISSIONS = 1337
|
val REQUEST_PERMISSIONS = 1337
|
||||||
|
|
||||||
private lateinit var dataStore: SynchronousDataStore
|
private lateinit var dataStore: SynchronousDataStore
|
||||||
@@ -88,15 +123,16 @@ class MainActivity : AppCompatActivity() {
|
|||||||
super.onChannelOpened(channel)
|
super.onChannelOpened(channel)
|
||||||
Log.d(TAG, "onChannelOpened")
|
Log.d(TAG, "onChannelOpened")
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
)
|
|
||||||
Downloader(this).prepareModelFiles();
|
Downloader(this).prepareModelFiles();
|
||||||
Log.w(TAG, "Finished setting up downloader")
|
|
||||||
requestPermissions()
|
requestPermissions()
|
||||||
|
|
||||||
|
|
||||||
soundClassifier = SoundClassifier(this, SoundClassifier.Options())
|
soundClassifier = SoundClassifier(this, SoundClassifier.Options())
|
||||||
Log.w(TAG, "Starting sound classifier")
|
soundClassifierSpectrogramBased = SoundClassifierSpectrogramBased(this);
|
||||||
Location.requestLocation(this, soundClassifier!!)
|
|
||||||
Log.w(TAG, "Starting location requester")
|
Location.requestLocation(this, soundClassifier!!, soundClassifierSpectrogramBased!!);
|
||||||
|
|
||||||
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
|
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
|
||||||
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
|
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
|
||||||
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
|
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
|
||||||
@@ -104,26 +140,79 @@ class MainActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
last_message_delay = findViewById<TextView>(R.id.last_message_delay)
|
||||||
dataStore = SynchronousDataStore(applicationContext)
|
dataStore = SynchronousDataStore(applicationContext)
|
||||||
|
|
||||||
|
|
||||||
|
val configuration = ResamplerConfiguration(
|
||||||
|
quality = ResamplerQuality.BEST,
|
||||||
|
inputChannel = ResamplerChannel.MONO,
|
||||||
|
inputSampleRate = 48000,
|
||||||
|
outputChannel = ResamplerChannel.MONO,
|
||||||
|
outputSampleRate = 16000
|
||||||
|
)
|
||||||
|
resampler = Resampler(configuration);
|
||||||
|
|
||||||
|
|
||||||
|
val thresholdText_birdsinging = findViewById<TextView>(R.id.threshold_value_text_birdheard)
|
||||||
Settings.threshold = dataStore.getThreshold();
|
val seekBar_bird_singing = findViewById<SeekBar>(R.id.threshold_set_scale_bar_birdheard);
|
||||||
seekBar.progress = Settings.threshold
|
Settings.threshold_bird_singing = dataStore.getBirdHeardThreshold();
|
||||||
thresholdText.text = String.format("%.2f", Settings.threshold/100f)
|
seekBar_bird_singing.progress = Settings.threshold_bird_singing
|
||||||
seekBar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
|
thresholdText_birdsinging.text =
|
||||||
|
String.format("%.2f", Settings.threshold_bird_singing / 100f)
|
||||||
|
seekBar_bird_singing.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
|
||||||
@SuppressLint("DefaultLocale")
|
@SuppressLint("DefaultLocale")
|
||||||
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
|
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
|
||||||
|
|
||||||
var actualValue = progress
|
var actualValue = progress
|
||||||
dataStore.saveThreshold(actualValue);
|
dataStore.saveBirdHeardThreshold(actualValue);
|
||||||
Settings.threshold = actualValue;
|
Settings.threshold_bird_singing = actualValue;
|
||||||
thresholdText.text = String.format("%.2f", actualValue/100f)
|
thresholdText_birdsinging.text = String.format("%.2f", actualValue / 100f)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onStartTrackingTouch(seekBar: SeekBar) {}
|
||||||
|
|
||||||
|
override fun onStopTrackingTouch(seekBar: SeekBar) {}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
val thresholdText_waveform = findViewById<TextView>(R.id.threshold_value_text_waveform)
|
||||||
|
val seekBar_Waveform = findViewById<SeekBar>(R.id.threshold_set_scale_bar_waveform);
|
||||||
|
Settings.threshold_waveform = dataStore.getThresholdWaveform();
|
||||||
|
seekBar_Waveform.progress = Settings.threshold_waveform
|
||||||
|
thresholdText_waveform.text = String.format("%.2f", Settings.threshold_waveform / 100f)
|
||||||
|
seekBar_Waveform.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
|
||||||
|
@SuppressLint("DefaultLocale")
|
||||||
|
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
|
||||||
|
|
||||||
|
var actualValue = progress
|
||||||
|
dataStore.saveThresholdWaveform(actualValue);
|
||||||
|
Settings.threshold_waveform = actualValue;
|
||||||
|
thresholdText_waveform.text = String.format("%.2f", actualValue / 100f)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onStartTrackingTouch(seekBar: SeekBar) {}
|
||||||
|
|
||||||
|
override fun onStopTrackingTouch(seekBar: SeekBar) {}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
val thresholdText_spectrogram =
|
||||||
|
findViewById<TextView>(R.id.threshold_value_text_spectrogram)
|
||||||
|
val seekBar_Spectrogram = findViewById<SeekBar>(R.id.threshold_set_scale_bar_spectrogram);
|
||||||
|
Settings.threshold_spectrogram = dataStore.getThresholdSpectrogram();
|
||||||
|
seekBar_Spectrogram.progress = Settings.threshold_spectrogram
|
||||||
|
thresholdText_spectrogram.text =
|
||||||
|
String.format("%.2f", Settings.threshold_spectrogram / 100f)
|
||||||
|
seekBar_Spectrogram.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
|
||||||
|
@SuppressLint("DefaultLocale")
|
||||||
|
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
|
||||||
|
|
||||||
|
var actualValue = progress
|
||||||
|
dataStore.saveThresholdSpectrogram(actualValue);
|
||||||
|
Settings.threshold_spectrogram = actualValue;
|
||||||
|
thresholdText_spectrogram.text = String.format("%.2f", actualValue / 100f)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onStartTrackingTouch(seekBar: SeekBar) {}
|
override fun onStartTrackingTouch(seekBar: SeekBar) {}
|
||||||
@@ -132,11 +221,21 @@ class MainActivity : AppCompatActivity() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun set_image_view(bitmap: Bitmap) {
|
||||||
|
val imageView = this.findViewById<ImageView>(R.id.imageView)
|
||||||
|
imageView.setImageBitmap(bitmap)
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
var soundClassifier: SoundClassifier? = null
|
var soundClassifier: SoundClassifier? = null
|
||||||
// fun getSoundClassifier(): SoundClassifier? {
|
var soundClassifierSpectrogramBased: SoundClassifierSpectrogramBased? = null
|
||||||
// return soundClassifier
|
|
||||||
// }
|
var sound_buffer_48000hz = ShortArray(48000 * 10) // Store last 10 seconds in buffer
|
||||||
|
var sound_buffer_22050hz = ShortArray(22050 * 10) // Store last 10 seconds in buffer
|
||||||
|
var resampler: Resampler? = null
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -147,12 +246,12 @@ class MainActivity : AppCompatActivity() {
|
|||||||
updateJob = lifecycleScope.launch {
|
updateJob = lifecycleScope.launch {
|
||||||
while (isActive) { // isActive is a property of the coroutine scope
|
while (isActive) { // isActive is a property of the coroutine scope
|
||||||
updateCounter++
|
updateCounter++
|
||||||
if (last_message_time == 0.toLong())
|
if (last_message_time == 0.toLong()) {
|
||||||
{
|
|
||||||
last_message_delay.text = "No messages received"
|
last_message_delay.text = "No messages received"
|
||||||
} else
|
} else {
|
||||||
{
|
last_message_delay.text = "Last message: ${
|
||||||
last_message_delay.text = "Last message: ${(Instant.now().toEpochMilli() - last_message_time)/1000F.toInt()} seconds ago"
|
(Instant.now().toEpochMilli() - last_message_time) / 1000F.toInt()
|
||||||
|
} seconds ago"
|
||||||
}
|
}
|
||||||
|
|
||||||
// last_message_delay.text = "Update count: $updateCounter"
|
// last_message_delay.text = "Update count: $updateCounter"
|
||||||
@@ -171,8 +270,7 @@ class MainActivity : AppCompatActivity() {
|
|||||||
val perms = mutableListOf<String>()
|
val perms = mutableListOf<String>()
|
||||||
|
|
||||||
if (ContextCompat.checkSelfPermission(
|
if (ContextCompat.checkSelfPermission(
|
||||||
this,
|
this, Manifest.permission.ACCESS_COARSE_LOCATION
|
||||||
Manifest.permission.ACCESS_COARSE_LOCATION
|
|
||||||
) != PackageManager.PERMISSION_GRANTED
|
) != PackageManager.PERMISSION_GRANTED
|
||||||
) {
|
) {
|
||||||
perms.add(Manifest.permission.ACCESS_COARSE_LOCATION)
|
perms.add(Manifest.permission.ACCESS_COARSE_LOCATION)
|
||||||
|
|||||||
@@ -1,16 +1,25 @@
|
|||||||
package com.birdsounds.identify
|
package com.birdsounds.identify
|
||||||
|
|
||||||
|
|
||||||
|
import android.graphics.Bitmap
|
||||||
|
import android.graphics.Color
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
|
||||||
import com.google.android.gms.wearable.MessageEvent
|
import com.google.android.gms.wearable.MessageEvent
|
||||||
import com.google.android.gms.wearable.WearableListenerService
|
import com.google.android.gms.wearable.WearableListenerService
|
||||||
import com.theeasiestway.opus.Constants
|
import com.theeasiestway.opus.Constants
|
||||||
import com.theeasiestway.opus.Opus
|
import com.theeasiestway.opus.Opus
|
||||||
import decodeAACToPCM
|
import decodeAACToPCM
|
||||||
|
import io.github.nailik.androidresampler.Resampler
|
||||||
|
import io.github.nailik.androidresampler.ResamplerConfiguration
|
||||||
|
import io.github.nailik.androidresampler.data.ResamplerChannel
|
||||||
|
import io.github.nailik.androidresampler.data.ResamplerQuality
|
||||||
import java.io.ByteArrayOutputStream
|
import java.io.ByteArrayOutputStream
|
||||||
import java.nio.ByteBuffer
|
import java.nio.ByteBuffer
|
||||||
import java.nio.ByteOrder
|
import java.nio.ByteOrder
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
|
import java.util.LinkedList
|
||||||
|
import java.util.Queue
|
||||||
|
|
||||||
|
|
||||||
var last_message_time = 0L;
|
var last_message_time = 0L;
|
||||||
@@ -24,6 +33,75 @@ fun ByteArray.toLong(): Long {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun ShortArray.chunked(size: Int): List<ShortArray> {
|
||||||
|
return this.asList()
|
||||||
|
.chunked(size)
|
||||||
|
.map { it.toShortArray() }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun FloatArray.chunked(size: Int): List<FloatArray> {
|
||||||
|
return this.asList()
|
||||||
|
.chunked(size)
|
||||||
|
.map { it.toFloatArray() }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun List<ShortArray>.mergeToShortArray(): ShortArray {
|
||||||
|
// Calculate total size needed
|
||||||
|
val totalSize = this.sumOf { it.size }
|
||||||
|
|
||||||
|
// Create the output array
|
||||||
|
val result = ShortArray(totalSize)
|
||||||
|
|
||||||
|
// Copy each array into the result
|
||||||
|
var position = 0
|
||||||
|
for (array in this) {
|
||||||
|
array.copyInto(result, position)
|
||||||
|
position += array.size
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//var sound_buffer_48000hz = ShortArray(48000*10) // Store last 10 seconds in buffer
|
||||||
|
//var sound_buffer_22050hz = ShortArray(22050*10) // Store last 10 seconds in buffer
|
||||||
|
|
||||||
|
var sound_buffer_48000hz: Queue<ShortArray> = LinkedList<ShortArray>();
|
||||||
|
var sound_buffer_22050hz: Queue<ShortArray> = LinkedList<ShortArray>();
|
||||||
|
|
||||||
|
val configuration = ResamplerConfiguration(
|
||||||
|
quality = ResamplerQuality.BEST,
|
||||||
|
inputChannel = ResamplerChannel.MONO,
|
||||||
|
inputSampleRate = 48000,
|
||||||
|
outputChannel = ResamplerChannel.MONO,
|
||||||
|
outputSampleRate = 22050
|
||||||
|
)
|
||||||
|
val resampler = Resampler(configuration);
|
||||||
|
|
||||||
|
|
||||||
|
fun getPixelValue(i: Int, is_invert: Boolean): Int {
|
||||||
|
return if (is_invert) 255 - i else i
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getPixelColor(i: Int, is_rgb: Boolean, is_invert: Boolean): Int {
|
||||||
|
val pixelValue = getPixelValue(i, is_invert);
|
||||||
|
if (is_rgb) {
|
||||||
|
return toRgb(pixelValue);
|
||||||
|
}
|
||||||
|
return toGrayscale(pixelValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
fun toGrayscale(i: Int): Int {
|
||||||
|
return Color.rgb(i, i, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
fun toRgb(i: Int): Int {
|
||||||
|
return Color.argb(255, i, i, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class MessageListenerService : WearableListenerService() {
|
class MessageListenerService : WearableListenerService() {
|
||||||
|
|
||||||
|
|
||||||
@@ -32,9 +110,10 @@ class MessageListenerService : WearableListenerService() {
|
|||||||
super.onMessageReceived(p0)
|
super.onMessageReceived(p0)
|
||||||
val codec_opus = Opus()
|
val codec_opus = Opus()
|
||||||
codec_opus.decoderInit(Constants.SampleRate._48000(), Constants.Channels.mono())
|
codec_opus.decoderInit(Constants.SampleRate._48000(), Constants.Channels.mono())
|
||||||
// MainActivity
|
|
||||||
Log.w(TAG, "Data recv: " + p0.data.size.toString() + " bytes")
|
Log.w(TAG, "Data recv: " + p0.data.size.toString() + " bytes")
|
||||||
val soundclassifier = MainActivity.soundClassifier
|
val soundclassifier = MainActivity.soundClassifier
|
||||||
|
val soundClassifierSpectrogramBased = MainActivity.soundClassifierSpectrogramBased;
|
||||||
|
|
||||||
if (soundclassifier == null) {
|
if (soundclassifier == null) {
|
||||||
Log.w(TAG, "Have invalid sound classifier")
|
Log.w(TAG, "Have invalid sound classifier")
|
||||||
return
|
return
|
||||||
@@ -42,6 +121,14 @@ class MessageListenerService : WearableListenerService() {
|
|||||||
Log.w(TAG, "Have valid classifier")
|
Log.w(TAG, "Have valid classifier")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (soundClassifierSpectrogramBased == null) {
|
||||||
|
Log.w(TAG, "Have invalid spectrogram classifier")
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, "Have valid spectrogram classifier")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
var tstamp_bytes = p0.data.copyOfRange(0, Long.SIZE_BYTES)
|
var tstamp_bytes = p0.data.copyOfRange(0, Long.SIZE_BYTES)
|
||||||
|
|
||||||
last_message_time = tstamp_bytes.toLong()
|
last_message_time = tstamp_bytes.toLong()
|
||||||
@@ -53,8 +140,7 @@ class MessageListenerService : WearableListenerService() {
|
|||||||
val buffer = ByteBuffer.wrap(audio_bytes_og)
|
val buffer = ByteBuffer.wrap(audio_bytes_og)
|
||||||
val sound_a = ByteArrayOutputStream();
|
val sound_a = ByteArrayOutputStream();
|
||||||
val byteArrayList = mutableListOf<ByteArray>()
|
val byteArrayList = mutableListOf<ByteArray>()
|
||||||
while (buffer.hasRemaining())
|
while (buffer.hasRemaining()) {
|
||||||
{
|
|
||||||
val num_to_read = buffer.get().toInt()
|
val num_to_read = buffer.get().toInt()
|
||||||
val read_this = ByteArray(num_to_read)
|
val read_this = ByteArray(num_to_read)
|
||||||
buffer.get(read_this)
|
buffer.get(read_this)
|
||||||
@@ -62,25 +148,94 @@ class MessageListenerService : WearableListenerService() {
|
|||||||
sound_a.write(decoded);
|
sound_a.write(decoded);
|
||||||
// Log.e(TAG,"Decompressed ${read_this.size} to ${decoded?.size}")
|
// Log.e(TAG,"Decompressed ${read_this.size} to ${decoded?.size}")
|
||||||
}
|
}
|
||||||
val audio_bytes = sound_a.toByteArray()
|
|
||||||
|
|
||||||
codec_opus.decoderRelease();
|
codec_opus.decoderRelease();
|
||||||
|
|
||||||
|
|
||||||
|
val audio_bytes = sound_a.toByteArray()
|
||||||
val short_array = ShortArray(audio_bytes.size / 2)
|
val short_array = ShortArray(audio_bytes.size / 2)
|
||||||
// Log.e(TAG,"Size of short array buffer: "+ decoded?.size.toString());
|
|
||||||
|
|
||||||
ByteBuffer.wrap(audio_bytes).order(
|
ByteBuffer.wrap(audio_bytes).order(
|
||||||
ByteOrder.LITTLE_ENDIAN
|
ByteOrder.LITTLE_ENDIAN
|
||||||
).asShortBuffer().get(short_array)
|
).asShortBuffer().get(short_array)
|
||||||
// Log.e(TAG, pcm_byte_array.sum().toString())
|
|
||||||
|
|
||||||
|
val audio_bytes_resampled = resampler.resample(audio_bytes)
|
||||||
|
|
||||||
|
if (true) {
|
||||||
|
val short_array_resampled = ShortArray(audio_bytes_resampled.size / 2)
|
||||||
|
ByteBuffer.wrap(audio_bytes_resampled).order(
|
||||||
|
ByteOrder.LITTLE_ENDIAN
|
||||||
|
).asShortBuffer().get(short_array_resampled)
|
||||||
|
|
||||||
|
val float_array_resampled = short_array_resampled.map { it.toFloat() }.toFloatArray()
|
||||||
|
val resampled_chunks: List<FloatArray> = float_array_resampled.chunked(512)
|
||||||
|
|
||||||
|
|
||||||
|
val freq_results = mutableListOf<ShortArray>()
|
||||||
|
for (seg in resampled_chunks) {
|
||||||
|
val add_this: ShortArray = soundClassifierSpectrogramBased.generateSpectrogram(seg)
|
||||||
|
freq_results.add(add_this);
|
||||||
|
// val color_int: List<Int> = add_this.map { Color.rgb(it.toInt(), it.toInt(), it.toInt()) }
|
||||||
|
// val color_int: List<Int> = add_this.map { Color.argb(125,125, 200, 5) }
|
||||||
|
// ret_val.add(color_int)
|
||||||
|
}
|
||||||
|
val total_points = freq_results.sumOf { it.size }
|
||||||
|
val freq_array = ShortArray(total_points)
|
||||||
|
var position = 0;
|
||||||
|
for (arr in freq_results) {
|
||||||
|
arr.copyInto(freq_array, position)
|
||||||
|
position += arr.size
|
||||||
|
}
|
||||||
|
|
||||||
|
var bitmap_pixels = IntArray(freq_array.size);
|
||||||
|
val createBitmap =
|
||||||
|
Bitmap.createBitmap(resampled_chunks.size, 128, Bitmap.Config.ARGB_8888);
|
||||||
|
|
||||||
|
// Log.d(TAG,"${freq_array.size.toString()} ${createBitmap.width.toString()} ${createBitmap.height.toString()}")
|
||||||
|
|
||||||
|
val is_rgb = true;
|
||||||
|
var is_invert = false;
|
||||||
|
var height = createBitmap.height
|
||||||
|
var i = 0
|
||||||
|
for (i2 in 0 until height) {
|
||||||
|
val width = createBitmap.getWidth();
|
||||||
|
var i3 = 0;
|
||||||
|
while (i3 < width) {
|
||||||
|
bitmap_pixels[i] =
|
||||||
|
getPixelColor(
|
||||||
|
freq_array[(createBitmap.height * i3) + i2].toInt(),
|
||||||
|
is_rgb,
|
||||||
|
is_invert
|
||||||
|
)
|
||||||
|
i3++;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
createBitmap.setPixels(
|
||||||
|
bitmap_pixels,
|
||||||
|
0,
|
||||||
|
createBitmap.width,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
createBitmap.width,
|
||||||
|
createBitmap.height
|
||||||
|
);
|
||||||
|
soundClassifierSpectrogramBased.run_interpreter(createBitmap)
|
||||||
|
}
|
||||||
|
//if (sound_buffer_48000hz.size >= 1) {
|
||||||
|
var string_send: String = ""
|
||||||
|
if (true) {
|
||||||
|
|
||||||
|
if (true) {
|
||||||
Log.e(TAG, "STARTING SCORING");
|
Log.e(TAG, "STARTING SCORING");
|
||||||
|
|
||||||
|
|
||||||
var string_send: String = ""
|
val array_score = short_array;
|
||||||
var sorted_list = soundclassifier.executeScoring(short_array)
|
var sorted_list = soundclassifier.executeScoring(array_score)
|
||||||
Log.w(TAG, "FINISHED SCORING");
|
Log.w(TAG, "FINISHED SCORING");
|
||||||
Log.w(TAG, "")
|
Log.w(TAG, "")
|
||||||
val threshold = Settings.threshold/100f
|
val threshold = Settings.threshold_waveform / 100f
|
||||||
for (i in 0 until 10) {
|
for (i in 0 until 10) {
|
||||||
val score = sorted_list[i].value
|
val score = sorted_list[i].value
|
||||||
if (score < threshold) {
|
if (score < threshold) {
|
||||||
@@ -89,13 +244,30 @@ class MessageListenerService : WearableListenerService() {
|
|||||||
val index = sorted_list[i].index
|
val index = sorted_list[i].index
|
||||||
val species_name = soundclassifier.labelList[index]
|
val species_name = soundclassifier.labelList[index]
|
||||||
Log.w(TAG, species_name + ", " + score.toString())
|
Log.w(TAG, species_name + ", " + score.toString())
|
||||||
string_send+= species_name
|
string_send += species_name + " ~";
|
||||||
string_send += ','
|
string_send += ','
|
||||||
string_send += score.toString()
|
string_send += score.toString()
|
||||||
string_send += ';'
|
string_send += ';'
|
||||||
}
|
}
|
||||||
MessageSenderFromPhone.sendMessage("/audio", tstamp_bytes + string_send.toByteArray(), this)
|
}
|
||||||
|
if (true) {
|
||||||
|
string_send+=soundClassifierSpectrogramBased.score_string
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Log.d(TAG, string_send)
|
||||||
|
|
||||||
|
|
||||||
|
MessageSenderFromPhone.sendMessage(
|
||||||
|
"/audio",
|
||||||
|
tstamp_bytes + string_send.toByteArray(), b v
|
||||||
|
this
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sound_buffer_22050hz.size >= 129) {
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,11 +1,43 @@
|
|||||||
package com.birdsounds.identify
|
package com.birdsounds.identify
|
||||||
|
|
||||||
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"
|
fun create_file_map(pkg_path: String): Map<String, String> {
|
||||||
var pkg_meta_model_file: String = "2024_08_16/meta-model.tflite"
|
return mapOf(
|
||||||
|
"pkg" to pkg_path,
|
||||||
|
"file" to pkg_path.replace("-", "_").replace("/", "_")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
var threshold: Int = 50;
|
object Settings {
|
||||||
|
|
||||||
|
var threshold_bird_singing: Int = 75
|
||||||
|
var threshold_spectrogram: Int = 10
|
||||||
|
var threshold_waveform: Int = 10;
|
||||||
|
var spectrogram_classify_model_file = create_file_map("from_merlin/sound_id_v40.tflite");
|
||||||
|
var spectrogram_classify_label_file = create_file_map("from_merlin/sound_id_v40.labels");
|
||||||
|
|
||||||
|
var spectrogram_geo_model_file = create_file_map("from_merlin/geo_v40.tflite")
|
||||||
|
var spectrogram_geo_label_file = create_file_map("from_merlin/geo_v40.labels")
|
||||||
|
|
||||||
|
var spectrogram_generate_model_file =
|
||||||
|
create_file_map("from_merlin/msid685v4_spectrogram.tflite");
|
||||||
|
|
||||||
|
var waveform_classify_model_file = create_file_map("2024_08_16/audio-model.tflite");
|
||||||
|
var waveform_meta_model_file = create_file_map("2024_08_16/meta-model.tflite");
|
||||||
|
|
||||||
|
var short_to_long_map = create_file_map("from_merlin/short_to_long.map");
|
||||||
|
|
||||||
|
|
||||||
|
val all_files = listOf(
|
||||||
|
spectrogram_classify_model_file,
|
||||||
|
spectrogram_classify_label_file,
|
||||||
|
spectrogram_geo_model_file,
|
||||||
|
spectrogram_geo_label_file,
|
||||||
|
spectrogram_generate_model_file,
|
||||||
|
waveform_classify_model_file,
|
||||||
|
waveform_meta_model_file,
|
||||||
|
short_to_long_map
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import android.os.SystemClock
|
|||||||
import android.preference.PreferenceManager
|
import android.preference.PreferenceManager
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import org.tensorflow.lite.Interpreter
|
import org.tensorflow.lite.Interpreter
|
||||||
|
|
||||||
import java.io.BufferedReader
|
import java.io.BufferedReader
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
@@ -99,9 +100,7 @@ class SoundClassifier(
|
|||||||
private lateinit var metaInputBuffer: FloatBuffer
|
private lateinit var metaInputBuffer: FloatBuffer
|
||||||
private var model_ready = false;
|
private var model_ready = false;
|
||||||
init {;
|
init {;
|
||||||
setupDecoder(context)
|
|
||||||
loadLabels(context)
|
loadLabels(context)
|
||||||
loadAssetList(context)
|
|
||||||
setupInterpreter(context)
|
setupInterpreter(context)
|
||||||
setupMetaInterpreter(context)
|
setupMetaInterpreter(context)
|
||||||
warmUpModel()
|
warmUpModel()
|
||||||
@@ -112,49 +111,12 @@ class SoundClassifier(
|
|||||||
{
|
{
|
||||||
return this.model_ready;
|
return this.model_ready;
|
||||||
}
|
}
|
||||||
private fun setupDecoder(context: Context) {
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Retrieve asset list from "asset_list" file */
|
|
||||||
private fun loadAssetList(context: Context) {
|
|
||||||
|
|
||||||
try {
|
|
||||||
val reader =
|
|
||||||
BufferedReader(InputStreamReader(context.assets.open(options.assetFile)))
|
|
||||||
val wordList = mutableListOf<String>()
|
|
||||||
reader.useLines { lines ->
|
|
||||||
lines.forEach {
|
|
||||||
wordList.add(it.trim())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assetList = wordList.map { it }
|
|
||||||
} catch (e: IOException) {
|
|
||||||
Log.e(TAG, "Failed to read labels ${options.assetFile}: ${e.message}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Retrieve labels from "labels.txt" file */
|
/** Retrieve labels from "labels.txt" file */
|
||||||
private fun loadLabels(context: Context) {
|
private fun loadLabels(context: Context) {
|
||||||
val localeList = context.resources.configuration.locales
|
var filename = "labels_en.txt"
|
||||||
val language = localeList.get(0).language
|
|
||||||
var filename = options.labelsBase + "_${language}.txt"
|
|
||||||
|
|
||||||
//Check if file exists
|
|
||||||
val assetManager = context.assets // Replace 'assets' with actual AssetManager instance
|
|
||||||
try {
|
|
||||||
val mapList = assetManager.list("")?.toMutableList()
|
|
||||||
|
|
||||||
if (mapList != null) {
|
|
||||||
if (!mapList.contains(filename)) {
|
|
||||||
filename = options.labelsBase + "_en.txt"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (ex: IOException) {
|
|
||||||
ex.printStackTrace()
|
|
||||||
filename = options.labelsBase + "_en.txt"
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.i(TAG, filename)
|
|
||||||
try {
|
try {
|
||||||
val reader =
|
val reader =
|
||||||
BufferedReader(InputStreamReader(context.assets.open(filename)))
|
BufferedReader(InputStreamReader(context.assets.open(filename)))
|
||||||
@@ -179,7 +141,7 @@ class SoundClassifier(
|
|||||||
context.getDir(
|
context.getDir(
|
||||||
"",
|
"",
|
||||||
Context.MODE_PRIVATE
|
Context.MODE_PRIVATE
|
||||||
).absolutePath + "/" + settings.local_model_file;
|
).absolutePath + "/" + settings.waveform_classify_model_file["file"].toString();
|
||||||
|
|
||||||
Log.i(TAG, "Trying to create TFLite buffer from $modelFilePath")
|
Log.i(TAG, "Trying to create TFLite buffer from $modelFilePath")
|
||||||
val modelFile = File(modelFilePath)
|
val modelFile = File(modelFilePath)
|
||||||
@@ -190,6 +152,9 @@ class SoundClassifier(
|
|||||||
Log.i(TAG, "Done creating TFLite buffer from $modelFilePath")
|
Log.i(TAG, "Done creating TFLite buffer from $modelFilePath")
|
||||||
|
|
||||||
interpreter = Interpreter(tfliteBuffer, Interpreter.Options())
|
interpreter = Interpreter(tfliteBuffer, Interpreter.Options())
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
Log.e(TAG, "Failed to load TFLite model - ${e.message}")
|
Log.e(TAG, "Failed to load TFLite model - ${e.message}")
|
||||||
return
|
return
|
||||||
@@ -222,7 +187,7 @@ class SoundClassifier(
|
|||||||
val metaModelFilePath = context.getDir(
|
val metaModelFilePath = context.getDir(
|
||||||
"",
|
"",
|
||||||
Context.MODE_PRIVATE
|
Context.MODE_PRIVATE
|
||||||
).absolutePath + "/" + settings.local_meta_model_file
|
).absolutePath + "/" + settings.waveform_meta_model_file["file"].toString();
|
||||||
Log.i(TAG, "Trying to create TFLite buffer from $metaModelFilePath")
|
Log.i(TAG, "Trying to create TFLite buffer from $metaModelFilePath")
|
||||||
val metaModelFile = File(metaModelFilePath)
|
val metaModelFile = File(metaModelFilePath)
|
||||||
val tfliteBuffer: ByteBuffer =
|
val tfliteBuffer: ByteBuffer =
|
||||||
@@ -270,6 +235,7 @@ class SoundClassifier(
|
|||||||
metaInputBuffer.put(1, lon)
|
metaInputBuffer.put(1, lon)
|
||||||
metaInputBuffer.put(2, weekMeta.toFloat())
|
metaInputBuffer.put(2, weekMeta.toFloat())
|
||||||
metaInputBuffer.rewind() // Reset position to beginning of buffer
|
metaInputBuffer.rewind() // Reset position to beginning of buffer
|
||||||
|
|
||||||
val metaOutputBuffer = FloatBuffer.allocate(metaModelNumClasses)
|
val metaOutputBuffer = FloatBuffer.allocate(metaModelNumClasses)
|
||||||
metaOutputBuffer.rewind()
|
metaOutputBuffer.rewind()
|
||||||
meta_interpreter.run(metaInputBuffer, metaOutputBuffer)
|
meta_interpreter.run(metaInputBuffer, metaOutputBuffer)
|
||||||
|
|||||||
+342
@@ -0,0 +1,342 @@
|
|||||||
|
package com.birdsounds.identify
|
||||||
|
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.graphics.Bitmap
|
||||||
|
import android.location.Location
|
||||||
|
import android.media.Image
|
||||||
|
import android.util.Log
|
||||||
|
import android.widget.ImageView
|
||||||
|
import org.apache.commons.math3.stat.descriptive.summary.Sum
|
||||||
|
import org.tensorflow.lite.Interpreter
|
||||||
|
import org.tensorflow.lite.support.image.ImageProcessor
|
||||||
|
import org.tensorflow.lite.support.image.TensorImage
|
||||||
|
import org.tensorflow.lite.support.image.ops.ResizeOp
|
||||||
|
import org.tensorflow.lite.support.label.Category
|
||||||
|
import org.tensorflow.lite.support.tensorbuffer.TensorBuffer
|
||||||
|
import org.tensorflow.lite.task.core.BaseOptions
|
||||||
|
import org.tensorflow.lite.task.vision.classifier.Classifications
|
||||||
|
import org.tensorflow.lite.task.vision.classifier.ImageClassifier
|
||||||
|
import org.tensorflow.lite.task.vision.classifier.ImageClassifier.ImageClassifierOptions
|
||||||
|
import java.io.File
|
||||||
|
import java.nio.ByteBuffer
|
||||||
|
import java.nio.FloatBuffer
|
||||||
|
import java.nio.channels.FileChannel
|
||||||
|
import java.nio.file.StandardOpenOption
|
||||||
|
import java.time.LocalDate
|
||||||
|
import java.time.ZoneId
|
||||||
|
import java.util.Calendar
|
||||||
|
import java.util.Calendar.WEEK_OF_YEAR
|
||||||
|
import java.util.Date
|
||||||
|
|
||||||
|
|
||||||
|
var local_species_spectrogram = "";
|
||||||
|
|
||||||
|
|
||||||
|
fun ByteBuffer.toUByteArray(): UByteArray {
|
||||||
|
// Get remaining bytes in the buffer
|
||||||
|
val bytes = ByteArray(this.remaining())
|
||||||
|
|
||||||
|
// Copy bytes from the buffer
|
||||||
|
this.get(bytes)
|
||||||
|
|
||||||
|
// Convert to UByteArray
|
||||||
|
return bytes.toUByteArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
class SoundClassifierSpectrogramBased(context_in: MainActivity) {
|
||||||
|
private var c_threshold: Float = 0.0f;
|
||||||
|
private var model_ready: Boolean = false;
|
||||||
|
private var context: MainActivity = context_in;
|
||||||
|
var norm_factor: Float = (1 shl 15).toFloat();
|
||||||
|
var score_string = "";
|
||||||
|
var image_resize_op =
|
||||||
|
ImageProcessor.Builder().add(ResizeOp(128, 512, ResizeOp.ResizeMethod.BILINEAR)).build();
|
||||||
|
private lateinit var interpreter_spectrogram: Interpreter;
|
||||||
|
private var spec_inp_dim: IntArray = intArrayOf(); // [512]
|
||||||
|
private var spec_out_dim: IntArray = intArrayOf(); // [128,1]
|
||||||
|
private lateinit var specInputBuffer: TensorBuffer;
|
||||||
|
private lateinit var specOutputBuffer: TensorBuffer;
|
||||||
|
|
||||||
|
|
||||||
|
private lateinit var interpreter_geo: Interpreter;
|
||||||
|
private var geo_inp_dim: IntArray = intArrayOf();
|
||||||
|
private var geo_out_dim: IntArray = intArrayOf();
|
||||||
|
|
||||||
|
private lateinit var interpreter_classify: Interpreter;
|
||||||
|
private lateinit var interpreter_imc: ImageClassifier;
|
||||||
|
private var classify_inp_dim: IntArray = intArrayOf();
|
||||||
|
private var classify_out_dim: IntArray = intArrayOf();
|
||||||
|
private lateinit var classify_input_buffer: FloatBuffer;
|
||||||
|
private lateinit var classify_input_array: FloatArray;
|
||||||
|
|
||||||
|
private lateinit var classify_output_buffer: FloatBuffer;
|
||||||
|
private lateinit var classify_output_array: FloatArray;
|
||||||
|
|
||||||
|
private lateinit var labels_geo: Array<String>
|
||||||
|
private lateinit var labels_classify: Array<String>
|
||||||
|
|
||||||
|
private lateinit var geo_input: FloatBuffer
|
||||||
|
private lateinit var geo_output: FloatArray
|
||||||
|
|
||||||
|
private var geo_prob_map = mutableMapOf<String, Float>();
|
||||||
|
var short_long_map = mutableMapOf<String, String>()
|
||||||
|
|
||||||
|
|
||||||
|
init {;
|
||||||
|
loadMetaLabels()
|
||||||
|
loadLabels()
|
||||||
|
loadShortLongMap()
|
||||||
|
setupSpectrogramGenerator()
|
||||||
|
setupInterpreter()
|
||||||
|
setupMetaInterpreter()
|
||||||
|
this.model_ready = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun setupSpectrogramGenerator() {
|
||||||
|
interpreter_spectrogram =
|
||||||
|
load_interpreter(Settings.spectrogram_generate_model_file["file"].toString())
|
||||||
|
|
||||||
|
var spec_inp = this.interpreter_spectrogram.getInputTensor(/* inputIndex = */ 0)
|
||||||
|
specInputBuffer = TensorBuffer.createFixedSize(spec_inp.shape(), spec_inp.dataType())
|
||||||
|
|
||||||
|
|
||||||
|
var spec_out = this.interpreter_spectrogram.getOutputTensor(/* inputIndex = */ 0)
|
||||||
|
specOutputBuffer = TensorBuffer.createFixedSize(spec_out.shape(), spec_out.dataType())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun generateSpectrogram(input_array: FloatArray): ShortArray {
|
||||||
|
specInputBuffer.buffer.clear()
|
||||||
|
specOutputBuffer.buffer.clear()
|
||||||
|
|
||||||
|
|
||||||
|
val array_to_load = input_array.copyOf(512).map { it / norm_factor }.toFloatArray()
|
||||||
|
specInputBuffer.loadArray(array_to_load)
|
||||||
|
// Log.d(TAG, "Loaded Array: ${array_to_load.min()} ${array_to_load.max()}")
|
||||||
|
|
||||||
|
interpreter_spectrogram.run(specInputBuffer.buffer, specOutputBuffer.buffer);
|
||||||
|
|
||||||
|
val output = specOutputBuffer.intArray.map { it.toShort() }.toShortArray()
|
||||||
|
|
||||||
|
return output
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupMetaInterpreter() {
|
||||||
|
interpreter_geo = load_interpreter(Settings.spectrogram_geo_model_file["file"].toString())
|
||||||
|
|
||||||
|
geo_inp_dim = this.interpreter_geo.getInputTensor(0).shape()
|
||||||
|
Log.i(TAG, "TFLite model input shape: ${geo_inp_dim.contentToString()}")
|
||||||
|
geo_input = FloatBuffer.allocate(3);
|
||||||
|
|
||||||
|
|
||||||
|
geo_out_dim = this.interpreter_geo.getOutputTensor(0).shape()
|
||||||
|
Log.i(TAG, "TFLite model input shape: ${geo_out_dim.contentToString()}")
|
||||||
|
geo_output = FloatArray(geo_out_dim[0]) { 1f };
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupInterpreter() {
|
||||||
|
|
||||||
|
|
||||||
|
interpreter_classify =
|
||||||
|
load_interpreter(Settings.spectrogram_classify_model_file["file"].toString())
|
||||||
|
|
||||||
|
interpreter_imc = ImageClassifier.createFromFileAndOptions(
|
||||||
|
File(get_file_path(Settings.spectrogram_classify_model_file["file"].toString())),
|
||||||
|
ImageClassifierOptions.builder().setBaseOptions(
|
||||||
|
BaseOptions.builder().setNumThreads(2).build()
|
||||||
|
).setMaxResults(10).build()
|
||||||
|
)
|
||||||
|
|
||||||
|
classify_inp_dim = this.interpreter_classify.getInputTensor(0).shape();
|
||||||
|
Log.i(TAG, "TFLite model input shape: ${classify_inp_dim.contentToString()}")
|
||||||
|
|
||||||
|
classify_out_dim = this.interpreter_classify.getOutputTensor(0).shape();
|
||||||
|
Log.i(TAG, "TFLite model output shape: ${classify_out_dim.contentToString()}")
|
||||||
|
|
||||||
|
|
||||||
|
val n_floats = classify_inp_dim.reduce(Int::times)
|
||||||
|
classify_input_buffer = FloatBuffer.allocate(n_floats)
|
||||||
|
classify_input_array = FloatArray(n_floats);
|
||||||
|
|
||||||
|
classify_output_buffer = FloatBuffer.allocate(classify_out_dim[1]);
|
||||||
|
classify_output_array = FloatArray(classify_out_dim[1]) { 0f };
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun loadMetaLabels() {
|
||||||
|
labels_geo = load_labels(Settings.spectrogram_geo_label_file["file"].toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadLabels() {
|
||||||
|
labels_classify = load_labels(Settings.spectrogram_classify_label_file["file"].toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadShortLongMap() {
|
||||||
|
var labx = load_labels(Settings.short_to_long_map["file"].toString());
|
||||||
|
for (x in labx) {
|
||||||
|
var ff = x.split(",")
|
||||||
|
short_long_map[ff[0]] = ff[1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun geo_prob_clamp(i: Float): Float {
|
||||||
|
return when {
|
||||||
|
i > 0.01 -> 1.0f
|
||||||
|
i > 0.008 -> 0.8f
|
||||||
|
i > 0.001 -> 0.5f
|
||||||
|
else -> 0.0f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun runMetaInterpreter(location: Location) {
|
||||||
|
|
||||||
|
var localDate: LocalDate = LocalDate.now()
|
||||||
|
val date: Date = Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant())
|
||||||
|
val calendar = Calendar.getInstance()
|
||||||
|
calendar.time = date
|
||||||
|
|
||||||
|
|
||||||
|
val lon = this.asFloatArray(location.longitude.toFloat());
|
||||||
|
val lat = this.asFloatArray(location.latitude.toFloat());
|
||||||
|
val week_of_year = this.asFloatArray(calendar.get(WEEK_OF_YEAR).coerceAtMost(52).toFloat())
|
||||||
|
|
||||||
|
val input_array = arrayOf(lon, week_of_year, lat)
|
||||||
|
|
||||||
|
|
||||||
|
var outputTensor = interpreter_geo.getOutputTensor(0);
|
||||||
|
var fixedTensor =
|
||||||
|
TensorBuffer.createFixedSize(outputTensor.shape(), outputTensor.dataType())
|
||||||
|
|
||||||
|
val outputs: MutableMap<Int, Any> = mutableMapOf()
|
||||||
|
outputs[0] = fixedTensor.buffer;
|
||||||
|
interpreter_geo.runForMultipleInputsOutputs(input_array, outputs)
|
||||||
|
|
||||||
|
fixedTensor.floatArray.zip(labels_geo).forEach { (score, label) ->
|
||||||
|
geo_prob_map[label] = geo_prob_clamp(score)
|
||||||
|
}
|
||||||
|
|
||||||
|
val cloned = fixedTensor.floatArray.clone()
|
||||||
|
val sorted_indices = cloned.withIndex().sortedByDescending { it.value }.map { it.index }
|
||||||
|
|
||||||
|
local_species_spectrogram = "Most likely:\n"
|
||||||
|
for (i in 0..10) {
|
||||||
|
var c_species = labels_geo[sorted_indices[i]]
|
||||||
|
local_species_spectrogram += " " + short_long_map.get(c_species).toString();
|
||||||
|
local_species_spectrogram += "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.d(TAG, "Likely: " + local_species_spectrogram)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun is_model_ready(): Boolean {
|
||||||
|
return this.model_ready;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun get_file_path(file: String): String {
|
||||||
|
return this.context.getDir(
|
||||||
|
"", Context.MODE_PRIVATE
|
||||||
|
).absolutePath + "/" + file.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun load_labels(file: String): Array<String> {
|
||||||
|
val label_file_path = this.get_file_path(file);
|
||||||
|
val labelFile = File(label_file_path)
|
||||||
|
return labelFile.readLines().toTypedArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun load_interpreter(file: String): Interpreter {
|
||||||
|
|
||||||
|
val modelFilePath = get_file_path(file);
|
||||||
|
|
||||||
|
val i = Log.i(TAG, "Trying to create TFLite buffer from $modelFilePath")
|
||||||
|
val modelFile = File(modelFilePath)
|
||||||
|
val tfliteBuffer: ByteBuffer =
|
||||||
|
FileChannel.open(modelFile.toPath(), StandardOpenOption.READ).use { channel ->
|
||||||
|
channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size())
|
||||||
|
}
|
||||||
|
Log.i(TAG, "Done creating TFLite buffer from $modelFilePath")
|
||||||
|
|
||||||
|
return Interpreter(tfliteBuffer, Interpreter.Options())
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun asFloatArray(d: Float): FloatArray {
|
||||||
|
return floatArrayOf(d)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun run_interpreter(createBitmap: Bitmap) {
|
||||||
|
val fromBitmap = TensorImage.fromBitmap(createBitmap);
|
||||||
|
val out: TensorImage = image_resize_op.process(fromBitmap);
|
||||||
|
|
||||||
|
|
||||||
|
this.context.runOnUiThread {
|
||||||
|
this.context.set_image_view(out.bitmap)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var classify: MutableList<Classifications> = interpreter_imc.classify(out)
|
||||||
|
|
||||||
|
var redone_scores = FloatArray(classify_output_array.size) { 0f };
|
||||||
|
var cats: MutableList<Category>? = classify.get(0).categories;
|
||||||
|
|
||||||
|
|
||||||
|
score_string = "";
|
||||||
|
cats?.forEach {
|
||||||
|
val c_name = it.label
|
||||||
|
val index = it.index
|
||||||
|
val score = it.score
|
||||||
|
var cur_score = 0f;
|
||||||
|
if (geo_prob_map.containsKey(c_name)) {
|
||||||
|
cur_score = score * geo_prob_map[c_name]!!
|
||||||
|
}
|
||||||
|
if (c_name == "bird1") {
|
||||||
|
cur_score = score
|
||||||
|
c_threshold = Settings.threshold_bird_singing / 100f;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
c_threshold = Settings.threshold_spectrogram / 100f
|
||||||
|
}
|
||||||
|
redone_scores[index] = cur_score
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (cur_score > c_threshold) {
|
||||||
|
var use_name = "";
|
||||||
|
if (short_long_map.containsKey(c_name)) {
|
||||||
|
use_name = short_long_map[c_name].toString()
|
||||||
|
} else {
|
||||||
|
use_name = c_name
|
||||||
|
};
|
||||||
|
|
||||||
|
score_string += use_name + "_" + use_name;
|
||||||
|
score_string += ","
|
||||||
|
score_string += cur_score.toString();
|
||||||
|
score_string += ';'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// val outList = redone_scores.withIndex().sortedByDescending { it -> it.value };
|
||||||
|
// for (i in 0 until 6) {
|
||||||
|
// var cs: IndexedValue<Float> = outList[i]
|
||||||
|
// Log.d(TAG, i.toString() + " " + labels_classify[cs.index] + " " + cs.value.toString())
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Log.d(TAG, classify_output_buffer.toString())
|
||||||
|
// Log.d(TAG, classify_output_array.sum().toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -12,14 +12,12 @@
|
|||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<TextView
|
<ImageView
|
||||||
android:id="@+id/textView"
|
android:id="@+id/imageView"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="100dp"
|
||||||
android:paddingLeft="5dp"
|
android:background="#ffffff"
|
||||||
android:paddingTop="15dp"
|
android:padding="5dp" />
|
||||||
android:text="Threshold:"
|
|
||||||
android:textSize="34sp" />
|
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@@ -27,23 +25,156 @@
|
|||||||
android:orientation="horizontal">
|
android:orientation="horizontal">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/threshold_value_text"
|
android:id="@+id/editTextText2"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="35dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:clipChildren="false"
|
||||||
|
android:clipToOutline="false"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:ems="10"
|
||||||
|
android:gravity="center_horizontal|center_vertical"
|
||||||
|
android:inputType="text"
|
||||||
|
android:padding="0dp"
|
||||||
|
android:paddingHorizontal="0dp"
|
||||||
|
android:paddingVertical="0dp"
|
||||||
|
android:paddingStart="0dp"
|
||||||
|
android:paddingLeft="0dp"
|
||||||
|
android:paddingTop="0dp"
|
||||||
|
android:paddingEnd="0dp"
|
||||||
|
android:paddingRight="0dp"
|
||||||
|
android:paddingBottom="0dp"
|
||||||
|
android:rotation="270"
|
||||||
|
android:singleLine="false"
|
||||||
|
android:text="Merlin"
|
||||||
|
android:textAppearance="@style/TextAppearance.AppCompat.Small"
|
||||||
|
android:textSize="10sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/threshold_value_text_spectrogram"
|
||||||
|
android:layout_width="76dp"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_weight="4"
|
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:text="Threshold:"
|
android:text="Threshold:"
|
||||||
android:textAlignment="center"
|
android:textAlignment="center"
|
||||||
android:textSize="28sp" />
|
android:textSize="28sp" />
|
||||||
|
|
||||||
<SeekBar
|
<SeekBar
|
||||||
android:id="@+id/threshold_set_scale_bar"
|
android:id="@+id/threshold_set_scale_bar_spectrogram"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_gravity="center|fill_horizontal|fill_vertical"
|
||||||
|
android:max="100"
|
||||||
|
android:min="0"
|
||||||
|
android:progress="20" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="55dp"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/editTextText5"
|
||||||
|
android:layout_width="35dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:clipChildren="false"
|
||||||
|
android:clipToOutline="false"
|
||||||
|
android:clipToPadding="true"
|
||||||
|
android:drawablePadding="0dp"
|
||||||
|
android:ems="10"
|
||||||
|
android:gravity="center_horizontal|center_vertical"
|
||||||
|
android:includeFontPadding="false"
|
||||||
|
android:inputType="text"
|
||||||
|
android:padding="0dp"
|
||||||
|
android:paddingHorizontal="0dp"
|
||||||
|
android:paddingVertical="0dp"
|
||||||
|
android:paddingStart="0dp"
|
||||||
|
android:paddingLeft="0dp"
|
||||||
|
android:paddingTop="0dp"
|
||||||
|
android:paddingEnd="0dp"
|
||||||
|
android:paddingRight="0dp"
|
||||||
|
android:paddingBottom="0dp"
|
||||||
|
android:rotation="270"
|
||||||
|
android:singleLine="false"
|
||||||
|
android:text="Regular"
|
||||||
|
android:textAppearance="@style/TextAppearance.AppCompat.Small"
|
||||||
|
android:textSize="10sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/threshold_value_text_waveform"
|
||||||
|
android:layout_width="76dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="Threshold:"
|
||||||
|
android:textAlignment="center"
|
||||||
|
android:textSize="28sp" />
|
||||||
|
|
||||||
|
<SeekBar
|
||||||
|
android:id="@+id/threshold_set_scale_bar_waveform"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_gravity="center|fill_horizontal|fill_vertical"
|
android:layout_gravity="center|fill_horizontal|fill_vertical"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:max="100"
|
android:max="100"
|
||||||
android:min="0" />
|
android:min="0"
|
||||||
|
android:progress="20" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="55dp"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/editTextText4"
|
||||||
|
android:layout_width="35dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:clipChildren="false"
|
||||||
|
android:clipToOutline="false"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:ems="10"
|
||||||
|
android:gravity="center_horizontal|center_vertical"
|
||||||
|
android:inputType="textMultiLine"
|
||||||
|
android:lines="2"
|
||||||
|
android:maxLines="2"
|
||||||
|
android:minLines="2"
|
||||||
|
android:padding="0dp"
|
||||||
|
android:paddingHorizontal="0dp"
|
||||||
|
android:paddingVertical="0dp"
|
||||||
|
android:paddingStart="0dp"
|
||||||
|
android:paddingLeft="0dp"
|
||||||
|
android:paddingTop="0dp"
|
||||||
|
android:paddingEnd="0dp"
|
||||||
|
android:paddingRight="0dp"
|
||||||
|
android:paddingBottom="0dp"
|
||||||
|
android:rotation="270"
|
||||||
|
android:singleLine="false"
|
||||||
|
android:text="Bird\nHeard"
|
||||||
|
android:textAppearance="@style/TextAppearance.AppCompat.Small"
|
||||||
|
android:textSize="10sp" />
|
||||||
|
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/threshold_value_text_birdheard"
|
||||||
|
android:layout_width="76dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="Threshold:"
|
||||||
|
android:textAlignment="center"
|
||||||
|
android:textSize="28sp" />
|
||||||
|
|
||||||
|
<SeekBar
|
||||||
|
android:id="@+id/threshold_set_scale_bar_birdheard"
|
||||||
|
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"
|
||||||
|
android:progress="80" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 0 B |
@@ -1,6 +0,0 @@
|
|||||||
<resources>
|
|
||||||
<string name="app_name">identify</string>
|
|
||||||
<string name="title_activity_download">DownloadActivity</string>
|
|
||||||
<string name="error_download">Download Error</string>
|
|
||||||
<string name="samples_zero">Samples are all zero</string>
|
|
||||||
</resources>
|
|
||||||
@@ -61,7 +61,8 @@ fun FlashingText(
|
|||||||
modifier = modifier
|
modifier = modifier
|
||||||
.background(color = backgroundColor),
|
.background(color = backgroundColor),
|
||||||
textDecor = textDecor,
|
textDecor = textDecor,
|
||||||
fontStyle = fontStyle
|
fontStyle = fontStyle,
|
||||||
|
key = key
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,6 +73,7 @@ fun AutoSizedText(
|
|||||||
textAlign: TextAlign = TextAlign.Center,
|
textAlign: TextAlign = TextAlign.Center,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
textDecor: TextDecoration = TextDecoration.None,
|
textDecor: TextDecoration = TextDecoration.None,
|
||||||
|
key: Long = 0,
|
||||||
minFontSize: TextUnit = 5.sp,
|
minFontSize: TextUnit = 5.sp,
|
||||||
targetFontSize: TextUnit = 24.sp,
|
targetFontSize: TextUnit = 24.sp,
|
||||||
fontWeight: FontWeight = FontWeight.Normal,
|
fontWeight: FontWeight = FontWeight.Normal,
|
||||||
@@ -115,7 +117,9 @@ fun AutoSizedText(
|
|||||||
|
|
||||||
// Convert the best font size back to sp and use it
|
// Convert the best font size back to sp and use it
|
||||||
// val bestFontSize = with(density) { bestFontSizePx.toSp() }
|
// val bestFontSize = with(density) { bestFontSizePx.toSp() }
|
||||||
|
LaunchedEffect(key1 = key) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
Text(
|
Text(
|
||||||
text = display_text,
|
text = display_text,
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import android.app.Activity
|
|||||||
import android.app.Notification
|
import android.app.Notification
|
||||||
import android.app.NotificationManager
|
import android.app.NotificationManager
|
||||||
import android.app.PendingIntent
|
import android.app.PendingIntent
|
||||||
import android.app.Service
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.ContextWrapper
|
import android.content.ContextWrapper
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
@@ -41,7 +40,6 @@ import androidx.compose.ui.platform.LocalContext
|
|||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.core.app.NotificationCompat
|
import androidx.core.app.NotificationCompat
|
||||||
import androidx.core.app.ServiceCompat.startForeground
|
|
||||||
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import androidx.navigation.NavHostController
|
import androidx.navigation.NavHostController
|
||||||
@@ -53,7 +51,6 @@ import androidx.wear.compose.navigation.SwipeDismissableNavHost
|
|||||||
import androidx.wear.compose.navigation.composable
|
import androidx.wear.compose.navigation.composable
|
||||||
import androidx.wear.compose.navigation.rememberSwipeDismissableNavController
|
import androidx.wear.compose.navigation.rememberSwipeDismissableNavController
|
||||||
import androidx.wear.ongoing.OngoingActivity
|
import androidx.wear.ongoing.OngoingActivity
|
||||||
import com.birdsounds.identify.R
|
|
||||||
import com.birdsounds.identify.presentation.theme.IdentifyTheme
|
import com.birdsounds.identify.presentation.theme.IdentifyTheme
|
||||||
import com.google.android.horologist.annotations.ExperimentalHorologistApi
|
import com.google.android.horologist.annotations.ExperimentalHorologistApi
|
||||||
import com.google.android.horologist.audio.ui.VolumeViewModel
|
import com.google.android.horologist.audio.ui.VolumeViewModel
|
||||||
@@ -166,7 +163,7 @@ fun WearApp() {
|
|||||||
) {
|
) {
|
||||||
composable("species_list") {
|
composable("species_list") {
|
||||||
ScreenScaffold {
|
ScreenScaffold {
|
||||||
SpeciesListView(context = context, appState = mainState.appState)
|
SpeciesListView(context = context, appState = mainState.appState, birdHeardState = mainState.birdHeardState, mainState = mainState )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,8 +20,14 @@ import java.time.Duration
|
|||||||
|
|
||||||
class MainState(private val activity: Activity, private val requestPermission: () -> Unit) {
|
class MainState(private val activity: Activity, private val requestPermission: () -> Unit) {
|
||||||
private val playbackStateMutatorMutex = MutatorMutex()
|
private val playbackStateMutatorMutex = MutatorMutex()
|
||||||
|
|
||||||
var appState by mutableStateOf<AppState>(AppState.Ready)
|
var appState by mutableStateOf<AppState>(AppState.Ready)
|
||||||
private set
|
|
||||||
|
|
||||||
|
var birdHeardState by mutableStateOf<BirdHeardState>(BirdHeardState.NotHearing)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var recordingProgress by mutableStateOf(0f)
|
var recordingProgress by mutableStateOf(0f)
|
||||||
private set
|
private set
|
||||||
var isPermissionDenied by mutableStateOf(false)
|
var isPermissionDenied by mutableStateOf(false)
|
||||||
@@ -82,6 +88,13 @@ sealed class AppState {
|
|||||||
object Recording : AppState()
|
object Recording : AppState()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sealed class BirdHeardState {
|
||||||
|
object Hearing : BirdHeardState()
|
||||||
|
object NotHearing : BirdHeardState()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@RequiresPermission(Manifest.permission.RECORD_AUDIO)
|
@RequiresPermission(Manifest.permission.RECORD_AUDIO)
|
||||||
private suspend fun record(activity: (Activity),soundRecorder: SoundRecorder,
|
private suspend fun record(activity: (Activity),soundRecorder: SoundRecorder,
|
||||||
setProgress: (progress: Float) -> Unit,
|
setProgress: (progress: Float) -> Unit,
|
||||||
|
|||||||
+16
-4
@@ -1,6 +1,7 @@
|
|||||||
package com.birdsounds.identify.presentation
|
package com.birdsounds.identify.presentation
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.util.Log
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import com.google.android.gms.wearable.MessageEvent
|
import com.google.android.gms.wearable.MessageEvent
|
||||||
import com.google.android.gms.wearable.WearableListenerService
|
import com.google.android.gms.wearable.WearableListenerService
|
||||||
@@ -16,6 +17,7 @@ class MessageListenerService : WearableListenerService() {
|
|||||||
{
|
{
|
||||||
this_context = context;
|
this_context = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onMessageReceived(p0: MessageEvent) {
|
override fun onMessageReceived(p0: MessageEvent) {
|
||||||
super.onMessageReceived(p0)
|
super.onMessageReceived(p0)
|
||||||
val t_scored = ByteBuffer.wrap(p0.data).getLong()
|
val t_scored = ByteBuffer.wrap(p0.data).getLong()
|
||||||
@@ -27,12 +29,14 @@ class MessageListenerService : WearableListenerService() {
|
|||||||
var do_vibrate = false;
|
var do_vibrate = false;
|
||||||
var did_add = false;
|
var did_add = false;
|
||||||
var new_entries = 0;
|
var new_entries = 0;
|
||||||
SpeciesList.clear_new_flags();
|
// bird_heard_state = BirdHeardState.NotHearing
|
||||||
|
SpeciesList.bird_not_heard();
|
||||||
list_strings.map({
|
list_strings.map({
|
||||||
var split_str = it.split(',')
|
var split_str = it.split(',')
|
||||||
if (split_str.size == 2) {
|
if ((split_str.size == 2) && !(split_str[0]=="bird1_bird1")) {
|
||||||
|
Log.d(TAG, split_str[0] + "${split_str[0] == "bird1"}")
|
||||||
new_entries+=1;
|
new_entries+=1;
|
||||||
var out = AScore(split_str[0], split_str[1].toFloat(), t_scored);
|
var out = AScore(split_str[0], split_str[1].toFloat(), t_scored, true);
|
||||||
out.new_entry = true;
|
out.new_entry = true;
|
||||||
|
|
||||||
if (SpeciesList.add_observation(out))
|
if (SpeciesList.add_observation(out))
|
||||||
@@ -41,8 +45,16 @@ class MessageListenerService : WearableListenerService() {
|
|||||||
}
|
}
|
||||||
do_vibrate = true;
|
do_vibrate = true;
|
||||||
}
|
}
|
||||||
|
else if (split_str[0]=="bird1_bird1") {
|
||||||
|
Log.d(TAG, "I AM HEARING A BIRD")
|
||||||
|
SpeciesList.bird_heard();
|
||||||
|
// bird_heard_state = BirdHeardState.Hearing
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
// SpeciesList.set_num_new_entries(new_entries);
|
SpeciesList.num_new_entries = new_entries;
|
||||||
|
|
||||||
// SpeciesList.insert_new_entry_spacer(new_entries);
|
// SpeciesList.insert_new_entry_spacer(new_entries);
|
||||||
if (did_add) {
|
if (did_add) {
|
||||||
vibrateDouble(this, 100, 250, 100);
|
vibrateDouble(this, 100, 250, 100);
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import androidx.compose.runtime.Composable
|
|||||||
import androidx.compose.runtime.DisposableEffect
|
import androidx.compose.runtime.DisposableEffect
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import java.nio.ByteOrder
|
||||||
|
|
||||||
fun vibrateDouble(
|
fun vibrateDouble(
|
||||||
context: Context,
|
context: Context,
|
||||||
@@ -40,7 +41,8 @@ fun vibrateDouble(
|
|||||||
) {
|
) {
|
||||||
// Get the vibrator service
|
// Get the vibrator service
|
||||||
val vibrator = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
val vibrator = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||||
val vibratorManager = context.getSystemService(Context.VIBRATOR_MANAGER_SERVICE) as VibratorManager
|
val vibratorManager =
|
||||||
|
context.getSystemService(Context.VIBRATOR_MANAGER_SERVICE) as VibratorManager
|
||||||
vibratorManager.defaultVibrator
|
vibratorManager.defaultVibrator
|
||||||
} else {
|
} else {
|
||||||
@Suppress("DEPRECATION")
|
@Suppress("DEPRECATION")
|
||||||
@@ -64,7 +66,8 @@ fun vibrateDouble(
|
|||||||
|
|
||||||
fun vibrate(context: Context, duration: Long = 500) {
|
fun vibrate(context: Context, duration: Long = 500) {
|
||||||
val vibrator = if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) {
|
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
|
val vibratorManager =
|
||||||
|
context.getSystemService(Context.VIBRATOR_MANAGER_SERVICE) as VibratorManager
|
||||||
vibratorManager.defaultVibrator
|
vibratorManager.defaultVibrator
|
||||||
} else {
|
} else {
|
||||||
@Suppress("DEPRECATION")
|
@Suppress("DEPRECATION")
|
||||||
@@ -113,7 +116,8 @@ class SoundRecorder(
|
|||||||
|
|
||||||
val frameSize = Constants.FrameSize._120();
|
val frameSize = Constants.FrameSize._120();
|
||||||
val chunkSize = frameSize.v * 2; // Mono * 2 bytes per sample
|
val chunkSize = frameSize.v * 2; // Mono * 2 bytes per sample
|
||||||
val counts_until_reset = 3 * sampleRateInHz / frameSize.v;
|
val seconds_per_message = 3
|
||||||
|
val counts_until_reset = seconds_per_message * sampleRateInHz / frameSize.v;
|
||||||
val bufferSize =
|
val bufferSize =
|
||||||
AudioRecord.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat);
|
AudioRecord.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat);
|
||||||
|
|
||||||
@@ -126,15 +130,14 @@ class SoundRecorder(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
val thread = Thread {
|
val thread = Thread {
|
||||||
// var sent_first: Boolean = false
|
// var sent_first: Boolean = false
|
||||||
val outputByteStream = ByteBuffer.allocate(1000000)
|
val outputByteStream = ByteBuffer.allocate(1000000)
|
||||||
audioRecord.startRecording()
|
audioRecord.startRecording()
|
||||||
var last_tstamp: Long = Instant.now().toEpochMilli();
|
var last_tstamp: Long = Instant.now().toEpochMilli();
|
||||||
var count: Int = 0;
|
var count: Int = 0;
|
||||||
|
var min_val = 1000000f
|
||||||
|
var max_val = -1000000f
|
||||||
while (true) /**/ {
|
while (true) /**/ {
|
||||||
if (Thread.interrupted()) {
|
if (Thread.interrupted()) {
|
||||||
audioRecord.release();
|
audioRecord.release();
|
||||||
@@ -155,10 +158,16 @@ class SoundRecorder(
|
|||||||
var offset = 0
|
var offset = 0
|
||||||
var remained = frame.size
|
var remained = frame.size
|
||||||
while (remained > 0) {
|
while (remained > 0) {
|
||||||
val read = audioRecord.read(frame, offset, remained)
|
val read: Int = audioRecord.read(frame, offset, remained)
|
||||||
offset += read
|
offset += read
|
||||||
remained -= read
|
remained -= read
|
||||||
}
|
}
|
||||||
|
val s_arry = ShortArray(frame.size / 2)
|
||||||
|
ByteBuffer.wrap(frame).order(
|
||||||
|
ByteOrder.LITTLE_ENDIAN
|
||||||
|
).asShortBuffer().get(s_arry);
|
||||||
|
min_val = Math.min(s_arry.min().toFloat(), min_val)
|
||||||
|
max_val = Math.max(s_arry.max().toFloat(), max_val);
|
||||||
|
|
||||||
|
|
||||||
val encoded: ByteArray? =
|
val encoded: ByteArray? =
|
||||||
@@ -185,8 +194,14 @@ class SoundRecorder(
|
|||||||
val tstamp_buffer = ByteBuffer.allocate(Long.SIZE_BYTES)
|
val tstamp_buffer = ByteBuffer.allocate(Long.SIZE_BYTES)
|
||||||
val tstamp_bytes = tstamp_buffer.putLong(tstamp).array()
|
val tstamp_bytes = tstamp_buffer.putLong(tstamp).array()
|
||||||
var byte_send: ByteArray = tstamp_bytes + bytes;
|
var byte_send: ByteArray = tstamp_bytes + bytes;
|
||||||
Log.e(TAG, "Sending message of 3s with size ${byte_send.size} + ${byte_send[0].toString()}")
|
Log.e(
|
||||||
|
TAG,
|
||||||
|
"Sending message of 3s with size ${byte_send.size} + ${byte_send[0].toString()}"
|
||||||
|
)
|
||||||
MessageSender.sendMessage("/audio", byte_send, context)
|
MessageSender.sendMessage("/audio", byte_send, context)
|
||||||
|
Log.d(TAG, "${min_val} ${max_val} ")
|
||||||
|
min_val = 1000000f
|
||||||
|
max_val = -1000000f
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -12,27 +12,22 @@ import java.time.Instant
|
|||||||
|
|
||||||
|
|
||||||
class AScore(
|
class AScore(
|
||||||
_species: String,
|
val _species: String,
|
||||||
_score: Float,
|
val _score: Float,
|
||||||
_timestamp: Long,
|
val _timestamp: Long,
|
||||||
_trigger: Boolean = false,
|
val _new_entry: Boolean = false,
|
||||||
_new_entry: Boolean = false,
|
val _trigger: Boolean = false,
|
||||||
_redraw_number: Long = 0,
|
val _redraw_number: Long = 0,
|
||||||
) {
|
) {
|
||||||
var redraw_number = _redraw_number;
|
|
||||||
val split_stuff: List<String> = _species.split("_");
|
val split_stuff: List<String> = _species.split("_");
|
||||||
val species = split_stuff[0];
|
val species = split_stuff[0];
|
||||||
val score = _score;
|
val score = _score;
|
||||||
var trigger = _trigger;
|
var trigger = _trigger;
|
||||||
var new_entry = _new_entry;
|
var new_entry = _new_entry;
|
||||||
|
|
||||||
// var common_name = split_stuff[1];
|
|
||||||
val common_name = if (split_stuff.size > 1) split_stuff[1] else "";
|
val common_name = if (split_stuff.size > 1) split_stuff[1] else "";
|
||||||
val timestamp = _timestamp
|
val timestamp = _timestamp
|
||||||
fun set_redraw_number(redraw_num: Long)
|
|
||||||
{
|
|
||||||
redraw_number = redraw_num
|
|
||||||
}
|
|
||||||
fun age(): Long {
|
fun age(): Long {
|
||||||
var tstamp: Long = Instant.now().toEpochMilli()
|
var tstamp: Long = Instant.now().toEpochMilli()
|
||||||
return (tstamp - timestamp)
|
return (tstamp - timestamp)
|
||||||
@@ -48,10 +43,10 @@ class AScore(
|
|||||||
var internal_list = mutableListOf<AScore>()
|
var internal_list = mutableListOf<AScore>()
|
||||||
|
|
||||||
object SpeciesList {
|
object SpeciesList {
|
||||||
var trigger_redraw = 0;
|
lateinit var _mainState: MainState
|
||||||
var num_new_entries = 0;
|
var num_new_entries = 0;
|
||||||
var do_add_observation = false
|
|
||||||
var _list_on_ui: SnapshotStateList<MutableState<AScore>>? = null
|
var _list_on_ui: SnapshotStateList<MutableState<AScore>>? = null
|
||||||
|
|
||||||
fun setSpeciecsShow_list(list_in: SnapshotStateList<MutableState<AScore>>) {
|
fun setSpeciecsShow_list(list_in: SnapshotStateList<MutableState<AScore>>) {
|
||||||
_list_on_ui = list_in;
|
_list_on_ui = list_in;
|
||||||
}
|
}
|
||||||
@@ -63,7 +58,7 @@ object SpeciesList {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun add_observation(species_in: AScore): Boolean {
|
fun add_observation(species_in: AScore): Boolean {
|
||||||
Log.w(TAG, "In add observation")
|
|
||||||
var idx = 0
|
var idx = 0
|
||||||
var idx_replace = -1
|
var idx_replace = -1
|
||||||
var added_new = false;
|
var added_new = false;
|
||||||
@@ -74,7 +69,6 @@ object SpeciesList {
|
|||||||
idx += 1
|
idx += 1
|
||||||
}
|
}
|
||||||
if (idx_replace >= 0) {
|
if (idx_replace >= 0) {
|
||||||
Log.w(TAG, "Replacing")
|
|
||||||
internal_list[idx_replace] = species_in
|
internal_list[idx_replace] = species_in
|
||||||
internal_list[idx_replace].trigger = true;
|
internal_list[idx_replace].trigger = true;
|
||||||
} else {
|
} else {
|
||||||
@@ -94,6 +88,7 @@ object SpeciesList {
|
|||||||
|
|
||||||
fun update_list_on_ui_thread(
|
fun update_list_on_ui_thread(
|
||||||
) {
|
) {
|
||||||
|
// _list_on_ui!!.clear();
|
||||||
while (_list_on_ui!!.size < internal_list.size) {
|
while (_list_on_ui!!.size < internal_list.size) {
|
||||||
_list_on_ui!!.add(mutableStateOf(AScore("", 0.0F, 0L)))
|
_list_on_ui!!.add(mutableStateOf(AScore("", 0.0F, 0L)))
|
||||||
}
|
}
|
||||||
@@ -101,9 +96,24 @@ object SpeciesList {
|
|||||||
for ((index, value) in internal_list.withIndex()) {
|
for ((index, value) in internal_list.withIndex()) {
|
||||||
_list_on_ui?.get(index)?.value = value;
|
_list_on_ui?.get(index)?.value = value;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun bird_not_heard() {
|
||||||
|
_mainState.birdHeardState= BirdHeardState.NotHearing;
|
||||||
|
Log.d(TAG, "I set the state to not hearing")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun bird_heard() {
|
||||||
|
_mainState.birdHeardState = BirdHeardState.Hearing;
|
||||||
|
Log.d(TAG, "I set the state to hearing")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setBirdHeardVar(birdHeardState: BirdHeardState) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun setMainState(mainState: MainState) {
|
||||||
|
_mainState = mainState;
|
||||||
|
Log.d(TAG, "I set the internal app main state variable")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -2,6 +2,9 @@ package com.birdsounds.identify.presentation
|
|||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.IntentFilter
|
||||||
|
import android.os.BatteryManager
|
||||||
import android.text.format.DateFormat
|
import android.text.format.DateFormat
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
@@ -11,17 +14,18 @@ import androidx.compose.foundation.layout.fillMaxSize
|
|||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.rounded.Face
|
||||||
|
import androidx.compose.material.icons.rounded.Info
|
||||||
import androidx.compose.material.icons.rounded.Mic
|
import androidx.compose.material.icons.rounded.Mic
|
||||||
import androidx.compose.material.icons.rounded.MicOff
|
import androidx.compose.material.icons.rounded.MicOff
|
||||||
|
import androidx.compose.material.icons.rounded.MusicNote
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.MutableState
|
import androidx.compose.runtime.MutableState
|
||||||
import androidx.compose.runtime.getValue
|
|
||||||
import androidx.compose.runtime.key
|
import androidx.compose.runtime.key
|
||||||
import androidx.compose.runtime.mutableStateListOf
|
import androidx.compose.runtime.mutableStateListOf
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.setValue
|
|
||||||
import androidx.compose.runtime.snapshots.SnapshotStateList
|
import androidx.compose.runtime.snapshots.SnapshotStateList
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
@@ -29,11 +33,13 @@ import androidx.compose.ui.graphics.Color
|
|||||||
import androidx.compose.ui.text.font.FontStyle
|
import androidx.compose.ui.text.font.FontStyle
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.text.style.TextDecoration
|
import androidx.compose.ui.text.style.TextDecoration
|
||||||
|
import androidx.compose.ui.unit.TextUnit
|
||||||
|
import androidx.compose.ui.unit.TextUnitType
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.wear.compose.foundation.curvedComposable
|
import androidx.wear.compose.foundation.curvedComposable
|
||||||
import androidx.wear.compose.foundation.lazy.items
|
|
||||||
import androidx.wear.compose.foundation.lazy.itemsIndexed
|
import androidx.wear.compose.foundation.lazy.itemsIndexed
|
||||||
import androidx.wear.compose.material.Icon
|
import androidx.wear.compose.material.Icon
|
||||||
|
import androidx.wear.compose.material.LocalContentColor
|
||||||
import androidx.wear.compose.material.TimeTextDefaults
|
import androidx.wear.compose.material.TimeTextDefaults
|
||||||
import androidx.wear.compose.material.TimeText
|
import androidx.wear.compose.material.TimeText
|
||||||
import androidx.wear.compose.material.curvedText
|
import androidx.wear.compose.material.curvedText
|
||||||
@@ -67,11 +73,13 @@ fun interpolateColor(value: Float): Color {
|
|||||||
|
|
||||||
val species_list_show = mutableStateListOf<MutableState<AScore>>()
|
val species_list_show = mutableStateListOf<MutableState<AScore>>()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@SuppressLint("UnrememberedMutableState")
|
@SuppressLint("UnrememberedMutableState")
|
||||||
@OptIn(ExperimentalHorologistApi::class)
|
@OptIn(ExperimentalHorologistApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun SpeciesListView(
|
fun SpeciesListView(
|
||||||
context: Context, appState: AppState,
|
context: Context, appState: AppState, birdHeardState: BirdHeardState, mainState: MainState,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
|
||||||
@@ -80,7 +88,7 @@ fun SpeciesListView(
|
|||||||
species_list_show.add(hi);
|
species_list_show.add(hi);
|
||||||
}
|
}
|
||||||
SpeciesList.setSpeciecsShow_list(species_list_show)
|
SpeciesList.setSpeciecsShow_list(species_list_show)
|
||||||
|
SpeciesList.setMainState(mainState);
|
||||||
val species_show: SnapshotStateList<MutableState<AScore>> = remember { species_list_show }
|
val species_show: SnapshotStateList<MutableState<AScore>> = remember { species_list_show }
|
||||||
|
|
||||||
// var sP = scalingParams( maxTransitionArea= 0.25f, minTransitionArea = 0.05f)
|
// var sP = scalingParams( maxTransitionArea= 0.25f, minTransitionArea = 0.05f)
|
||||||
@@ -96,6 +104,16 @@ fun SpeciesListView(
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
var battery_level = remember { mutableStateOf(getBatteryLevel(context)) }
|
||||||
|
|
||||||
|
LaunchedEffect(key1 = Unit) {
|
||||||
|
while (true) {
|
||||||
|
battery_level.value = getBatteryLevel(context)
|
||||||
|
delay(60000) // Update every minute
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val text_counter = remember { mutableStateOf("Initial text") }
|
val text_counter = remember { mutableStateOf("Initial text") }
|
||||||
LaunchedEffect(key1 = true) {
|
LaunchedEffect(key1 = true) {
|
||||||
while (true) {
|
while (true) {
|
||||||
@@ -115,7 +133,6 @@ fun SpeciesListView(
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
ScreenScaffold(
|
ScreenScaffold(
|
||||||
scrollState = columnState
|
scrollState = columnState
|
||||||
) {
|
) {
|
||||||
@@ -125,23 +142,34 @@ fun SpeciesListView(
|
|||||||
DateFormat.getBestDateTimePattern(Locale.getDefault(), "hh:mm")
|
DateFormat.getBestDateTimePattern(Locale.getDefault(), "hh:mm")
|
||||||
),
|
),
|
||||||
startCurvedContent = {
|
startCurvedContent = {
|
||||||
|
|
||||||
|
curvedComposable {
|
||||||
|
Icon(
|
||||||
|
Icons.Rounded.MusicNote,
|
||||||
|
contentDescription = "",
|
||||||
|
modifier = Modifier.size(14.dp),
|
||||||
|
tint = if (mainState.birdHeardState == BirdHeardState.Hearing
|
||||||
|
) Color.Green else Color.Black
|
||||||
|
)
|
||||||
|
}
|
||||||
curvedComposable {
|
curvedComposable {
|
||||||
Icon(
|
Icon(
|
||||||
if (appState == AppState.Recording) Icons.Rounded.Mic else Icons.Rounded.MicOff,
|
if (appState == AppState.Recording) Icons.Rounded.Mic else Icons.Rounded.MicOff,
|
||||||
contentDescription = "",
|
contentDescription = "",
|
||||||
modifier = Modifier.size(16.dp)
|
modifier = Modifier.size(14.dp)
|
||||||
// tint = androidx.compose.ui.graphics.Color.Red
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// curvedText(
|
|
||||||
// text = if (appState == AppState.Recording) "" else "Inactive0",
|
|
||||||
//// textAlign = TextAlign.Center,
|
|
||||||
// )
|
|
||||||
},
|
},
|
||||||
endCurvedContent = {
|
endCurvedContent = {
|
||||||
|
|
||||||
curvedText(text = text_counter.value)//, textAlign = TextAlign.Center)
|
|
||||||
|
curvedText(
|
||||||
|
text = text_counter.value + " · " + battery_level.value+"%",
|
||||||
|
fontSize = TextUnit(value = 14F, type = TextUnitType.Sp)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
//, textAlign = TextAlign.Center)`
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,45 +184,29 @@ fun SpeciesListView(
|
|||||||
) {
|
) {
|
||||||
|
|
||||||
ScalingLazyColumn(
|
ScalingLazyColumn(
|
||||||
columnState = columnState,
|
modifier = Modifier.fillMaxWidth(),
|
||||||
modifier = Modifier.fillMaxWidth()
|
columnState = columnState
|
||||||
) {
|
) {
|
||||||
itemsIndexed(
|
itemsIndexed(
|
||||||
species_show,
|
species_show,
|
||||||
) { index, aSpec ->
|
) { index, aSpec ->
|
||||||
var text_decor = TextDecoration.None
|
var text_decor = TextDecoration.None
|
||||||
var text_style = FontStyle.Normal;
|
var text_style = FontStyle.Normal;
|
||||||
|
|
||||||
val currentName = aSpec.value.common_name
|
val currentName = aSpec.value.common_name
|
||||||
val currentScore = aSpec.value.score
|
|
||||||
val isNewEntry = aSpec.value.new_entry
|
val isNewEntry = aSpec.value.new_entry
|
||||||
val shouldTrigger = aSpec.value.trigger
|
val shouldTrigger = aSpec.value.trigger
|
||||||
|
// if (isNewEntry) {
|
||||||
var add_sep = false;
|
// text_style = FontStyle.Italic;
|
||||||
if (aSpec.value.new_entry) {
|
// aSpec.value.new_entry = false;
|
||||||
// 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(
|
Log.w(
|
||||||
TAG,
|
TAG,
|
||||||
"Adding Separator: " + index.toString() + " " + SpeciesList.num_new_entries + " " + add_sep.toString()
|
index.toString() + " " + currentName + " " + isNewEntry.toString() + " " + shouldTrigger.toString()
|
||||||
)
|
)
|
||||||
aSpec.value.new_entry = false;
|
|
||||||
|
|
||||||
// var composables: List<@Composable () -> Unit> = listOf();
|
|
||||||
|
|
||||||
key(currentName, isNewEntry, shouldTrigger) {
|
key(currentName, isNewEntry, shouldTrigger) {
|
||||||
if (shouldTrigger) {
|
if (shouldTrigger) {
|
||||||
Log.w(TAG, "Trigger " + aSpec.toString())
|
Log.w(TAG, "Trigger " + aSpec.toString())
|
||||||
|
Row {
|
||||||
FlashingText(
|
FlashingText(
|
||||||
text = aSpec.value.common_name,
|
text = aSpec.value.common_name,
|
||||||
fontStyle = text_style,
|
fontStyle = text_style,
|
||||||
@@ -207,51 +219,42 @@ fun SpeciesListView(
|
|||||||
delay(100);
|
delay(100);
|
||||||
aSpec.value.trigger = false
|
aSpec.value.trigger = false
|
||||||
}
|
}
|
||||||
Log.e("TAG", "BLINKING")
|
}
|
||||||
} else {
|
|
||||||
|
|
||||||
|
} else {
|
||||||
|
Row {
|
||||||
AutoSizedText(
|
AutoSizedText(
|
||||||
text = aSpec.value.common_name,
|
text = aSpec.value.common_name,
|
||||||
color = interpolateColor(aSpec.value.score),
|
color = interpolateColor(aSpec.value.score),
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize(),
|
||||||
textAlign = TextAlign.Center,
|
textAlign = TextAlign.Center,
|
||||||
|
key = Instant.now().toEpochMilli(),
|
||||||
textDecor = text_decor,
|
textDecor = text_decor,
|
||||||
fontStyle = text_style,
|
fontStyle = text_style,
|
||||||
)
|
)
|
||||||
|
}
|
||||||
Log.e("TAG", "Not blinking")
|
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()) {
|
// Get current battery level
|
||||||
// items(species_show) { aSpec -> Text(text = aSpec.value.common_name)
|
private fun getBatteryLevel(context: Context): Int {
|
||||||
// } // Dynamically display the chips
|
val batteryStatus: Intent? = IntentFilter(Intent.ACTION_BATTERY_CHANGED).let { iFilter ->
|
||||||
//
|
context.registerReceiver(null, iFilter)
|
||||||
// }
|
}
|
||||||
// }
|
|
||||||
|
val level = batteryStatus?.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) ?: -1
|
||||||
|
val scale = batteryStatus?.getIntExtra(BatteryManager.EXTRA_SCALE, -1) ?: -1
|
||||||
|
|
||||||
|
return if (level != -1 && scale != -1) {
|
||||||
|
level * 100 / scale
|
||||||
|
} else {
|
||||||
|
-1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 0 B |
Reference in New Issue
Block a user