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>
|
||||
<SelectionState runConfigName="mobile">
|
||||
<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">
|
||||
<handle>
|
||||
<DeviceId pluginId="PhysicalDevice" identifier="serial=RFAX803LMFR" />
|
||||
<DeviceId pluginId="PhysicalDevice" identifier="serial=569cc5b0" />
|
||||
</handle>
|
||||
</Target>
|
||||
</DropdownSelection>
|
||||
@@ -15,10 +15,10 @@
|
||||
</SelectionState>
|
||||
<SelectionState runConfigName="wear">
|
||||
<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">
|
||||
<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>
|
||||
</Target>
|
||||
</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"
|
||||
coreKtxVersion = "1.13.0"
|
||||
animationCoreAndroid = "1.6.6"
|
||||
#litertSupportApi = "1.2.0"
|
||||
#litert = "1.2.0"
|
||||
#litert = "1.0.1"
|
||||
|
||||
[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" }
|
||||
ui-tooling = { module = "androidx.compose.ui:ui-tooling", version.ref = "uiTooling" }
|
||||
androidx-animation-core-android = { group = "androidx.compose.animation", name = "animation-core-android", version.ref = "animationCoreAndroid" }
|
||||
#litert-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" }
|
||||
|
||||
[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.constraintlayout)
|
||||
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.activity.compose)
|
||||
implementation(platform(libs.androidx.compose.bom))
|
||||
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("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("uk.me.berndporr:iirj:1.7")
|
||||
implementation(libs.androidx.ui.graphics)
|
||||
@@ -65,6 +71,8 @@ dependencies {
|
||||
implementation(libs.androidx.material3)
|
||||
implementation(libs.androidx.datastore.core.android)
|
||||
implementation(libs.core.ktx)
|
||||
// implementation(libs.litert.support.api)
|
||||
// implementation(libs.litert)
|
||||
// implementation(libs.litert)
|
||||
testImplementation(libs.junit)
|
||||
androidTestImplementation(libs.androidx.junit)
|
||||
|
||||
Binary file not shown.
@@ -42,11 +42,13 @@ class Downloader(mainActivity: MainActivity) {
|
||||
}
|
||||
}
|
||||
|
||||
fun prepareModelFiles()
|
||||
{
|
||||
copyAssetToFolder(settings.pkg_model_file, settings.local_model_file);
|
||||
copyAssetToFolder(settings.pkg_meta_model_file, settings.local_meta_model_file);
|
||||
}
|
||||
|
||||
fun prepareModelFiles() {
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
fun requestLocation(context: Context, soundClassifier: SoundClassifier) {
|
||||
fun requestLocation(
|
||||
context: Context,
|
||||
soundClassifier: SoundClassifier,
|
||||
soundClassifierSpectrogramBased: SoundClassifierSpectrogramBased
|
||||
) {
|
||||
if (ActivityCompat.checkSelfPermission(
|
||||
context,
|
||||
Manifest.permission.ACCESS_COARSE_LOCATION
|
||||
@@ -44,6 +48,8 @@ object Location {
|
||||
if (locationListenerGPS == null) locationListenerGPS = object : LocationListener {
|
||||
@SuppressLint("SetTextI18n")
|
||||
override fun onLocationChanged(location: Location) {
|
||||
|
||||
|
||||
Log.w(TAG, "Got location changed");
|
||||
while (!soundClassifier.is_model_ready()) {
|
||||
Thread.sleep(50);
|
||||
@@ -51,11 +57,19 @@ object Location {
|
||||
Log.w(TAG, "Sound classifier is ready");
|
||||
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;
|
||||
activity?.let {
|
||||
|
||||
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)
|
||||
loc_lon.text =
|
||||
@@ -71,12 +85,7 @@ object Location {
|
||||
geocoder.getFromLocation(location.latitude, location.longitude, 1)
|
||||
if (addresses?.isNotEmpty() == true) {
|
||||
val address: Address = addresses[0]
|
||||
|
||||
// loc_string.text = address.locality.toString() + ", " + address.adminArea.toString() + " " + address.countryName.toString()
|
||||
loc_string.text = address.getAddressLine(0).toString()
|
||||
// Log.w(TAG, address.toString())
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,9 +4,10 @@ import android.Manifest
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.content.pm.PackageManager
|
||||
import android.graphics.Bitmap
|
||||
import android.os.Bundle
|
||||
import android.preference.PreferenceManager
|
||||
import android.util.Log
|
||||
import android.widget.ImageView
|
||||
import android.widget.SeekBar
|
||||
import android.widget.TextView
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
@@ -22,11 +23,13 @@ import androidx.datastore.preferences.preferencesDataStore
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.google.android.gms.wearable.ChannelClient
|
||||
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.delay
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
@@ -36,8 +39,6 @@ private var updateJob: Job? = null
|
||||
private var updateCounter = 0
|
||||
val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "settings")
|
||||
|
||||
|
||||
|
||||
val Any.TAG: String
|
||||
get() {
|
||||
val tag = javaClass.simpleName
|
||||
@@ -49,30 +50,64 @@ class SynchronousDataStore(private val context: Context) {
|
||||
|
||||
// Define keys
|
||||
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 saveThreshold(value: Int) {
|
||||
fun saveThresholdSpectrogram(value: Int) {
|
||||
runBlocking {
|
||||
context.dataStore.edit { preferences ->
|
||||
preferences[THRESHOLD_KEY] = value;
|
||||
preferences[THRESHOLD_SPECTROGRAM_KEY] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getThreshold(): Int {
|
||||
fun getThresholdSpectrogram(): Int {
|
||||
|
||||
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() {
|
||||
// private lateinit var soundClassifier: SoundClassifier
|
||||
val REQUEST_PERMISSIONS = 1337
|
||||
|
||||
private lateinit var dataStore: SynchronousDataStore
|
||||
@@ -88,15 +123,16 @@ class MainActivity : AppCompatActivity() {
|
||||
super.onChannelOpened(channel)
|
||||
Log.d(TAG, "onChannelOpened")
|
||||
}
|
||||
}
|
||||
)
|
||||
})
|
||||
Downloader(this).prepareModelFiles();
|
||||
Log.w(TAG, "Finished setting up downloader")
|
||||
requestPermissions()
|
||||
|
||||
|
||||
soundClassifier = SoundClassifier(this, SoundClassifier.Options())
|
||||
Log.w(TAG, "Starting sound classifier")
|
||||
Location.requestLocation(this, soundClassifier!!)
|
||||
Log.w(TAG, "Starting location requester")
|
||||
soundClassifierSpectrogramBased = SoundClassifierSpectrogramBased(this);
|
||||
|
||||
Location.requestLocation(this, soundClassifier!!, soundClassifierSpectrogramBased!!);
|
||||
|
||||
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
|
||||
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
|
||||
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)
|
||||
dataStore = SynchronousDataStore(applicationContext)
|
||||
|
||||
|
||||
val configuration = ResamplerConfiguration(
|
||||
quality = ResamplerQuality.BEST,
|
||||
inputChannel = ResamplerChannel.MONO,
|
||||
inputSampleRate = 48000,
|
||||
outputChannel = ResamplerChannel.MONO,
|
||||
outputSampleRate = 16000
|
||||
)
|
||||
resampler = Resampler(configuration);
|
||||
|
||||
|
||||
|
||||
Settings.threshold = dataStore.getThreshold();
|
||||
seekBar.progress = Settings.threshold
|
||||
thresholdText.text = String.format("%.2f", Settings.threshold/100f)
|
||||
seekBar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
|
||||
val thresholdText_birdsinging = findViewById<TextView>(R.id.threshold_value_text_birdheard)
|
||||
val seekBar_bird_singing = findViewById<SeekBar>(R.id.threshold_set_scale_bar_birdheard);
|
||||
Settings.threshold_bird_singing = dataStore.getBirdHeardThreshold();
|
||||
seekBar_bird_singing.progress = Settings.threshold_bird_singing
|
||||
thresholdText_birdsinging.text =
|
||||
String.format("%.2f", Settings.threshold_bird_singing / 100f)
|
||||
seekBar_bird_singing.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
|
||||
@SuppressLint("DefaultLocale")
|
||||
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
|
||||
|
||||
var actualValue = progress
|
||||
dataStore.saveThreshold(actualValue);
|
||||
Settings.threshold = actualValue;
|
||||
thresholdText.text = String.format("%.2f", actualValue/100f)
|
||||
dataStore.saveBirdHeardThreshold(actualValue);
|
||||
Settings.threshold_bird_singing = actualValue;
|
||||
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) {}
|
||||
@@ -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 {
|
||||
|
||||
var soundClassifier: SoundClassifier? = null
|
||||
// fun getSoundClassifier(): SoundClassifier? {
|
||||
// return soundClassifier
|
||||
// }
|
||||
var soundClassifierSpectrogramBased: SoundClassifierSpectrogramBased? = null
|
||||
|
||||
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 {
|
||||
while (isActive) { // isActive is a property of the coroutine scope
|
||||
updateCounter++
|
||||
if (last_message_time == 0.toLong())
|
||||
{
|
||||
if (last_message_time == 0.toLong()) {
|
||||
last_message_delay.text = "No messages received"
|
||||
} else
|
||||
{
|
||||
last_message_delay.text = "Last message: ${(Instant.now().toEpochMilli() - last_message_time)/1000F.toInt()} seconds ago"
|
||||
} else {
|
||||
last_message_delay.text = "Last message: ${
|
||||
(Instant.now().toEpochMilli() - last_message_time) / 1000F.toInt()
|
||||
} seconds ago"
|
||||
}
|
||||
|
||||
// last_message_delay.text = "Update count: $updateCounter"
|
||||
@@ -171,8 +270,7 @@ class MainActivity : AppCompatActivity() {
|
||||
val perms = mutableListOf<String>()
|
||||
|
||||
if (ContextCompat.checkSelfPermission(
|
||||
this,
|
||||
Manifest.permission.ACCESS_COARSE_LOCATION
|
||||
this, Manifest.permission.ACCESS_COARSE_LOCATION
|
||||
) != PackageManager.PERMISSION_GRANTED
|
||||
) {
|
||||
perms.add(Manifest.permission.ACCESS_COARSE_LOCATION)
|
||||
|
||||
@@ -1,16 +1,25 @@
|
||||
package com.birdsounds.identify
|
||||
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Color
|
||||
import android.util.Log
|
||||
|
||||
import com.google.android.gms.wearable.MessageEvent
|
||||
import com.google.android.gms.wearable.WearableListenerService
|
||||
import com.theeasiestway.opus.Constants
|
||||
import com.theeasiestway.opus.Opus
|
||||
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.nio.ByteBuffer
|
||||
import java.nio.ByteOrder
|
||||
import java.time.Instant
|
||||
import java.util.LinkedList
|
||||
import java.util.Queue
|
||||
|
||||
|
||||
var last_message_time = 0L;
|
||||
@@ -24,6 +33,75 @@ fun ByteArray.toLong(): Long {
|
||||
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() {
|
||||
|
||||
|
||||
@@ -32,9 +110,10 @@ class MessageListenerService : WearableListenerService() {
|
||||
super.onMessageReceived(p0)
|
||||
val codec_opus = Opus()
|
||||
codec_opus.decoderInit(Constants.SampleRate._48000(), Constants.Channels.mono())
|
||||
// MainActivity
|
||||
Log.w(TAG, "Data recv: " + p0.data.size.toString() + " bytes")
|
||||
val soundclassifier = MainActivity.soundClassifier
|
||||
val soundClassifierSpectrogramBased = MainActivity.soundClassifierSpectrogramBased;
|
||||
|
||||
if (soundclassifier == null) {
|
||||
Log.w(TAG, "Have invalid sound classifier")
|
||||
return
|
||||
@@ -42,6 +121,14 @@ class MessageListenerService : WearableListenerService() {
|
||||
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)
|
||||
|
||||
last_message_time = tstamp_bytes.toLong()
|
||||
@@ -53,8 +140,7 @@ class MessageListenerService : WearableListenerService() {
|
||||
val buffer = ByteBuffer.wrap(audio_bytes_og)
|
||||
val sound_a = ByteArrayOutputStream();
|
||||
val byteArrayList = mutableListOf<ByteArray>()
|
||||
while (buffer.hasRemaining())
|
||||
{
|
||||
while (buffer.hasRemaining()) {
|
||||
val num_to_read = buffer.get().toInt()
|
||||
val read_this = ByteArray(num_to_read)
|
||||
buffer.get(read_this)
|
||||
@@ -62,25 +148,94 @@ class MessageListenerService : WearableListenerService() {
|
||||
sound_a.write(decoded);
|
||||
// Log.e(TAG,"Decompressed ${read_this.size} to ${decoded?.size}")
|
||||
}
|
||||
val audio_bytes = sound_a.toByteArray()
|
||||
|
||||
codec_opus.decoderRelease();
|
||||
|
||||
|
||||
val audio_bytes = sound_a.toByteArray()
|
||||
val short_array = ShortArray(audio_bytes.size / 2)
|
||||
// Log.e(TAG,"Size of short array buffer: "+ decoded?.size.toString());
|
||||
|
||||
ByteBuffer.wrap(audio_bytes).order(
|
||||
ByteOrder.LITTLE_ENDIAN
|
||||
).asShortBuffer().get(short_array)
|
||||
// Log.e(TAG, pcm_byte_array.sum().toString())
|
||||
|
||||
|
||||
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");
|
||||
|
||||
|
||||
var string_send: String = ""
|
||||
var sorted_list = soundclassifier.executeScoring(short_array)
|
||||
val array_score = short_array;
|
||||
var sorted_list = soundclassifier.executeScoring(array_score)
|
||||
Log.w(TAG, "FINISHED SCORING");
|
||||
Log.w(TAG, "")
|
||||
val threshold = Settings.threshold/100f
|
||||
val threshold = Settings.threshold_waveform / 100f
|
||||
for (i in 0 until 10) {
|
||||
val score = sorted_list[i].value
|
||||
if (score < threshold) {
|
||||
@@ -89,13 +244,30 @@ class MessageListenerService : WearableListenerService() {
|
||||
val index = sorted_list[i].index
|
||||
val species_name = soundclassifier.labelList[index]
|
||||
Log.w(TAG, species_name + ", " + score.toString())
|
||||
string_send+= species_name
|
||||
string_send += species_name + " ~";
|
||||
string_send += ','
|
||||
string_send += score.toString()
|
||||
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
|
||||
|
||||
object Settings {
|
||||
var local_model_file: String = "2024_08_16_audio_model.tflite"
|
||||
var pkg_model_file: String = "2024_08_16/audio-model.tflite"
|
||||
|
||||
var local_meta_model_file: String = "2024_08_16_meta_model.tflite"
|
||||
var pkg_meta_model_file: String = "2024_08_16/meta-model.tflite"
|
||||
fun create_file_map(pkg_path: String): Map<String, String> {
|
||||
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.util.Log
|
||||
import org.tensorflow.lite.Interpreter
|
||||
|
||||
import java.io.BufferedReader
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
@@ -99,9 +100,7 @@ class SoundClassifier(
|
||||
private lateinit var metaInputBuffer: FloatBuffer
|
||||
private var model_ready = false;
|
||||
init {;
|
||||
setupDecoder(context)
|
||||
loadLabels(context)
|
||||
loadAssetList(context)
|
||||
setupInterpreter(context)
|
||||
setupMetaInterpreter(context)
|
||||
warmUpModel()
|
||||
@@ -112,49 +111,12 @@ class SoundClassifier(
|
||||
{
|
||||
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 */
|
||||
private fun loadLabels(context: Context) {
|
||||
val localeList = context.resources.configuration.locales
|
||||
val language = localeList.get(0).language
|
||||
var filename = options.labelsBase + "_${language}.txt"
|
||||
var filename = "labels_en.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 {
|
||||
val reader =
|
||||
BufferedReader(InputStreamReader(context.assets.open(filename)))
|
||||
@@ -179,7 +141,7 @@ class SoundClassifier(
|
||||
context.getDir(
|
||||
"",
|
||||
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")
|
||||
val modelFile = File(modelFilePath)
|
||||
@@ -190,6 +152,9 @@ class SoundClassifier(
|
||||
Log.i(TAG, "Done creating TFLite buffer from $modelFilePath")
|
||||
|
||||
interpreter = Interpreter(tfliteBuffer, Interpreter.Options())
|
||||
|
||||
|
||||
|
||||
} catch (e: IOException) {
|
||||
Log.e(TAG, "Failed to load TFLite model - ${e.message}")
|
||||
return
|
||||
@@ -222,7 +187,7 @@ class SoundClassifier(
|
||||
val metaModelFilePath = context.getDir(
|
||||
"",
|
||||
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")
|
||||
val metaModelFile = File(metaModelFilePath)
|
||||
val tfliteBuffer: ByteBuffer =
|
||||
@@ -270,6 +235,7 @@ class SoundClassifier(
|
||||
metaInputBuffer.put(1, lon)
|
||||
metaInputBuffer.put(2, weekMeta.toFloat())
|
||||
metaInputBuffer.rewind() // Reset position to beginning of buffer
|
||||
|
||||
val metaOutputBuffer = FloatBuffer.allocate(metaModelNumClasses)
|
||||
metaOutputBuffer.rewind()
|
||||
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:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView"
|
||||
<ImageView
|
||||
android:id="@+id/imageView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingLeft="5dp"
|
||||
android:paddingTop="15dp"
|
||||
android:text="Threshold:"
|
||||
android:textSize="34sp" />
|
||||
android:layout_height="100dp"
|
||||
android:background="#ffffff"
|
||||
android:padding="5dp" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
@@ -27,23 +25,156 @@
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/threshold_value_text"
|
||||
android:layout_width="match_parent"
|
||||
android:id="@+id/editTextText2"
|
||||
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_weight="4"
|
||||
android:gravity="center"
|
||||
android:text="Threshold:"
|
||||
android:textAlignment="center"
|
||||
android:textSize="28sp" />
|
||||
|
||||
<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_height="match_parent"
|
||||
android:layout_gravity="center|fill_horizontal|fill_vertical"
|
||||
android:layout_weight="1"
|
||||
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>
|
||||
|
||||
|
||||
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
|
||||
.background(color = backgroundColor),
|
||||
textDecor = textDecor,
|
||||
fontStyle = fontStyle
|
||||
fontStyle = fontStyle,
|
||||
key = key
|
||||
)
|
||||
}
|
||||
|
||||
@@ -72,6 +73,7 @@ fun AutoSizedText(
|
||||
textAlign: TextAlign = TextAlign.Center,
|
||||
modifier: Modifier = Modifier,
|
||||
textDecor: TextDecoration = TextDecoration.None,
|
||||
key: Long = 0,
|
||||
minFontSize: TextUnit = 5.sp,
|
||||
targetFontSize: TextUnit = 24.sp,
|
||||
fontWeight: FontWeight = FontWeight.Normal,
|
||||
@@ -115,7 +117,9 @@ fun AutoSizedText(
|
||||
|
||||
// Convert the best font size back to sp and use it
|
||||
// val bestFontSize = with(density) { bestFontSizePx.toSp() }
|
||||
LaunchedEffect(key1 = key) {
|
||||
|
||||
}
|
||||
|
||||
Text(
|
||||
text = display_text,
|
||||
|
||||
@@ -12,7 +12,6 @@ import android.app.Activity
|
||||
import android.app.Notification
|
||||
import android.app.NotificationManager
|
||||
import android.app.PendingIntent
|
||||
import android.app.Service
|
||||
import android.content.Context
|
||||
import android.content.ContextWrapper
|
||||
import android.content.Intent
|
||||
@@ -41,7 +40,6 @@ import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.app.NotificationCompat
|
||||
import androidx.core.app.ServiceCompat.startForeground
|
||||
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import androidx.navigation.NavHostController
|
||||
@@ -53,7 +51,6 @@ import androidx.wear.compose.navigation.SwipeDismissableNavHost
|
||||
import androidx.wear.compose.navigation.composable
|
||||
import androidx.wear.compose.navigation.rememberSwipeDismissableNavController
|
||||
import androidx.wear.ongoing.OngoingActivity
|
||||
import com.birdsounds.identify.R
|
||||
import com.birdsounds.identify.presentation.theme.IdentifyTheme
|
||||
import com.google.android.horologist.annotations.ExperimentalHorologistApi
|
||||
import com.google.android.horologist.audio.ui.VolumeViewModel
|
||||
@@ -166,7 +163,7 @@ fun WearApp() {
|
||||
) {
|
||||
composable("species_list") {
|
||||
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) {
|
||||
private val playbackStateMutatorMutex = MutatorMutex()
|
||||
|
||||
var appState by mutableStateOf<AppState>(AppState.Ready)
|
||||
private set
|
||||
|
||||
|
||||
var birdHeardState by mutableStateOf<BirdHeardState>(BirdHeardState.NotHearing)
|
||||
|
||||
|
||||
|
||||
var recordingProgress by mutableStateOf(0f)
|
||||
private set
|
||||
var isPermissionDenied by mutableStateOf(false)
|
||||
@@ -82,6 +88,13 @@ sealed class AppState {
|
||||
object Recording : AppState()
|
||||
}
|
||||
|
||||
sealed class BirdHeardState {
|
||||
object Hearing : BirdHeardState()
|
||||
object NotHearing : BirdHeardState()
|
||||
}
|
||||
|
||||
|
||||
|
||||
@RequiresPermission(Manifest.permission.RECORD_AUDIO)
|
||||
private suspend fun record(activity: (Activity),soundRecorder: SoundRecorder,
|
||||
setProgress: (progress: Float) -> Unit,
|
||||
|
||||
+16
-4
@@ -1,6 +1,7 @@
|
||||
package com.birdsounds.identify.presentation
|
||||
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import com.google.android.gms.wearable.MessageEvent
|
||||
import com.google.android.gms.wearable.WearableListenerService
|
||||
@@ -16,6 +17,7 @@ class MessageListenerService : WearableListenerService() {
|
||||
{
|
||||
this_context = context;
|
||||
}
|
||||
|
||||
override fun onMessageReceived(p0: MessageEvent) {
|
||||
super.onMessageReceived(p0)
|
||||
val t_scored = ByteBuffer.wrap(p0.data).getLong()
|
||||
@@ -27,12 +29,14 @@ class MessageListenerService : WearableListenerService() {
|
||||
var do_vibrate = false;
|
||||
var did_add = false;
|
||||
var new_entries = 0;
|
||||
SpeciesList.clear_new_flags();
|
||||
// bird_heard_state = BirdHeardState.NotHearing
|
||||
SpeciesList.bird_not_heard();
|
||||
list_strings.map({
|
||||
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;
|
||||
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;
|
||||
|
||||
if (SpeciesList.add_observation(out))
|
||||
@@ -41,8 +45,16 @@ class MessageListenerService : WearableListenerService() {
|
||||
}
|
||||
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);
|
||||
if (did_add) {
|
||||
vibrateDouble(this, 100, 250, 100);
|
||||
|
||||
@@ -31,6 +31,7 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import java.nio.ByteOrder
|
||||
|
||||
fun vibrateDouble(
|
||||
context: Context,
|
||||
@@ -40,7 +41,8 @@ fun vibrateDouble(
|
||||
) {
|
||||
// Get the vibrator service
|
||||
val vibrator = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
val vibratorManager = context.getSystemService(Context.VIBRATOR_MANAGER_SERVICE) as VibratorManager
|
||||
val vibratorManager =
|
||||
context.getSystemService(Context.VIBRATOR_MANAGER_SERVICE) as VibratorManager
|
||||
vibratorManager.defaultVibrator
|
||||
} else {
|
||||
@Suppress("DEPRECATION")
|
||||
@@ -64,7 +66,8 @@ fun vibrateDouble(
|
||||
|
||||
fun vibrate(context: Context, duration: Long = 500) {
|
||||
val vibrator = if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) {
|
||||
val vibratorManager = context.getSystemService(Context.VIBRATOR_MANAGER_SERVICE) as VibratorManager
|
||||
val vibratorManager =
|
||||
context.getSystemService(Context.VIBRATOR_MANAGER_SERVICE) as VibratorManager
|
||||
vibratorManager.defaultVibrator
|
||||
} else {
|
||||
@Suppress("DEPRECATION")
|
||||
@@ -113,7 +116,8 @@ class SoundRecorder(
|
||||
|
||||
val frameSize = Constants.FrameSize._120();
|
||||
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 =
|
||||
AudioRecord.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat);
|
||||
|
||||
@@ -126,15 +130,14 @@ class SoundRecorder(
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
val thread = Thread {
|
||||
// var sent_first: Boolean = false
|
||||
val outputByteStream = ByteBuffer.allocate(1000000)
|
||||
audioRecord.startRecording()
|
||||
var last_tstamp: Long = Instant.now().toEpochMilli();
|
||||
var count: Int = 0;
|
||||
var min_val = 1000000f
|
||||
var max_val = -1000000f
|
||||
while (true) /**/ {
|
||||
if (Thread.interrupted()) {
|
||||
audioRecord.release();
|
||||
@@ -155,10 +158,16 @@ class SoundRecorder(
|
||||
var offset = 0
|
||||
var remained = frame.size
|
||||
while (remained > 0) {
|
||||
val read = audioRecord.read(frame, offset, remained)
|
||||
val read: Int = audioRecord.read(frame, offset, remained)
|
||||
offset += 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? =
|
||||
@@ -185,8 +194,14 @@ class SoundRecorder(
|
||||
val tstamp_buffer = ByteBuffer.allocate(Long.SIZE_BYTES)
|
||||
val tstamp_bytes = tstamp_buffer.putLong(tstamp).array()
|
||||
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)
|
||||
Log.d(TAG, "${min_val} ${max_val} ")
|
||||
min_val = 1000000f
|
||||
max_val = -1000000f
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -12,27 +12,22 @@ import java.time.Instant
|
||||
|
||||
|
||||
class AScore(
|
||||
_species: String,
|
||||
_score: Float,
|
||||
_timestamp: Long,
|
||||
_trigger: Boolean = false,
|
||||
_new_entry: Boolean = false,
|
||||
_redraw_number: Long = 0,
|
||||
val _species: String,
|
||||
val _score: Float,
|
||||
val _timestamp: Long,
|
||||
val _new_entry: Boolean = false,
|
||||
val _trigger: Boolean = false,
|
||||
val _redraw_number: Long = 0,
|
||||
) {
|
||||
var redraw_number = _redraw_number;
|
||||
val split_stuff: List<String> = _species.split("_");
|
||||
val species = split_stuff[0];
|
||||
val score = _score;
|
||||
var trigger = _trigger;
|
||||
var new_entry = _new_entry;
|
||||
|
||||
// var common_name = split_stuff[1];
|
||||
val common_name = if (split_stuff.size > 1) split_stuff[1] else "";
|
||||
val timestamp = _timestamp
|
||||
fun set_redraw_number(redraw_num: Long)
|
||||
{
|
||||
redraw_number = redraw_num
|
||||
}
|
||||
|
||||
fun age(): Long {
|
||||
var tstamp: Long = Instant.now().toEpochMilli()
|
||||
return (tstamp - timestamp)
|
||||
@@ -48,10 +43,10 @@ class AScore(
|
||||
var internal_list = mutableListOf<AScore>()
|
||||
|
||||
object SpeciesList {
|
||||
var trigger_redraw = 0;
|
||||
lateinit var _mainState: MainState
|
||||
var num_new_entries = 0;
|
||||
var do_add_observation = false
|
||||
var _list_on_ui: SnapshotStateList<MutableState<AScore>>? = null
|
||||
|
||||
fun setSpeciecsShow_list(list_in: SnapshotStateList<MutableState<AScore>>) {
|
||||
_list_on_ui = list_in;
|
||||
}
|
||||
@@ -63,7 +58,7 @@ object SpeciesList {
|
||||
}
|
||||
|
||||
fun add_observation(species_in: AScore): Boolean {
|
||||
Log.w(TAG, "In add observation")
|
||||
|
||||
var idx = 0
|
||||
var idx_replace = -1
|
||||
var added_new = false;
|
||||
@@ -74,7 +69,6 @@ object SpeciesList {
|
||||
idx += 1
|
||||
}
|
||||
if (idx_replace >= 0) {
|
||||
Log.w(TAG, "Replacing")
|
||||
internal_list[idx_replace] = species_in
|
||||
internal_list[idx_replace].trigger = true;
|
||||
} else {
|
||||
@@ -94,6 +88,7 @@ object SpeciesList {
|
||||
|
||||
fun update_list_on_ui_thread(
|
||||
) {
|
||||
// _list_on_ui!!.clear();
|
||||
while (_list_on_ui!!.size < internal_list.size) {
|
||||
_list_on_ui!!.add(mutableStateOf(AScore("", 0.0F, 0L)))
|
||||
}
|
||||
@@ -101,9 +96,24 @@ object SpeciesList {
|
||||
for ((index, value) in internal_list.withIndex()) {
|
||||
_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.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.os.BatteryManager
|
||||
import android.text.format.DateFormat
|
||||
import android.util.Log
|
||||
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.size
|
||||
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.MicOff
|
||||
import androidx.compose.material.icons.rounded.MusicNote
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.key
|
||||
import androidx.compose.runtime.mutableStateListOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.runtime.snapshots.SnapshotStateList
|
||||
import androidx.compose.ui.Alignment
|
||||
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.style.TextAlign
|
||||
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.wear.compose.foundation.curvedComposable
|
||||
import androidx.wear.compose.foundation.lazy.items
|
||||
import androidx.wear.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.wear.compose.material.Icon
|
||||
import androidx.wear.compose.material.LocalContentColor
|
||||
import androidx.wear.compose.material.TimeTextDefaults
|
||||
import androidx.wear.compose.material.TimeText
|
||||
import androidx.wear.compose.material.curvedText
|
||||
@@ -67,11 +73,13 @@ fun interpolateColor(value: Float): Color {
|
||||
|
||||
val species_list_show = mutableStateListOf<MutableState<AScore>>()
|
||||
|
||||
|
||||
|
||||
@SuppressLint("UnrememberedMutableState")
|
||||
@OptIn(ExperimentalHorologistApi::class)
|
||||
@Composable
|
||||
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);
|
||||
}
|
||||
SpeciesList.setSpeciecsShow_list(species_list_show)
|
||||
|
||||
SpeciesList.setMainState(mainState);
|
||||
val species_show: SnapshotStateList<MutableState<AScore>> = remember { species_list_show }
|
||||
|
||||
// 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") }
|
||||
LaunchedEffect(key1 = true) {
|
||||
while (true) {
|
||||
@@ -115,7 +133,6 @@ fun SpeciesListView(
|
||||
|
||||
|
||||
|
||||
|
||||
ScreenScaffold(
|
||||
scrollState = columnState
|
||||
) {
|
||||
@@ -125,23 +142,34 @@ fun SpeciesListView(
|
||||
DateFormat.getBestDateTimePattern(Locale.getDefault(), "hh:mm")
|
||||
),
|
||||
startCurvedContent = {
|
||||
|
||||
curvedComposable {
|
||||
Icon(
|
||||
Icons.Rounded.MusicNote,
|
||||
contentDescription = "",
|
||||
modifier = Modifier.size(14.dp),
|
||||
tint = if (mainState.birdHeardState == BirdHeardState.Hearing
|
||||
) Color.Green else Color.Black
|
||||
)
|
||||
}
|
||||
curvedComposable {
|
||||
Icon(
|
||||
if (appState == AppState.Recording) Icons.Rounded.Mic else Icons.Rounded.MicOff,
|
||||
contentDescription = "",
|
||||
modifier = Modifier.size(16.dp)
|
||||
// tint = androidx.compose.ui.graphics.Color.Red
|
||||
modifier = Modifier.size(14.dp)
|
||||
)
|
||||
}
|
||||
|
||||
// curvedText(
|
||||
// text = if (appState == AppState.Recording) "" else "Inactive0",
|
||||
//// textAlign = TextAlign.Center,
|
||||
// )
|
||||
},
|
||||
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(
|
||||
columnState = columnState,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
columnState = columnState
|
||||
) {
|
||||
itemsIndexed(
|
||||
species_show,
|
||||
) { index, aSpec ->
|
||||
var text_decor = TextDecoration.None
|
||||
var text_style = FontStyle.Normal;
|
||||
|
||||
val currentName = aSpec.value.common_name
|
||||
val currentScore = aSpec.value.score
|
||||
val isNewEntry = aSpec.value.new_entry
|
||||
val shouldTrigger = aSpec.value.trigger
|
||||
|
||||
var add_sep = false;
|
||||
if (aSpec.value.new_entry) {
|
||||
// add_sep = true;
|
||||
text_style = FontStyle.Italic;
|
||||
// aSpec.value.new_entry = false
|
||||
}
|
||||
Log.w(TAG, "Species " + currentName + " " + aSpec.value.new_entry.toString())
|
||||
LaunchedEffect(Unit) {
|
||||
delay(100);
|
||||
aSpec.value.new_entry = false
|
||||
}
|
||||
// if (index == (SpeciesList.num_new_entries-1)) {
|
||||
// add_sep = true;"
|
||||
// if (isNewEntry) {
|
||||
// text_style = FontStyle.Italic;
|
||||
// aSpec.value.new_entry = false;
|
||||
// }
|
||||
Log.w(
|
||||
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) {
|
||||
if (shouldTrigger) {
|
||||
Log.w(TAG, "Trigger " + aSpec.toString())
|
||||
Row {
|
||||
FlashingText(
|
||||
text = aSpec.value.common_name,
|
||||
fontStyle = text_style,
|
||||
@@ -207,51 +219,42 @@ fun SpeciesListView(
|
||||
delay(100);
|
||||
aSpec.value.trigger = false
|
||||
}
|
||||
Log.e("TAG", "BLINKING")
|
||||
} else {
|
||||
}
|
||||
|
||||
} else {
|
||||
Row {
|
||||
AutoSizedText(
|
||||
text = aSpec.value.common_name,
|
||||
color = interpolateColor(aSpec.value.score),
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
textAlign = TextAlign.Center,
|
||||
key = Instant.now().toEpochMilli(),
|
||||
textDecor = text_decor,
|
||||
fontStyle = text_style,
|
||||
)
|
||||
|
||||
}
|
||||
Log.e("TAG", "Not blinking")
|
||||
}
|
||||
}
|
||||
|
||||
if (add_sep) {
|
||||
// composables += {
|
||||
AutoSizedText(
|
||||
text = "-------------------",
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
// }
|
||||
}
|
||||
|
||||
// Column {
|
||||
// composables.forEach { composable ->
|
||||
// Row {
|
||||
// composable()
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
}// Dynamically display the chips
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
// ScreenScaffold(scrollState = columnState) {
|
||||
// ScalingLazyColumn(columnState = columnState, modifier = Modifier.fillMaxSize()) {
|
||||
// items(species_show) { aSpec -> Text(text = aSpec.value.common_name)
|
||||
// } // Dynamically display the chips
|
||||
//
|
||||
// }
|
||||
// }
|
||||
|
||||
// Get current battery level
|
||||
private fun getBatteryLevel(context: Context): Int {
|
||||
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