diff --git a/app/build.gradle b/app/build.gradle
index f74a7ad..78bee0e 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -4,6 +4,8 @@ plugins {
id 'kotlin-kapt'
}
+def oneSentenceAsrAar = file('libs/asr-one-sentence-release.aar')
+
kapt {
// Room uses javac stubs under kapt; keep parameter names for :bind variables.
javacOptions {
@@ -62,6 +64,9 @@ android {
buildConfigField "String", "LLM_MODEL", "\"${(project.findProperty('LLM_MODEL') ?: 'doubao-1-5-pro-32k-character-250228').toString()}\""
buildConfigField "boolean", "USE_LIVE2D", "${(project.findProperty('USE_LIVE2D') ?: 'true').toString()}"
+ // 腾讯云「一句话识别」Android SDK:将 asr-one-sentence-release.aar 放入 app/libs/ 后为 true
+ buildConfigField "boolean", "HAS_TENCENT_ASR_SDK", "${oneSentenceAsrAar.exists()}"
+
ndk {
abiFilters "arm64-v8a"
}
@@ -119,4 +124,6 @@ dependencies {
implementation 'com.google.guava:guava:31.1-android'
implementation 'org.ejml:ejml-core:0.43.1'
implementation 'org.ejml:ejml-simple:0.43.1'
+
+ // 腾讯云「一句话识别」通过 OkHttp 直接实现 TC3 签名,无需 AAR SDK
}
diff --git a/app/libs/ASR_SDK_PLACE_AAR_HERE.txt b/app/libs/ASR_SDK_PLACE_AAR_HERE.txt
new file mode 100644
index 0000000..b646b36
--- /dev/null
+++ b/app/libs/ASR_SDK_PLACE_AAR_HERE.txt
@@ -0,0 +1,4 @@
+1. Open https://console.cloud.tencent.com/asr/download
+2. Download the Android package for real-time speech recognition (实时语音识别).
+3. Copy asr-realtime-release.aar into this folder (app/libs/).
+4. Sync Gradle — BuildConfig.HAS_TENCENT_ASR_SDK will become true.
diff --git a/app/libs/_asr.zip b/app/libs/_asr.zip
new file mode 100644
index 0000000..c1e2c26
Binary files /dev/null and b/app/libs/_asr.zip differ
diff --git a/app/libs/_asr_out/AndroidManifest.xml b/app/libs/_asr_out/AndroidManifest.xml
new file mode 100644
index 0000000..12dc170
--- /dev/null
+++ b/app/libs/_asr_out/AndroidManifest.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/libs/_asr_out/R.txt b/app/libs/_asr_out/R.txt
new file mode 100644
index 0000000..a9551e5
--- /dev/null
+++ b/app/libs/_asr_out/R.txt
@@ -0,0 +1 @@
+int string app_name 0x0
diff --git a/app/libs/_asr_out/classes.jar b/app/libs/_asr_out/classes.jar
new file mode 100644
index 0000000..1e71a13
Binary files /dev/null and b/app/libs/_asr_out/classes.jar differ
diff --git a/app/libs/_asr_out/jni/arm64-v8a/libqcloud_asr_realtime.so b/app/libs/_asr_out/jni/arm64-v8a/libqcloud_asr_realtime.so
new file mode 100644
index 0000000..caf3050
Binary files /dev/null and b/app/libs/_asr_out/jni/arm64-v8a/libqcloud_asr_realtime.so differ
diff --git a/app/libs/_asr_out/jni/armeabi-v7a/libqcloud_asr_realtime.so b/app/libs/_asr_out/jni/armeabi-v7a/libqcloud_asr_realtime.so
new file mode 100644
index 0000000..9b89c36
Binary files /dev/null and b/app/libs/_asr_out/jni/armeabi-v7a/libqcloud_asr_realtime.so differ
diff --git a/app/libs/_asr_out/jni/x86/libqcloud_asr_realtime.so b/app/libs/_asr_out/jni/x86/libqcloud_asr_realtime.so
new file mode 100644
index 0000000..af7814f
Binary files /dev/null and b/app/libs/_asr_out/jni/x86/libqcloud_asr_realtime.so differ
diff --git a/app/libs/_asr_out/jni/x86_64/libqcloud_asr_realtime.so b/app/libs/_asr_out/jni/x86_64/libqcloud_asr_realtime.so
new file mode 100644
index 0000000..9a641f2
Binary files /dev/null and b/app/libs/_asr_out/jni/x86_64/libqcloud_asr_realtime.so differ
diff --git a/app/libs/_asr_out/proguard.txt b/app/libs/_asr_out/proguard.txt
new file mode 100644
index 0000000..bd53e7f
--- /dev/null
+++ b/app/libs/_asr_out/proguard.txt
@@ -0,0 +1,9 @@
+# SDK
+-keepclasseswithmembernames class com.tencent.aai.** { # 保持 native 方法不被混淆
+native ;
+}
+
+-keep public class com.tencent.aai.** {*;}
+-keep interface com.tencent.aai.audio.data.PcmAudioDataSource {
+ void start(); throws com.tencent.aai.exception.ClientException;
+}
\ No newline at end of file
diff --git a/app/libs/_asr_out/res/values/values.xml b/app/libs/_asr_out/res/values/values.xml
new file mode 100644
index 0000000..80079fc
--- /dev/null
+++ b/app/libs/_asr_out/res/values/values.xml
@@ -0,0 +1,4 @@
+
+
+ aai
+
\ No newline at end of file
diff --git a/app/libs/_osr_out/AndroidManifest.xml b/app/libs/_osr_out/AndroidManifest.xml
new file mode 100644
index 0000000..a1ae54c
--- /dev/null
+++ b/app/libs/_osr_out/AndroidManifest.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/libs/_osr_out/R.txt b/app/libs/_osr_out/R.txt
new file mode 100644
index 0000000..8e03d55
--- /dev/null
+++ b/app/libs/_osr_out/R.txt
@@ -0,0 +1,1577 @@
+int anim abc_fade_in 0x0
+int anim abc_fade_out 0x0
+int anim abc_grow_fade_in_from_bottom 0x0
+int anim abc_popup_enter 0x0
+int anim abc_popup_exit 0x0
+int anim abc_shrink_fade_out_from_bottom 0x0
+int anim abc_slide_in_bottom 0x0
+int anim abc_slide_in_top 0x0
+int anim abc_slide_out_bottom 0x0
+int anim abc_slide_out_top 0x0
+int anim abc_tooltip_enter 0x0
+int anim abc_tooltip_exit 0x0
+int attr actionBarDivider 0x0
+int attr actionBarItemBackground 0x0
+int attr actionBarPopupTheme 0x0
+int attr actionBarSize 0x0
+int attr actionBarSplitStyle 0x0
+int attr actionBarStyle 0x0
+int attr actionBarTabBarStyle 0x0
+int attr actionBarTabStyle 0x0
+int attr actionBarTabTextStyle 0x0
+int attr actionBarTheme 0x0
+int attr actionBarWidgetTheme 0x0
+int attr actionButtonStyle 0x0
+int attr actionDropDownStyle 0x0
+int attr actionLayout 0x0
+int attr actionMenuTextAppearance 0x0
+int attr actionMenuTextColor 0x0
+int attr actionModeBackground 0x0
+int attr actionModeCloseButtonStyle 0x0
+int attr actionModeCloseDrawable 0x0
+int attr actionModeCopyDrawable 0x0
+int attr actionModeCutDrawable 0x0
+int attr actionModeFindDrawable 0x0
+int attr actionModePasteDrawable 0x0
+int attr actionModePopupWindowStyle 0x0
+int attr actionModeSelectAllDrawable 0x0
+int attr actionModeShareDrawable 0x0
+int attr actionModeSplitBackground 0x0
+int attr actionModeStyle 0x0
+int attr actionModeWebSearchDrawable 0x0
+int attr actionOverflowButtonStyle 0x0
+int attr actionOverflowMenuStyle 0x0
+int attr actionProviderClass 0x0
+int attr actionViewClass 0x0
+int attr activityChooserViewStyle 0x0
+int attr alertDialogButtonGroupStyle 0x0
+int attr alertDialogCenterButtons 0x0
+int attr alertDialogStyle 0x0
+int attr alertDialogTheme 0x0
+int attr allowStacking 0x0
+int attr alpha 0x0
+int attr alphabeticModifiers 0x0
+int attr arrowHeadLength 0x0
+int attr arrowShaftLength 0x0
+int attr autoCompleteTextViewStyle 0x0
+int attr autoSizeMaxTextSize 0x0
+int attr autoSizeMinTextSize 0x0
+int attr autoSizePresetSizes 0x0
+int attr autoSizeStepGranularity 0x0
+int attr autoSizeTextType 0x0
+int attr background 0x0
+int attr backgroundSplit 0x0
+int attr backgroundStacked 0x0
+int attr backgroundTint 0x0
+int attr backgroundTintMode 0x0
+int attr barLength 0x0
+int attr borderlessButtonStyle 0x0
+int attr buttonBarButtonStyle 0x0
+int attr buttonBarNegativeButtonStyle 0x0
+int attr buttonBarNeutralButtonStyle 0x0
+int attr buttonBarPositiveButtonStyle 0x0
+int attr buttonBarStyle 0x0
+int attr buttonGravity 0x0
+int attr buttonIconDimen 0x0
+int attr buttonPanelSideLayout 0x0
+int attr buttonStyle 0x0
+int attr buttonStyleSmall 0x0
+int attr buttonTint 0x0
+int attr buttonTintMode 0x0
+int attr checkboxStyle 0x0
+int attr checkedTextViewStyle 0x0
+int attr closeIcon 0x0
+int attr closeItemLayout 0x0
+int attr collapseContentDescription 0x0
+int attr collapseIcon 0x0
+int attr color 0x0
+int attr colorAccent 0x0
+int attr colorBackgroundFloating 0x0
+int attr colorButtonNormal 0x0
+int attr colorControlActivated 0x0
+int attr colorControlHighlight 0x0
+int attr colorControlNormal 0x0
+int attr colorError 0x0
+int attr colorPrimary 0x0
+int attr colorPrimaryDark 0x0
+int attr colorSwitchThumbNormal 0x0
+int attr commitIcon 0x0
+int attr contentDescription 0x0
+int attr contentInsetEnd 0x0
+int attr contentInsetEndWithActions 0x0
+int attr contentInsetLeft 0x0
+int attr contentInsetRight 0x0
+int attr contentInsetStart 0x0
+int attr contentInsetStartWithNavigation 0x0
+int attr controlBackground 0x0
+int attr coordinatorLayoutStyle 0x0
+int attr customNavigationLayout 0x0
+int attr defaultQueryHint 0x0
+int attr dialogCornerRadius 0x0
+int attr dialogPreferredPadding 0x0
+int attr dialogTheme 0x0
+int attr displayOptions 0x0
+int attr divider 0x0
+int attr dividerHorizontal 0x0
+int attr dividerPadding 0x0
+int attr dividerVertical 0x0
+int attr drawableSize 0x0
+int attr drawerArrowStyle 0x0
+int attr dropDownListViewStyle 0x0
+int attr dropdownListPreferredItemHeight 0x0
+int attr editTextBackground 0x0
+int attr editTextColor 0x0
+int attr editTextStyle 0x0
+int attr elevation 0x0
+int attr expandActivityOverflowButtonDrawable 0x0
+int attr firstBaselineToTopHeight 0x0
+int attr font 0x0
+int attr fontFamily 0x0
+int attr fontProviderAuthority 0x0
+int attr fontProviderCerts 0x0
+int attr fontProviderFetchStrategy 0x0
+int attr fontProviderFetchTimeout 0x0
+int attr fontProviderPackage 0x0
+int attr fontProviderQuery 0x0
+int attr fontStyle 0x0
+int attr fontVariationSettings 0x0
+int attr fontWeight 0x0
+int attr gapBetweenBars 0x0
+int attr goIcon 0x0
+int attr height 0x0
+int attr hideOnContentScroll 0x0
+int attr homeAsUpIndicator 0x0
+int attr homeLayout 0x0
+int attr icon 0x0
+int attr iconTint 0x0
+int attr iconTintMode 0x0
+int attr iconifiedByDefault 0x0
+int attr imageButtonStyle 0x0
+int attr indeterminateProgressStyle 0x0
+int attr initialActivityCount 0x0
+int attr isLightTheme 0x0
+int attr itemPadding 0x0
+int attr keylines 0x0
+int attr lastBaselineToBottomHeight 0x0
+int attr layout 0x0
+int attr layout_anchor 0x0
+int attr layout_anchorGravity 0x0
+int attr layout_behavior 0x0
+int attr layout_dodgeInsetEdges 0x0
+int attr layout_insetEdge 0x0
+int attr layout_keyline 0x0
+int attr lineHeight 0x0
+int attr listChoiceBackgroundIndicator 0x0
+int attr listDividerAlertDialog 0x0
+int attr listItemLayout 0x0
+int attr listLayout 0x0
+int attr listMenuViewStyle 0x0
+int attr listPopupWindowStyle 0x0
+int attr listPreferredItemHeight 0x0
+int attr listPreferredItemHeightLarge 0x0
+int attr listPreferredItemHeightSmall 0x0
+int attr listPreferredItemPaddingLeft 0x0
+int attr listPreferredItemPaddingRight 0x0
+int attr logo 0x0
+int attr logoDescription 0x0
+int attr maxButtonHeight 0x0
+int attr measureWithLargestChild 0x0
+int attr multiChoiceItemLayout 0x0
+int attr navigationContentDescription 0x0
+int attr navigationIcon 0x0
+int attr navigationMode 0x0
+int attr numericModifiers 0x0
+int attr overlapAnchor 0x0
+int attr paddingBottomNoButtons 0x0
+int attr paddingEnd 0x0
+int attr paddingStart 0x0
+int attr paddingTopNoTitle 0x0
+int attr panelBackground 0x0
+int attr panelMenuListTheme 0x0
+int attr panelMenuListWidth 0x0
+int attr popupMenuStyle 0x0
+int attr popupTheme 0x0
+int attr popupWindowStyle 0x0
+int attr preserveIconSpacing 0x0
+int attr progressBarPadding 0x0
+int attr progressBarStyle 0x0
+int attr queryBackground 0x0
+int attr queryHint 0x0
+int attr radioButtonStyle 0x0
+int attr ratingBarStyle 0x0
+int attr ratingBarStyleIndicator 0x0
+int attr ratingBarStyleSmall 0x0
+int attr searchHintIcon 0x0
+int attr searchIcon 0x0
+int attr searchViewStyle 0x0
+int attr seekBarStyle 0x0
+int attr selectableItemBackground 0x0
+int attr selectableItemBackgroundBorderless 0x0
+int attr showAsAction 0x0
+int attr showDividers 0x0
+int attr showText 0x0
+int attr showTitle 0x0
+int attr singleChoiceItemLayout 0x0
+int attr spinBars 0x0
+int attr spinnerDropDownItemStyle 0x0
+int attr spinnerStyle 0x0
+int attr splitTrack 0x0
+int attr srcCompat 0x0
+int attr state_above_anchor 0x0
+int attr statusBarBackground 0x0
+int attr subMenuArrow 0x0
+int attr submitBackground 0x0
+int attr subtitle 0x0
+int attr subtitleTextAppearance 0x0
+int attr subtitleTextColor 0x0
+int attr subtitleTextStyle 0x0
+int attr suggestionRowLayout 0x0
+int attr switchMinWidth 0x0
+int attr switchPadding 0x0
+int attr switchStyle 0x0
+int attr switchTextAppearance 0x0
+int attr textAllCaps 0x0
+int attr textAppearanceLargePopupMenu 0x0
+int attr textAppearanceListItem 0x0
+int attr textAppearanceListItemSecondary 0x0
+int attr textAppearanceListItemSmall 0x0
+int attr textAppearancePopupMenuHeader 0x0
+int attr textAppearanceSearchResultSubtitle 0x0
+int attr textAppearanceSearchResultTitle 0x0
+int attr textAppearanceSmallPopupMenu 0x0
+int attr textColorAlertDialogListItem 0x0
+int attr textColorSearchUrl 0x0
+int attr theme 0x0
+int attr thickness 0x0
+int attr thumbTextPadding 0x0
+int attr thumbTint 0x0
+int attr thumbTintMode 0x0
+int attr tickMark 0x0
+int attr tickMarkTint 0x0
+int attr tickMarkTintMode 0x0
+int attr tint 0x0
+int attr tintMode 0x0
+int attr title 0x0
+int attr titleMargin 0x0
+int attr titleMarginBottom 0x0
+int attr titleMarginEnd 0x0
+int attr titleMarginStart 0x0
+int attr titleMarginTop 0x0
+int attr titleMargins 0x0
+int attr titleTextAppearance 0x0
+int attr titleTextColor 0x0
+int attr titleTextStyle 0x0
+int attr toolbarNavigationButtonStyle 0x0
+int attr toolbarStyle 0x0
+int attr tooltipForegroundColor 0x0
+int attr tooltipFrameBackground 0x0
+int attr tooltipText 0x0
+int attr track 0x0
+int attr trackTint 0x0
+int attr trackTintMode 0x0
+int attr ttcIndex 0x0
+int attr viewInflaterClass 0x0
+int attr voiceIcon 0x0
+int attr windowActionBar 0x0
+int attr windowActionBarOverlay 0x0
+int attr windowActionModeOverlay 0x0
+int attr windowFixedHeightMajor 0x0
+int attr windowFixedHeightMinor 0x0
+int attr windowFixedWidthMajor 0x0
+int attr windowFixedWidthMinor 0x0
+int attr windowMinWidthMajor 0x0
+int attr windowMinWidthMinor 0x0
+int attr windowNoTitle 0x0
+int bool abc_action_bar_embed_tabs 0x0
+int bool abc_allow_stacked_button_bar 0x0
+int bool abc_config_actionMenuItemAllCaps 0x0
+int color abc_background_cache_hint_selector_material_dark 0x0
+int color abc_background_cache_hint_selector_material_light 0x0
+int color abc_btn_colored_borderless_text_material 0x0
+int color abc_btn_colored_text_material 0x0
+int color abc_color_highlight_material 0x0
+int color abc_hint_foreground_material_dark 0x0
+int color abc_hint_foreground_material_light 0x0
+int color abc_input_method_navigation_guard 0x0
+int color abc_primary_text_disable_only_material_dark 0x0
+int color abc_primary_text_disable_only_material_light 0x0
+int color abc_primary_text_material_dark 0x0
+int color abc_primary_text_material_light 0x0
+int color abc_search_url_text 0x0
+int color abc_search_url_text_normal 0x0
+int color abc_search_url_text_pressed 0x0
+int color abc_search_url_text_selected 0x0
+int color abc_secondary_text_material_dark 0x0
+int color abc_secondary_text_material_light 0x0
+int color abc_tint_btn_checkable 0x0
+int color abc_tint_default 0x0
+int color abc_tint_edittext 0x0
+int color abc_tint_seek_thumb 0x0
+int color abc_tint_spinner 0x0
+int color abc_tint_switch_track 0x0
+int color accent_material_dark 0x0
+int color accent_material_light 0x0
+int color background_floating_material_dark 0x0
+int color background_floating_material_light 0x0
+int color background_material_dark 0x0
+int color background_material_light 0x0
+int color bright_foreground_disabled_material_dark 0x0
+int color bright_foreground_disabled_material_light 0x0
+int color bright_foreground_inverse_material_dark 0x0
+int color bright_foreground_inverse_material_light 0x0
+int color bright_foreground_material_dark 0x0
+int color bright_foreground_material_light 0x0
+int color button_material_dark 0x0
+int color button_material_light 0x0
+int color dim_foreground_disabled_material_dark 0x0
+int color dim_foreground_disabled_material_light 0x0
+int color dim_foreground_material_dark 0x0
+int color dim_foreground_material_light 0x0
+int color error_color_material_dark 0x0
+int color error_color_material_light 0x0
+int color foreground_material_dark 0x0
+int color foreground_material_light 0x0
+int color highlighted_text_material_dark 0x0
+int color highlighted_text_material_light 0x0
+int color material_blue_grey_800 0x0
+int color material_blue_grey_900 0x0
+int color material_blue_grey_950 0x0
+int color material_deep_teal_200 0x0
+int color material_deep_teal_500 0x0
+int color material_grey_100 0x0
+int color material_grey_300 0x0
+int color material_grey_50 0x0
+int color material_grey_600 0x0
+int color material_grey_800 0x0
+int color material_grey_850 0x0
+int color material_grey_900 0x0
+int color notification_action_color_filter 0x0
+int color notification_icon_bg_color 0x0
+int color primary_dark_material_dark 0x0
+int color primary_dark_material_light 0x0
+int color primary_material_dark 0x0
+int color primary_material_light 0x0
+int color primary_text_default_material_dark 0x0
+int color primary_text_default_material_light 0x0
+int color primary_text_disabled_material_dark 0x0
+int color primary_text_disabled_material_light 0x0
+int color ripple_material_dark 0x0
+int color ripple_material_light 0x0
+int color secondary_text_default_material_dark 0x0
+int color secondary_text_default_material_light 0x0
+int color secondary_text_disabled_material_dark 0x0
+int color secondary_text_disabled_material_light 0x0
+int color switch_thumb_disabled_material_dark 0x0
+int color switch_thumb_disabled_material_light 0x0
+int color switch_thumb_material_dark 0x0
+int color switch_thumb_material_light 0x0
+int color switch_thumb_normal_material_dark 0x0
+int color switch_thumb_normal_material_light 0x0
+int color tooltip_background_dark 0x0
+int color tooltip_background_light 0x0
+int dimen abc_action_bar_content_inset_material 0x0
+int dimen abc_action_bar_content_inset_with_nav 0x0
+int dimen abc_action_bar_default_height_material 0x0
+int dimen abc_action_bar_default_padding_end_material 0x0
+int dimen abc_action_bar_default_padding_start_material 0x0
+int dimen abc_action_bar_elevation_material 0x0
+int dimen abc_action_bar_icon_vertical_padding_material 0x0
+int dimen abc_action_bar_overflow_padding_end_material 0x0
+int dimen abc_action_bar_overflow_padding_start_material 0x0
+int dimen abc_action_bar_stacked_max_height 0x0
+int dimen abc_action_bar_stacked_tab_max_width 0x0
+int dimen abc_action_bar_subtitle_bottom_margin_material 0x0
+int dimen abc_action_bar_subtitle_top_margin_material 0x0
+int dimen abc_action_button_min_height_material 0x0
+int dimen abc_action_button_min_width_material 0x0
+int dimen abc_action_button_min_width_overflow_material 0x0
+int dimen abc_alert_dialog_button_bar_height 0x0
+int dimen abc_alert_dialog_button_dimen 0x0
+int dimen abc_button_inset_horizontal_material 0x0
+int dimen abc_button_inset_vertical_material 0x0
+int dimen abc_button_padding_horizontal_material 0x0
+int dimen abc_button_padding_vertical_material 0x0
+int dimen abc_cascading_menus_min_smallest_width 0x0
+int dimen abc_config_prefDialogWidth 0x0
+int dimen abc_control_corner_material 0x0
+int dimen abc_control_inset_material 0x0
+int dimen abc_control_padding_material 0x0
+int dimen abc_dialog_corner_radius_material 0x0
+int dimen abc_dialog_fixed_height_major 0x0
+int dimen abc_dialog_fixed_height_minor 0x0
+int dimen abc_dialog_fixed_width_major 0x0
+int dimen abc_dialog_fixed_width_minor 0x0
+int dimen abc_dialog_list_padding_bottom_no_buttons 0x0
+int dimen abc_dialog_list_padding_top_no_title 0x0
+int dimen abc_dialog_min_width_major 0x0
+int dimen abc_dialog_min_width_minor 0x0
+int dimen abc_dialog_padding_material 0x0
+int dimen abc_dialog_padding_top_material 0x0
+int dimen abc_dialog_title_divider_material 0x0
+int dimen abc_disabled_alpha_material_dark 0x0
+int dimen abc_disabled_alpha_material_light 0x0
+int dimen abc_dropdownitem_icon_width 0x0
+int dimen abc_dropdownitem_text_padding_left 0x0
+int dimen abc_dropdownitem_text_padding_right 0x0
+int dimen abc_edit_text_inset_bottom_material 0x0
+int dimen abc_edit_text_inset_horizontal_material 0x0
+int dimen abc_edit_text_inset_top_material 0x0
+int dimen abc_floating_window_z 0x0
+int dimen abc_list_item_padding_horizontal_material 0x0
+int dimen abc_panel_menu_list_width 0x0
+int dimen abc_progress_bar_height_material 0x0
+int dimen abc_search_view_preferred_height 0x0
+int dimen abc_search_view_preferred_width 0x0
+int dimen abc_seekbar_track_background_height_material 0x0
+int dimen abc_seekbar_track_progress_height_material 0x0
+int dimen abc_select_dialog_padding_start_material 0x0
+int dimen abc_switch_padding 0x0
+int dimen abc_text_size_body_1_material 0x0
+int dimen abc_text_size_body_2_material 0x0
+int dimen abc_text_size_button_material 0x0
+int dimen abc_text_size_caption_material 0x0
+int dimen abc_text_size_display_1_material 0x0
+int dimen abc_text_size_display_2_material 0x0
+int dimen abc_text_size_display_3_material 0x0
+int dimen abc_text_size_display_4_material 0x0
+int dimen abc_text_size_headline_material 0x0
+int dimen abc_text_size_large_material 0x0
+int dimen abc_text_size_medium_material 0x0
+int dimen abc_text_size_menu_header_material 0x0
+int dimen abc_text_size_menu_material 0x0
+int dimen abc_text_size_small_material 0x0
+int dimen abc_text_size_subhead_material 0x0
+int dimen abc_text_size_subtitle_material_toolbar 0x0
+int dimen abc_text_size_title_material 0x0
+int dimen abc_text_size_title_material_toolbar 0x0
+int dimen compat_button_inset_horizontal_material 0x0
+int dimen compat_button_inset_vertical_material 0x0
+int dimen compat_button_padding_horizontal_material 0x0
+int dimen compat_button_padding_vertical_material 0x0
+int dimen compat_control_corner_material 0x0
+int dimen compat_notification_large_icon_max_height 0x0
+int dimen compat_notification_large_icon_max_width 0x0
+int dimen disabled_alpha_material_dark 0x0
+int dimen disabled_alpha_material_light 0x0
+int dimen highlight_alpha_material_colored 0x0
+int dimen highlight_alpha_material_dark 0x0
+int dimen highlight_alpha_material_light 0x0
+int dimen hint_alpha_material_dark 0x0
+int dimen hint_alpha_material_light 0x0
+int dimen hint_pressed_alpha_material_dark 0x0
+int dimen hint_pressed_alpha_material_light 0x0
+int dimen notification_action_icon_size 0x0
+int dimen notification_action_text_size 0x0
+int dimen notification_big_circle_margin 0x0
+int dimen notification_content_margin_start 0x0
+int dimen notification_large_icon_height 0x0
+int dimen notification_large_icon_width 0x0
+int dimen notification_main_column_padding_top 0x0
+int dimen notification_media_narrow_margin 0x0
+int dimen notification_right_icon_size 0x0
+int dimen notification_right_side_padding_top 0x0
+int dimen notification_small_icon_background_padding 0x0
+int dimen notification_small_icon_size_as_large 0x0
+int dimen notification_subtext_size 0x0
+int dimen notification_top_pad 0x0
+int dimen notification_top_pad_large_text 0x0
+int dimen tooltip_corner_radius 0x0
+int dimen tooltip_horizontal_padding 0x0
+int dimen tooltip_margin 0x0
+int dimen tooltip_precise_anchor_extra_offset 0x0
+int dimen tooltip_precise_anchor_threshold 0x0
+int dimen tooltip_vertical_padding 0x0
+int dimen tooltip_y_offset_non_touch 0x0
+int dimen tooltip_y_offset_touch 0x0
+int drawable abc_ab_share_pack_mtrl_alpha 0x0
+int drawable abc_action_bar_item_background_material 0x0
+int drawable abc_btn_borderless_material 0x0
+int drawable abc_btn_check_material 0x0
+int drawable abc_btn_check_to_on_mtrl_000 0x0
+int drawable abc_btn_check_to_on_mtrl_015 0x0
+int drawable abc_btn_colored_material 0x0
+int drawable abc_btn_default_mtrl_shape 0x0
+int drawable abc_btn_radio_material 0x0
+int drawable abc_btn_radio_to_on_mtrl_000 0x0
+int drawable abc_btn_radio_to_on_mtrl_015 0x0
+int drawable abc_btn_switch_to_on_mtrl_00001 0x0
+int drawable abc_btn_switch_to_on_mtrl_00012 0x0
+int drawable abc_cab_background_internal_bg 0x0
+int drawable abc_cab_background_top_material 0x0
+int drawable abc_cab_background_top_mtrl_alpha 0x0
+int drawable abc_control_background_material 0x0
+int drawable abc_dialog_material_background 0x0
+int drawable abc_edit_text_material 0x0
+int drawable abc_ic_ab_back_material 0x0
+int drawable abc_ic_arrow_drop_right_black_24dp 0x0
+int drawable abc_ic_clear_material 0x0
+int drawable abc_ic_commit_search_api_mtrl_alpha 0x0
+int drawable abc_ic_go_search_api_material 0x0
+int drawable abc_ic_menu_copy_mtrl_am_alpha 0x0
+int drawable abc_ic_menu_cut_mtrl_alpha 0x0
+int drawable abc_ic_menu_overflow_material 0x0
+int drawable abc_ic_menu_paste_mtrl_am_alpha 0x0
+int drawable abc_ic_menu_selectall_mtrl_alpha 0x0
+int drawable abc_ic_menu_share_mtrl_alpha 0x0
+int drawable abc_ic_search_api_material 0x0
+int drawable abc_ic_star_black_16dp 0x0
+int drawable abc_ic_star_black_36dp 0x0
+int drawable abc_ic_star_black_48dp 0x0
+int drawable abc_ic_star_half_black_16dp 0x0
+int drawable abc_ic_star_half_black_36dp 0x0
+int drawable abc_ic_star_half_black_48dp 0x0
+int drawable abc_ic_voice_search_api_material 0x0
+int drawable abc_item_background_holo_dark 0x0
+int drawable abc_item_background_holo_light 0x0
+int drawable abc_list_divider_material 0x0
+int drawable abc_list_divider_mtrl_alpha 0x0
+int drawable abc_list_focused_holo 0x0
+int drawable abc_list_longpressed_holo 0x0
+int drawable abc_list_pressed_holo_dark 0x0
+int drawable abc_list_pressed_holo_light 0x0
+int drawable abc_list_selector_background_transition_holo_dark 0x0
+int drawable abc_list_selector_background_transition_holo_light 0x0
+int drawable abc_list_selector_disabled_holo_dark 0x0
+int drawable abc_list_selector_disabled_holo_light 0x0
+int drawable abc_list_selector_holo_dark 0x0
+int drawable abc_list_selector_holo_light 0x0
+int drawable abc_menu_hardkey_panel_mtrl_mult 0x0
+int drawable abc_popup_background_mtrl_mult 0x0
+int drawable abc_ratingbar_indicator_material 0x0
+int drawable abc_ratingbar_material 0x0
+int drawable abc_ratingbar_small_material 0x0
+int drawable abc_scrubber_control_off_mtrl_alpha 0x0
+int drawable abc_scrubber_control_to_pressed_mtrl_000 0x0
+int drawable abc_scrubber_control_to_pressed_mtrl_005 0x0
+int drawable abc_scrubber_primary_mtrl_alpha 0x0
+int drawable abc_scrubber_track_mtrl_alpha 0x0
+int drawable abc_seekbar_thumb_material 0x0
+int drawable abc_seekbar_tick_mark_material 0x0
+int drawable abc_seekbar_track_material 0x0
+int drawable abc_spinner_mtrl_am_alpha 0x0
+int drawable abc_spinner_textfield_background_material 0x0
+int drawable abc_switch_thumb_material 0x0
+int drawable abc_switch_track_mtrl_alpha 0x0
+int drawable abc_tab_indicator_material 0x0
+int drawable abc_tab_indicator_mtrl_alpha 0x0
+int drawable abc_text_cursor_material 0x0
+int drawable abc_text_select_handle_left_mtrl_dark 0x0
+int drawable abc_text_select_handle_left_mtrl_light 0x0
+int drawable abc_text_select_handle_middle_mtrl_dark 0x0
+int drawable abc_text_select_handle_middle_mtrl_light 0x0
+int drawable abc_text_select_handle_right_mtrl_dark 0x0
+int drawable abc_text_select_handle_right_mtrl_light 0x0
+int drawable abc_textfield_activated_mtrl_alpha 0x0
+int drawable abc_textfield_default_mtrl_alpha 0x0
+int drawable abc_textfield_search_activated_mtrl_alpha 0x0
+int drawable abc_textfield_search_default_mtrl_alpha 0x0
+int drawable abc_textfield_search_material 0x0
+int drawable abc_vector_test 0x0
+int drawable notification_action_background 0x0
+int drawable notification_bg 0x0
+int drawable notification_bg_low 0x0
+int drawable notification_bg_low_normal 0x0
+int drawable notification_bg_low_pressed 0x0
+int drawable notification_bg_normal 0x0
+int drawable notification_bg_normal_pressed 0x0
+int drawable notification_icon_background 0x0
+int drawable notification_template_icon_bg 0x0
+int drawable notification_template_icon_low_bg 0x0
+int drawable notification_tile_bg 0x0
+int drawable notify_panel_notification_icon_bg 0x0
+int drawable tooltip_frame_dark 0x0
+int drawable tooltip_frame_light 0x0
+int id action_bar 0x0
+int id action_bar_activity_content 0x0
+int id action_bar_container 0x0
+int id action_bar_root 0x0
+int id action_bar_spinner 0x0
+int id action_bar_subtitle 0x0
+int id action_bar_title 0x0
+int id action_container 0x0
+int id action_context_bar 0x0
+int id action_divider 0x0
+int id action_image 0x0
+int id action_menu_divider 0x0
+int id action_menu_presenter 0x0
+int id action_mode_bar 0x0
+int id action_mode_bar_stub 0x0
+int id action_mode_close_button 0x0
+int id action_text 0x0
+int id actions 0x0
+int id activity_chooser_view_content 0x0
+int id add 0x0
+int id alertTitle 0x0
+int id async 0x0
+int id blocking 0x0
+int id bottom 0x0
+int id buttonPanel 0x0
+int id checkbox 0x0
+int id chronometer 0x0
+int id content 0x0
+int id contentPanel 0x0
+int id custom 0x0
+int id customPanel 0x0
+int id decor_content_parent 0x0
+int id default_activity_button 0x0
+int id edit_query 0x0
+int id end 0x0
+int id expand_activities_button 0x0
+int id expanded_menu 0x0
+int id forever 0x0
+int id group_divider 0x0
+int id home 0x0
+int id icon 0x0
+int id icon_group 0x0
+int id image 0x0
+int id info 0x0
+int id italic 0x0
+int id left 0x0
+int id line1 0x0
+int id line3 0x0
+int id listMode 0x0
+int id list_item 0x0
+int id message 0x0
+int id multiply 0x0
+int id none 0x0
+int id normal 0x0
+int id notification_background 0x0
+int id notification_main_column 0x0
+int id notification_main_column_container 0x0
+int id parentPanel 0x0
+int id progress_circular 0x0
+int id progress_horizontal 0x0
+int id radio 0x0
+int id right 0x0
+int id right_icon 0x0
+int id right_side 0x0
+int id screen 0x0
+int id scrollIndicatorDown 0x0
+int id scrollIndicatorUp 0x0
+int id scrollView 0x0
+int id search_badge 0x0
+int id search_bar 0x0
+int id search_button 0x0
+int id search_close_btn 0x0
+int id search_edit_frame 0x0
+int id search_go_btn 0x0
+int id search_mag_icon 0x0
+int id search_plate 0x0
+int id search_src_text 0x0
+int id search_voice_btn 0x0
+int id select_dialog_listview 0x0
+int id shortcut 0x0
+int id spacer 0x0
+int id split_action_bar 0x0
+int id src_atop 0x0
+int id src_in 0x0
+int id src_over 0x0
+int id start 0x0
+int id submenuarrow 0x0
+int id submit_area 0x0
+int id tabMode 0x0
+int id tag_transition_group 0x0
+int id tag_unhandled_key_event_manager 0x0
+int id tag_unhandled_key_listeners 0x0
+int id text 0x0
+int id text2 0x0
+int id textSpacerNoButtons 0x0
+int id textSpacerNoTitle 0x0
+int id time 0x0
+int id title 0x0
+int id titleDividerNoCustom 0x0
+int id title_template 0x0
+int id top 0x0
+int id topPanel 0x0
+int id uniform 0x0
+int id up 0x0
+int id wrap_content 0x0
+int integer abc_config_activityDefaultDur 0x0
+int integer abc_config_activityShortDur 0x0
+int integer cancel_button_image_alpha 0x0
+int integer config_tooltipAnimTime 0x0
+int integer status_bar_notification_info_maxnum 0x0
+int layout abc_action_bar_title_item 0x0
+int layout abc_action_bar_up_container 0x0
+int layout abc_action_menu_item_layout 0x0
+int layout abc_action_menu_layout 0x0
+int layout abc_action_mode_bar 0x0
+int layout abc_action_mode_close_item_material 0x0
+int layout abc_activity_chooser_view 0x0
+int layout abc_activity_chooser_view_list_item 0x0
+int layout abc_alert_dialog_button_bar_material 0x0
+int layout abc_alert_dialog_material 0x0
+int layout abc_alert_dialog_title_material 0x0
+int layout abc_cascading_menu_item_layout 0x0
+int layout abc_dialog_title_material 0x0
+int layout abc_expanded_menu_layout 0x0
+int layout abc_list_menu_item_checkbox 0x0
+int layout abc_list_menu_item_icon 0x0
+int layout abc_list_menu_item_layout 0x0
+int layout abc_list_menu_item_radio 0x0
+int layout abc_popup_menu_header_item_layout 0x0
+int layout abc_popup_menu_item_layout 0x0
+int layout abc_screen_content_include 0x0
+int layout abc_screen_simple 0x0
+int layout abc_screen_simple_overlay_action_mode 0x0
+int layout abc_screen_toolbar 0x0
+int layout abc_search_dropdown_item_icons_2line 0x0
+int layout abc_search_view 0x0
+int layout abc_select_dialog_material 0x0
+int layout abc_tooltip 0x0
+int layout notification_action 0x0
+int layout notification_action_tombstone 0x0
+int layout notification_template_custom_big 0x0
+int layout notification_template_icon_group 0x0
+int layout notification_template_part_chronometer 0x0
+int layout notification_template_part_time 0x0
+int layout select_dialog_item_material 0x0
+int layout select_dialog_multichoice_material 0x0
+int layout select_dialog_singlechoice_material 0x0
+int layout support_simple_spinner_dropdown_item 0x0
+int string abc_action_bar_home_description 0x0
+int string abc_action_bar_up_description 0x0
+int string abc_action_menu_overflow_description 0x0
+int string abc_action_mode_done 0x0
+int string abc_activity_chooser_view_see_all 0x0
+int string abc_activitychooserview_choose_application 0x0
+int string abc_capital_off 0x0
+int string abc_capital_on 0x0
+int string abc_font_family_body_1_material 0x0
+int string abc_font_family_body_2_material 0x0
+int string abc_font_family_button_material 0x0
+int string abc_font_family_caption_material 0x0
+int string abc_font_family_display_1_material 0x0
+int string abc_font_family_display_2_material 0x0
+int string abc_font_family_display_3_material 0x0
+int string abc_font_family_display_4_material 0x0
+int string abc_font_family_headline_material 0x0
+int string abc_font_family_menu_material 0x0
+int string abc_font_family_subhead_material 0x0
+int string abc_font_family_title_material 0x0
+int string abc_menu_alt_shortcut_label 0x0
+int string abc_menu_ctrl_shortcut_label 0x0
+int string abc_menu_delete_shortcut_label 0x0
+int string abc_menu_enter_shortcut_label 0x0
+int string abc_menu_function_shortcut_label 0x0
+int string abc_menu_meta_shortcut_label 0x0
+int string abc_menu_shift_shortcut_label 0x0
+int string abc_menu_space_shortcut_label 0x0
+int string abc_menu_sym_shortcut_label 0x0
+int string abc_prepend_shortcut_label 0x0
+int string abc_search_hint 0x0
+int string abc_searchview_description_clear 0x0
+int string abc_searchview_description_query 0x0
+int string abc_searchview_description_search 0x0
+int string abc_searchview_description_submit 0x0
+int string abc_searchview_description_voice 0x0
+int string abc_shareactionprovider_share_with 0x0
+int string abc_shareactionprovider_share_with_application 0x0
+int string abc_toolbar_collapse_description 0x0
+int string search_menu_title 0x0
+int string status_bar_notification_info_overflow 0x0
+int style AlertDialog_AppCompat 0x0
+int style AlertDialog_AppCompat_Light 0x0
+int style Animation_AppCompat_Dialog 0x0
+int style Animation_AppCompat_DropDownUp 0x0
+int style Animation_AppCompat_Tooltip 0x0
+int style Base_AlertDialog_AppCompat 0x0
+int style Base_AlertDialog_AppCompat_Light 0x0
+int style Base_Animation_AppCompat_Dialog 0x0
+int style Base_Animation_AppCompat_DropDownUp 0x0
+int style Base_Animation_AppCompat_Tooltip 0x0
+int style Base_DialogWindowTitleBackground_AppCompat 0x0
+int style Base_DialogWindowTitle_AppCompat 0x0
+int style Base_TextAppearance_AppCompat 0x0
+int style Base_TextAppearance_AppCompat_Body1 0x0
+int style Base_TextAppearance_AppCompat_Body2 0x0
+int style Base_TextAppearance_AppCompat_Button 0x0
+int style Base_TextAppearance_AppCompat_Caption 0x0
+int style Base_TextAppearance_AppCompat_Display1 0x0
+int style Base_TextAppearance_AppCompat_Display2 0x0
+int style Base_TextAppearance_AppCompat_Display3 0x0
+int style Base_TextAppearance_AppCompat_Display4 0x0
+int style Base_TextAppearance_AppCompat_Headline 0x0
+int style Base_TextAppearance_AppCompat_Inverse 0x0
+int style Base_TextAppearance_AppCompat_Large 0x0
+int style Base_TextAppearance_AppCompat_Large_Inverse 0x0
+int style Base_TextAppearance_AppCompat_Light_Widget_PopupMenu_Large 0x0
+int style Base_TextAppearance_AppCompat_Light_Widget_PopupMenu_Small 0x0
+int style Base_TextAppearance_AppCompat_Medium 0x0
+int style Base_TextAppearance_AppCompat_Medium_Inverse 0x0
+int style Base_TextAppearance_AppCompat_Menu 0x0
+int style Base_TextAppearance_AppCompat_SearchResult 0x0
+int style Base_TextAppearance_AppCompat_SearchResult_Subtitle 0x0
+int style Base_TextAppearance_AppCompat_SearchResult_Title 0x0
+int style Base_TextAppearance_AppCompat_Small 0x0
+int style Base_TextAppearance_AppCompat_Small_Inverse 0x0
+int style Base_TextAppearance_AppCompat_Subhead 0x0
+int style Base_TextAppearance_AppCompat_Subhead_Inverse 0x0
+int style Base_TextAppearance_AppCompat_Title 0x0
+int style Base_TextAppearance_AppCompat_Title_Inverse 0x0
+int style Base_TextAppearance_AppCompat_Tooltip 0x0
+int style Base_TextAppearance_AppCompat_Widget_ActionBar_Menu 0x0
+int style Base_TextAppearance_AppCompat_Widget_ActionBar_Subtitle 0x0
+int style Base_TextAppearance_AppCompat_Widget_ActionBar_Subtitle_Inverse 0x0
+int style Base_TextAppearance_AppCompat_Widget_ActionBar_Title 0x0
+int style Base_TextAppearance_AppCompat_Widget_ActionBar_Title_Inverse 0x0
+int style Base_TextAppearance_AppCompat_Widget_ActionMode_Subtitle 0x0
+int style Base_TextAppearance_AppCompat_Widget_ActionMode_Title 0x0
+int style Base_TextAppearance_AppCompat_Widget_Button 0x0
+int style Base_TextAppearance_AppCompat_Widget_Button_Borderless_Colored 0x0
+int style Base_TextAppearance_AppCompat_Widget_Button_Colored 0x0
+int style Base_TextAppearance_AppCompat_Widget_Button_Inverse 0x0
+int style Base_TextAppearance_AppCompat_Widget_DropDownItem 0x0
+int style Base_TextAppearance_AppCompat_Widget_PopupMenu_Header 0x0
+int style Base_TextAppearance_AppCompat_Widget_PopupMenu_Large 0x0
+int style Base_TextAppearance_AppCompat_Widget_PopupMenu_Small 0x0
+int style Base_TextAppearance_AppCompat_Widget_Switch 0x0
+int style Base_TextAppearance_AppCompat_Widget_TextView_SpinnerItem 0x0
+int style Base_TextAppearance_Widget_AppCompat_ExpandedMenu_Item 0x0
+int style Base_TextAppearance_Widget_AppCompat_Toolbar_Subtitle 0x0
+int style Base_TextAppearance_Widget_AppCompat_Toolbar_Title 0x0
+int style Base_ThemeOverlay_AppCompat 0x0
+int style Base_ThemeOverlay_AppCompat_ActionBar 0x0
+int style Base_ThemeOverlay_AppCompat_Dark 0x0
+int style Base_ThemeOverlay_AppCompat_Dark_ActionBar 0x0
+int style Base_ThemeOverlay_AppCompat_Dialog 0x0
+int style Base_ThemeOverlay_AppCompat_Dialog_Alert 0x0
+int style Base_ThemeOverlay_AppCompat_Light 0x0
+int style Base_Theme_AppCompat 0x0
+int style Base_Theme_AppCompat_CompactMenu 0x0
+int style Base_Theme_AppCompat_Dialog 0x0
+int style Base_Theme_AppCompat_DialogWhenLarge 0x0
+int style Base_Theme_AppCompat_Dialog_Alert 0x0
+int style Base_Theme_AppCompat_Dialog_FixedSize 0x0
+int style Base_Theme_AppCompat_Dialog_MinWidth 0x0
+int style Base_Theme_AppCompat_Light 0x0
+int style Base_Theme_AppCompat_Light_DarkActionBar 0x0
+int style Base_Theme_AppCompat_Light_Dialog 0x0
+int style Base_Theme_AppCompat_Light_DialogWhenLarge 0x0
+int style Base_Theme_AppCompat_Light_Dialog_Alert 0x0
+int style Base_Theme_AppCompat_Light_Dialog_FixedSize 0x0
+int style Base_Theme_AppCompat_Light_Dialog_MinWidth 0x0
+int style Base_V21_ThemeOverlay_AppCompat_Dialog 0x0
+int style Base_V21_Theme_AppCompat 0x0
+int style Base_V21_Theme_AppCompat_Dialog 0x0
+int style Base_V21_Theme_AppCompat_Light 0x0
+int style Base_V21_Theme_AppCompat_Light_Dialog 0x0
+int style Base_V22_Theme_AppCompat 0x0
+int style Base_V22_Theme_AppCompat_Light 0x0
+int style Base_V23_Theme_AppCompat 0x0
+int style Base_V23_Theme_AppCompat_Light 0x0
+int style Base_V26_Theme_AppCompat 0x0
+int style Base_V26_Theme_AppCompat_Light 0x0
+int style Base_V26_Widget_AppCompat_Toolbar 0x0
+int style Base_V28_Theme_AppCompat 0x0
+int style Base_V28_Theme_AppCompat_Light 0x0
+int style Base_V7_ThemeOverlay_AppCompat_Dialog 0x0
+int style Base_V7_Theme_AppCompat 0x0
+int style Base_V7_Theme_AppCompat_Dialog 0x0
+int style Base_V7_Theme_AppCompat_Light 0x0
+int style Base_V7_Theme_AppCompat_Light_Dialog 0x0
+int style Base_V7_Widget_AppCompat_AutoCompleteTextView 0x0
+int style Base_V7_Widget_AppCompat_EditText 0x0
+int style Base_V7_Widget_AppCompat_Toolbar 0x0
+int style Base_Widget_AppCompat_ActionBar 0x0
+int style Base_Widget_AppCompat_ActionBar_Solid 0x0
+int style Base_Widget_AppCompat_ActionBar_TabBar 0x0
+int style Base_Widget_AppCompat_ActionBar_TabText 0x0
+int style Base_Widget_AppCompat_ActionBar_TabView 0x0
+int style Base_Widget_AppCompat_ActionButton 0x0
+int style Base_Widget_AppCompat_ActionButton_CloseMode 0x0
+int style Base_Widget_AppCompat_ActionButton_Overflow 0x0
+int style Base_Widget_AppCompat_ActionMode 0x0
+int style Base_Widget_AppCompat_ActivityChooserView 0x0
+int style Base_Widget_AppCompat_AutoCompleteTextView 0x0
+int style Base_Widget_AppCompat_Button 0x0
+int style Base_Widget_AppCompat_ButtonBar 0x0
+int style Base_Widget_AppCompat_ButtonBar_AlertDialog 0x0
+int style Base_Widget_AppCompat_Button_Borderless 0x0
+int style Base_Widget_AppCompat_Button_Borderless_Colored 0x0
+int style Base_Widget_AppCompat_Button_ButtonBar_AlertDialog 0x0
+int style Base_Widget_AppCompat_Button_Colored 0x0
+int style Base_Widget_AppCompat_Button_Small 0x0
+int style Base_Widget_AppCompat_CompoundButton_CheckBox 0x0
+int style Base_Widget_AppCompat_CompoundButton_RadioButton 0x0
+int style Base_Widget_AppCompat_CompoundButton_Switch 0x0
+int style Base_Widget_AppCompat_DrawerArrowToggle 0x0
+int style Base_Widget_AppCompat_DrawerArrowToggle_Common 0x0
+int style Base_Widget_AppCompat_DropDownItem_Spinner 0x0
+int style Base_Widget_AppCompat_EditText 0x0
+int style Base_Widget_AppCompat_ImageButton 0x0
+int style Base_Widget_AppCompat_Light_ActionBar 0x0
+int style Base_Widget_AppCompat_Light_ActionBar_Solid 0x0
+int style Base_Widget_AppCompat_Light_ActionBar_TabBar 0x0
+int style Base_Widget_AppCompat_Light_ActionBar_TabText 0x0
+int style Base_Widget_AppCompat_Light_ActionBar_TabText_Inverse 0x0
+int style Base_Widget_AppCompat_Light_ActionBar_TabView 0x0
+int style Base_Widget_AppCompat_Light_PopupMenu 0x0
+int style Base_Widget_AppCompat_Light_PopupMenu_Overflow 0x0
+int style Base_Widget_AppCompat_ListMenuView 0x0
+int style Base_Widget_AppCompat_ListPopupWindow 0x0
+int style Base_Widget_AppCompat_ListView 0x0
+int style Base_Widget_AppCompat_ListView_DropDown 0x0
+int style Base_Widget_AppCompat_ListView_Menu 0x0
+int style Base_Widget_AppCompat_PopupMenu 0x0
+int style Base_Widget_AppCompat_PopupMenu_Overflow 0x0
+int style Base_Widget_AppCompat_PopupWindow 0x0
+int style Base_Widget_AppCompat_ProgressBar 0x0
+int style Base_Widget_AppCompat_ProgressBar_Horizontal 0x0
+int style Base_Widget_AppCompat_RatingBar 0x0
+int style Base_Widget_AppCompat_RatingBar_Indicator 0x0
+int style Base_Widget_AppCompat_RatingBar_Small 0x0
+int style Base_Widget_AppCompat_SearchView 0x0
+int style Base_Widget_AppCompat_SearchView_ActionBar 0x0
+int style Base_Widget_AppCompat_SeekBar 0x0
+int style Base_Widget_AppCompat_SeekBar_Discrete 0x0
+int style Base_Widget_AppCompat_Spinner 0x0
+int style Base_Widget_AppCompat_Spinner_Underlined 0x0
+int style Base_Widget_AppCompat_TextView_SpinnerItem 0x0
+int style Base_Widget_AppCompat_Toolbar 0x0
+int style Base_Widget_AppCompat_Toolbar_Button_Navigation 0x0
+int style Platform_AppCompat 0x0
+int style Platform_AppCompat_Light 0x0
+int style Platform_ThemeOverlay_AppCompat 0x0
+int style Platform_ThemeOverlay_AppCompat_Dark 0x0
+int style Platform_ThemeOverlay_AppCompat_Light 0x0
+int style Platform_V21_AppCompat 0x0
+int style Platform_V21_AppCompat_Light 0x0
+int style Platform_V25_AppCompat 0x0
+int style Platform_V25_AppCompat_Light 0x0
+int style Platform_Widget_AppCompat_Spinner 0x0
+int style RtlOverlay_DialogWindowTitle_AppCompat 0x0
+int style RtlOverlay_Widget_AppCompat_ActionBar_TitleItem 0x0
+int style RtlOverlay_Widget_AppCompat_DialogTitle_Icon 0x0
+int style RtlOverlay_Widget_AppCompat_PopupMenuItem 0x0
+int style RtlOverlay_Widget_AppCompat_PopupMenuItem_InternalGroup 0x0
+int style RtlOverlay_Widget_AppCompat_PopupMenuItem_Shortcut 0x0
+int style RtlOverlay_Widget_AppCompat_PopupMenuItem_SubmenuArrow 0x0
+int style RtlOverlay_Widget_AppCompat_PopupMenuItem_Text 0x0
+int style RtlOverlay_Widget_AppCompat_PopupMenuItem_Title 0x0
+int style RtlOverlay_Widget_AppCompat_SearchView_MagIcon 0x0
+int style RtlOverlay_Widget_AppCompat_Search_DropDown 0x0
+int style RtlOverlay_Widget_AppCompat_Search_DropDown_Icon1 0x0
+int style RtlOverlay_Widget_AppCompat_Search_DropDown_Icon2 0x0
+int style RtlOverlay_Widget_AppCompat_Search_DropDown_Query 0x0
+int style RtlOverlay_Widget_AppCompat_Search_DropDown_Text 0x0
+int style RtlUnderlay_Widget_AppCompat_ActionButton 0x0
+int style RtlUnderlay_Widget_AppCompat_ActionButton_Overflow 0x0
+int style TextAppearance_AppCompat 0x0
+int style TextAppearance_AppCompat_Body1 0x0
+int style TextAppearance_AppCompat_Body2 0x0
+int style TextAppearance_AppCompat_Button 0x0
+int style TextAppearance_AppCompat_Caption 0x0
+int style TextAppearance_AppCompat_Display1 0x0
+int style TextAppearance_AppCompat_Display2 0x0
+int style TextAppearance_AppCompat_Display3 0x0
+int style TextAppearance_AppCompat_Display4 0x0
+int style TextAppearance_AppCompat_Headline 0x0
+int style TextAppearance_AppCompat_Inverse 0x0
+int style TextAppearance_AppCompat_Large 0x0
+int style TextAppearance_AppCompat_Large_Inverse 0x0
+int style TextAppearance_AppCompat_Light_SearchResult_Subtitle 0x0
+int style TextAppearance_AppCompat_Light_SearchResult_Title 0x0
+int style TextAppearance_AppCompat_Light_Widget_PopupMenu_Large 0x0
+int style TextAppearance_AppCompat_Light_Widget_PopupMenu_Small 0x0
+int style TextAppearance_AppCompat_Medium 0x0
+int style TextAppearance_AppCompat_Medium_Inverse 0x0
+int style TextAppearance_AppCompat_Menu 0x0
+int style TextAppearance_AppCompat_SearchResult_Subtitle 0x0
+int style TextAppearance_AppCompat_SearchResult_Title 0x0
+int style TextAppearance_AppCompat_Small 0x0
+int style TextAppearance_AppCompat_Small_Inverse 0x0
+int style TextAppearance_AppCompat_Subhead 0x0
+int style TextAppearance_AppCompat_Subhead_Inverse 0x0
+int style TextAppearance_AppCompat_Title 0x0
+int style TextAppearance_AppCompat_Title_Inverse 0x0
+int style TextAppearance_AppCompat_Tooltip 0x0
+int style TextAppearance_AppCompat_Widget_ActionBar_Menu 0x0
+int style TextAppearance_AppCompat_Widget_ActionBar_Subtitle 0x0
+int style TextAppearance_AppCompat_Widget_ActionBar_Subtitle_Inverse 0x0
+int style TextAppearance_AppCompat_Widget_ActionBar_Title 0x0
+int style TextAppearance_AppCompat_Widget_ActionBar_Title_Inverse 0x0
+int style TextAppearance_AppCompat_Widget_ActionMode_Subtitle 0x0
+int style TextAppearance_AppCompat_Widget_ActionMode_Subtitle_Inverse 0x0
+int style TextAppearance_AppCompat_Widget_ActionMode_Title 0x0
+int style TextAppearance_AppCompat_Widget_ActionMode_Title_Inverse 0x0
+int style TextAppearance_AppCompat_Widget_Button 0x0
+int style TextAppearance_AppCompat_Widget_Button_Borderless_Colored 0x0
+int style TextAppearance_AppCompat_Widget_Button_Colored 0x0
+int style TextAppearance_AppCompat_Widget_Button_Inverse 0x0
+int style TextAppearance_AppCompat_Widget_DropDownItem 0x0
+int style TextAppearance_AppCompat_Widget_PopupMenu_Header 0x0
+int style TextAppearance_AppCompat_Widget_PopupMenu_Large 0x0
+int style TextAppearance_AppCompat_Widget_PopupMenu_Small 0x0
+int style TextAppearance_AppCompat_Widget_Switch 0x0
+int style TextAppearance_AppCompat_Widget_TextView_SpinnerItem 0x0
+int style TextAppearance_Compat_Notification 0x0
+int style TextAppearance_Compat_Notification_Info 0x0
+int style TextAppearance_Compat_Notification_Line2 0x0
+int style TextAppearance_Compat_Notification_Time 0x0
+int style TextAppearance_Compat_Notification_Title 0x0
+int style TextAppearance_Widget_AppCompat_ExpandedMenu_Item 0x0
+int style TextAppearance_Widget_AppCompat_Toolbar_Subtitle 0x0
+int style TextAppearance_Widget_AppCompat_Toolbar_Title 0x0
+int style ThemeOverlay_AppCompat 0x0
+int style ThemeOverlay_AppCompat_ActionBar 0x0
+int style ThemeOverlay_AppCompat_Dark 0x0
+int style ThemeOverlay_AppCompat_Dark_ActionBar 0x0
+int style ThemeOverlay_AppCompat_Dialog 0x0
+int style ThemeOverlay_AppCompat_Dialog_Alert 0x0
+int style ThemeOverlay_AppCompat_Light 0x0
+int style Theme_AppCompat 0x0
+int style Theme_AppCompat_CompactMenu 0x0
+int style Theme_AppCompat_DayNight 0x0
+int style Theme_AppCompat_DayNight_DarkActionBar 0x0
+int style Theme_AppCompat_DayNight_Dialog 0x0
+int style Theme_AppCompat_DayNight_DialogWhenLarge 0x0
+int style Theme_AppCompat_DayNight_Dialog_Alert 0x0
+int style Theme_AppCompat_DayNight_Dialog_MinWidth 0x0
+int style Theme_AppCompat_DayNight_NoActionBar 0x0
+int style Theme_AppCompat_Dialog 0x0
+int style Theme_AppCompat_DialogWhenLarge 0x0
+int style Theme_AppCompat_Dialog_Alert 0x0
+int style Theme_AppCompat_Dialog_MinWidth 0x0
+int style Theme_AppCompat_Light 0x0
+int style Theme_AppCompat_Light_DarkActionBar 0x0
+int style Theme_AppCompat_Light_Dialog 0x0
+int style Theme_AppCompat_Light_DialogWhenLarge 0x0
+int style Theme_AppCompat_Light_Dialog_Alert 0x0
+int style Theme_AppCompat_Light_Dialog_MinWidth 0x0
+int style Theme_AppCompat_Light_NoActionBar 0x0
+int style Theme_AppCompat_NoActionBar 0x0
+int style Widget_AppCompat_ActionBar 0x0
+int style Widget_AppCompat_ActionBar_Solid 0x0
+int style Widget_AppCompat_ActionBar_TabBar 0x0
+int style Widget_AppCompat_ActionBar_TabText 0x0
+int style Widget_AppCompat_ActionBar_TabView 0x0
+int style Widget_AppCompat_ActionButton 0x0
+int style Widget_AppCompat_ActionButton_CloseMode 0x0
+int style Widget_AppCompat_ActionButton_Overflow 0x0
+int style Widget_AppCompat_ActionMode 0x0
+int style Widget_AppCompat_ActivityChooserView 0x0
+int style Widget_AppCompat_AutoCompleteTextView 0x0
+int style Widget_AppCompat_Button 0x0
+int style Widget_AppCompat_ButtonBar 0x0
+int style Widget_AppCompat_ButtonBar_AlertDialog 0x0
+int style Widget_AppCompat_Button_Borderless 0x0
+int style Widget_AppCompat_Button_Borderless_Colored 0x0
+int style Widget_AppCompat_Button_ButtonBar_AlertDialog 0x0
+int style Widget_AppCompat_Button_Colored 0x0
+int style Widget_AppCompat_Button_Small 0x0
+int style Widget_AppCompat_CompoundButton_CheckBox 0x0
+int style Widget_AppCompat_CompoundButton_RadioButton 0x0
+int style Widget_AppCompat_CompoundButton_Switch 0x0
+int style Widget_AppCompat_DrawerArrowToggle 0x0
+int style Widget_AppCompat_DropDownItem_Spinner 0x0
+int style Widget_AppCompat_EditText 0x0
+int style Widget_AppCompat_ImageButton 0x0
+int style Widget_AppCompat_Light_ActionBar 0x0
+int style Widget_AppCompat_Light_ActionBar_Solid 0x0
+int style Widget_AppCompat_Light_ActionBar_Solid_Inverse 0x0
+int style Widget_AppCompat_Light_ActionBar_TabBar 0x0
+int style Widget_AppCompat_Light_ActionBar_TabBar_Inverse 0x0
+int style Widget_AppCompat_Light_ActionBar_TabText 0x0
+int style Widget_AppCompat_Light_ActionBar_TabText_Inverse 0x0
+int style Widget_AppCompat_Light_ActionBar_TabView 0x0
+int style Widget_AppCompat_Light_ActionBar_TabView_Inverse 0x0
+int style Widget_AppCompat_Light_ActionButton 0x0
+int style Widget_AppCompat_Light_ActionButton_CloseMode 0x0
+int style Widget_AppCompat_Light_ActionButton_Overflow 0x0
+int style Widget_AppCompat_Light_ActionMode_Inverse 0x0
+int style Widget_AppCompat_Light_ActivityChooserView 0x0
+int style Widget_AppCompat_Light_AutoCompleteTextView 0x0
+int style Widget_AppCompat_Light_DropDownItem_Spinner 0x0
+int style Widget_AppCompat_Light_ListPopupWindow 0x0
+int style Widget_AppCompat_Light_ListView_DropDown 0x0
+int style Widget_AppCompat_Light_PopupMenu 0x0
+int style Widget_AppCompat_Light_PopupMenu_Overflow 0x0
+int style Widget_AppCompat_Light_SearchView 0x0
+int style Widget_AppCompat_Light_Spinner_DropDown_ActionBar 0x0
+int style Widget_AppCompat_ListMenuView 0x0
+int style Widget_AppCompat_ListPopupWindow 0x0
+int style Widget_AppCompat_ListView 0x0
+int style Widget_AppCompat_ListView_DropDown 0x0
+int style Widget_AppCompat_ListView_Menu 0x0
+int style Widget_AppCompat_PopupMenu 0x0
+int style Widget_AppCompat_PopupMenu_Overflow 0x0
+int style Widget_AppCompat_PopupWindow 0x0
+int style Widget_AppCompat_ProgressBar 0x0
+int style Widget_AppCompat_ProgressBar_Horizontal 0x0
+int style Widget_AppCompat_RatingBar 0x0
+int style Widget_AppCompat_RatingBar_Indicator 0x0
+int style Widget_AppCompat_RatingBar_Small 0x0
+int style Widget_AppCompat_SearchView 0x0
+int style Widget_AppCompat_SearchView_ActionBar 0x0
+int style Widget_AppCompat_SeekBar 0x0
+int style Widget_AppCompat_SeekBar_Discrete 0x0
+int style Widget_AppCompat_Spinner 0x0
+int style Widget_AppCompat_Spinner_DropDown 0x0
+int style Widget_AppCompat_Spinner_DropDown_ActionBar 0x0
+int style Widget_AppCompat_Spinner_Underlined 0x0
+int style Widget_AppCompat_TextView_SpinnerItem 0x0
+int style Widget_AppCompat_Toolbar 0x0
+int style Widget_AppCompat_Toolbar_Button_Navigation 0x0
+int style Widget_Compat_NotificationActionContainer 0x0
+int style Widget_Compat_NotificationActionText 0x0
+int style Widget_Support_CoordinatorLayout 0x0
+int[] styleable ActionBar { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }
+int styleable ActionBar_background 0
+int styleable ActionBar_backgroundSplit 1
+int styleable ActionBar_backgroundStacked 2
+int styleable ActionBar_contentInsetEnd 3
+int styleable ActionBar_contentInsetEndWithActions 4
+int styleable ActionBar_contentInsetLeft 5
+int styleable ActionBar_contentInsetRight 6
+int styleable ActionBar_contentInsetStart 7
+int styleable ActionBar_contentInsetStartWithNavigation 8
+int styleable ActionBar_customNavigationLayout 9
+int styleable ActionBar_displayOptions 10
+int styleable ActionBar_divider 11
+int styleable ActionBar_elevation 12
+int styleable ActionBar_height 13
+int styleable ActionBar_hideOnContentScroll 14
+int styleable ActionBar_homeAsUpIndicator 15
+int styleable ActionBar_homeLayout 16
+int styleable ActionBar_icon 17
+int styleable ActionBar_indeterminateProgressStyle 18
+int styleable ActionBar_itemPadding 19
+int styleable ActionBar_logo 20
+int styleable ActionBar_navigationMode 21
+int styleable ActionBar_popupTheme 22
+int styleable ActionBar_progressBarPadding 23
+int styleable ActionBar_progressBarStyle 24
+int styleable ActionBar_subtitle 25
+int styleable ActionBar_subtitleTextStyle 26
+int styleable ActionBar_title 27
+int styleable ActionBar_titleTextStyle 28
+int[] styleable ActionBarLayout { 0x10100b3 }
+int styleable ActionBarLayout_android_layout_gravity 0
+int[] styleable ActionMenuItemView { 0x101013f }
+int styleable ActionMenuItemView_android_minWidth 0
+int[] styleable ActionMenuView { }
+int[] styleable ActionMode { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }
+int styleable ActionMode_background 0
+int styleable ActionMode_backgroundSplit 1
+int styleable ActionMode_closeItemLayout 2
+int styleable ActionMode_height 3
+int styleable ActionMode_subtitleTextStyle 4
+int styleable ActionMode_titleTextStyle 5
+int[] styleable ActivityChooserView { 0x0, 0x0 }
+int styleable ActivityChooserView_expandActivityOverflowButtonDrawable 0
+int styleable ActivityChooserView_initialActivityCount 1
+int[] styleable AlertDialog { 0x10100f2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }
+int styleable AlertDialog_android_layout 0
+int styleable AlertDialog_buttonIconDimen 1
+int styleable AlertDialog_buttonPanelSideLayout 2
+int styleable AlertDialog_listItemLayout 3
+int styleable AlertDialog_listLayout 4
+int styleable AlertDialog_multiChoiceItemLayout 5
+int styleable AlertDialog_showTitle 6
+int styleable AlertDialog_singleChoiceItemLayout 7
+int[] styleable AnimatedStateListDrawableCompat { 0x1010196, 0x101011c, 0x101030c, 0x101030d, 0x1010195, 0x1010194 }
+int styleable AnimatedStateListDrawableCompat_android_constantSize 0
+int styleable AnimatedStateListDrawableCompat_android_dither 1
+int styleable AnimatedStateListDrawableCompat_android_enterFadeDuration 2
+int styleable AnimatedStateListDrawableCompat_android_exitFadeDuration 3
+int styleable AnimatedStateListDrawableCompat_android_variablePadding 4
+int styleable AnimatedStateListDrawableCompat_android_visible 5
+int[] styleable AnimatedStateListDrawableItem { 0x1010199, 0x10100d0 }
+int styleable AnimatedStateListDrawableItem_android_drawable 0
+int styleable AnimatedStateListDrawableItem_android_id 1
+int[] styleable AnimatedStateListDrawableTransition { 0x1010199, 0x101044a, 0x101044b, 0x1010449 }
+int styleable AnimatedStateListDrawableTransition_android_drawable 0
+int styleable AnimatedStateListDrawableTransition_android_fromId 1
+int styleable AnimatedStateListDrawableTransition_android_reversible 2
+int styleable AnimatedStateListDrawableTransition_android_toId 3
+int[] styleable AppCompatImageView { 0x1010119, 0x0, 0x0, 0x0 }
+int styleable AppCompatImageView_android_src 0
+int styleable AppCompatImageView_srcCompat 1
+int styleable AppCompatImageView_tint 2
+int styleable AppCompatImageView_tintMode 3
+int[] styleable AppCompatSeekBar { 0x1010142, 0x0, 0x0, 0x0 }
+int styleable AppCompatSeekBar_android_thumb 0
+int styleable AppCompatSeekBar_tickMark 1
+int styleable AppCompatSeekBar_tickMarkTint 2
+int styleable AppCompatSeekBar_tickMarkTintMode 3
+int[] styleable AppCompatTextHelper { 0x101016e, 0x1010393, 0x101016f, 0x1010170, 0x1010392, 0x101016d, 0x1010034 }
+int styleable AppCompatTextHelper_android_drawableBottom 0
+int styleable AppCompatTextHelper_android_drawableEnd 1
+int styleable AppCompatTextHelper_android_drawableLeft 2
+int styleable AppCompatTextHelper_android_drawableRight 3
+int styleable AppCompatTextHelper_android_drawableStart 4
+int styleable AppCompatTextHelper_android_drawableTop 5
+int styleable AppCompatTextHelper_android_textAppearance 6
+int[] styleable AppCompatTextView { 0x1010034, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }
+int styleable AppCompatTextView_android_textAppearance 0
+int styleable AppCompatTextView_autoSizeMaxTextSize 1
+int styleable AppCompatTextView_autoSizeMinTextSize 2
+int styleable AppCompatTextView_autoSizePresetSizes 3
+int styleable AppCompatTextView_autoSizeStepGranularity 4
+int styleable AppCompatTextView_autoSizeTextType 5
+int styleable AppCompatTextView_firstBaselineToTopHeight 6
+int styleable AppCompatTextView_fontFamily 7
+int styleable AppCompatTextView_lastBaselineToBottomHeight 8
+int styleable AppCompatTextView_lineHeight 9
+int styleable AppCompatTextView_textAllCaps 10
+int[] styleable AppCompatTheme { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10100ae, 0x1010057, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }
+int styleable AppCompatTheme_actionBarDivider 0
+int styleable AppCompatTheme_actionBarItemBackground 1
+int styleable AppCompatTheme_actionBarPopupTheme 2
+int styleable AppCompatTheme_actionBarSize 3
+int styleable AppCompatTheme_actionBarSplitStyle 4
+int styleable AppCompatTheme_actionBarStyle 5
+int styleable AppCompatTheme_actionBarTabBarStyle 6
+int styleable AppCompatTheme_actionBarTabStyle 7
+int styleable AppCompatTheme_actionBarTabTextStyle 8
+int styleable AppCompatTheme_actionBarTheme 9
+int styleable AppCompatTheme_actionBarWidgetTheme 10
+int styleable AppCompatTheme_actionButtonStyle 11
+int styleable AppCompatTheme_actionDropDownStyle 12
+int styleable AppCompatTheme_actionMenuTextAppearance 13
+int styleable AppCompatTheme_actionMenuTextColor 14
+int styleable AppCompatTheme_actionModeBackground 15
+int styleable AppCompatTheme_actionModeCloseButtonStyle 16
+int styleable AppCompatTheme_actionModeCloseDrawable 17
+int styleable AppCompatTheme_actionModeCopyDrawable 18
+int styleable AppCompatTheme_actionModeCutDrawable 19
+int styleable AppCompatTheme_actionModeFindDrawable 20
+int styleable AppCompatTheme_actionModePasteDrawable 21
+int styleable AppCompatTheme_actionModePopupWindowStyle 22
+int styleable AppCompatTheme_actionModeSelectAllDrawable 23
+int styleable AppCompatTheme_actionModeShareDrawable 24
+int styleable AppCompatTheme_actionModeSplitBackground 25
+int styleable AppCompatTheme_actionModeStyle 26
+int styleable AppCompatTheme_actionModeWebSearchDrawable 27
+int styleable AppCompatTheme_actionOverflowButtonStyle 28
+int styleable AppCompatTheme_actionOverflowMenuStyle 29
+int styleable AppCompatTheme_activityChooserViewStyle 30
+int styleable AppCompatTheme_alertDialogButtonGroupStyle 31
+int styleable AppCompatTheme_alertDialogCenterButtons 32
+int styleable AppCompatTheme_alertDialogStyle 33
+int styleable AppCompatTheme_alertDialogTheme 34
+int styleable AppCompatTheme_android_windowAnimationStyle 35
+int styleable AppCompatTheme_android_windowIsFloating 36
+int styleable AppCompatTheme_autoCompleteTextViewStyle 37
+int styleable AppCompatTheme_borderlessButtonStyle 38
+int styleable AppCompatTheme_buttonBarButtonStyle 39
+int styleable AppCompatTheme_buttonBarNegativeButtonStyle 40
+int styleable AppCompatTheme_buttonBarNeutralButtonStyle 41
+int styleable AppCompatTheme_buttonBarPositiveButtonStyle 42
+int styleable AppCompatTheme_buttonBarStyle 43
+int styleable AppCompatTheme_buttonStyle 44
+int styleable AppCompatTheme_buttonStyleSmall 45
+int styleable AppCompatTheme_checkboxStyle 46
+int styleable AppCompatTheme_checkedTextViewStyle 47
+int styleable AppCompatTheme_colorAccent 48
+int styleable AppCompatTheme_colorBackgroundFloating 49
+int styleable AppCompatTheme_colorButtonNormal 50
+int styleable AppCompatTheme_colorControlActivated 51
+int styleable AppCompatTheme_colorControlHighlight 52
+int styleable AppCompatTheme_colorControlNormal 53
+int styleable AppCompatTheme_colorError 54
+int styleable AppCompatTheme_colorPrimary 55
+int styleable AppCompatTheme_colorPrimaryDark 56
+int styleable AppCompatTheme_colorSwitchThumbNormal 57
+int styleable AppCompatTheme_controlBackground 58
+int styleable AppCompatTheme_dialogCornerRadius 59
+int styleable AppCompatTheme_dialogPreferredPadding 60
+int styleable AppCompatTheme_dialogTheme 61
+int styleable AppCompatTheme_dividerHorizontal 62
+int styleable AppCompatTheme_dividerVertical 63
+int styleable AppCompatTheme_dropDownListViewStyle 64
+int styleable AppCompatTheme_dropdownListPreferredItemHeight 65
+int styleable AppCompatTheme_editTextBackground 66
+int styleable AppCompatTheme_editTextColor 67
+int styleable AppCompatTheme_editTextStyle 68
+int styleable AppCompatTheme_homeAsUpIndicator 69
+int styleable AppCompatTheme_imageButtonStyle 70
+int styleable AppCompatTheme_listChoiceBackgroundIndicator 71
+int styleable AppCompatTheme_listDividerAlertDialog 72
+int styleable AppCompatTheme_listMenuViewStyle 73
+int styleable AppCompatTheme_listPopupWindowStyle 74
+int styleable AppCompatTheme_listPreferredItemHeight 75
+int styleable AppCompatTheme_listPreferredItemHeightLarge 76
+int styleable AppCompatTheme_listPreferredItemHeightSmall 77
+int styleable AppCompatTheme_listPreferredItemPaddingLeft 78
+int styleable AppCompatTheme_listPreferredItemPaddingRight 79
+int styleable AppCompatTheme_panelBackground 80
+int styleable AppCompatTheme_panelMenuListTheme 81
+int styleable AppCompatTheme_panelMenuListWidth 82
+int styleable AppCompatTheme_popupMenuStyle 83
+int styleable AppCompatTheme_popupWindowStyle 84
+int styleable AppCompatTheme_radioButtonStyle 85
+int styleable AppCompatTheme_ratingBarStyle 86
+int styleable AppCompatTheme_ratingBarStyleIndicator 87
+int styleable AppCompatTheme_ratingBarStyleSmall 88
+int styleable AppCompatTheme_searchViewStyle 89
+int styleable AppCompatTheme_seekBarStyle 90
+int styleable AppCompatTheme_selectableItemBackground 91
+int styleable AppCompatTheme_selectableItemBackgroundBorderless 92
+int styleable AppCompatTheme_spinnerDropDownItemStyle 93
+int styleable AppCompatTheme_spinnerStyle 94
+int styleable AppCompatTheme_switchStyle 95
+int styleable AppCompatTheme_textAppearanceLargePopupMenu 96
+int styleable AppCompatTheme_textAppearanceListItem 97
+int styleable AppCompatTheme_textAppearanceListItemSecondary 98
+int styleable AppCompatTheme_textAppearanceListItemSmall 99
+int styleable AppCompatTheme_textAppearancePopupMenuHeader 100
+int styleable AppCompatTheme_textAppearanceSearchResultSubtitle 101
+int styleable AppCompatTheme_textAppearanceSearchResultTitle 102
+int styleable AppCompatTheme_textAppearanceSmallPopupMenu 103
+int styleable AppCompatTheme_textColorAlertDialogListItem 104
+int styleable AppCompatTheme_textColorSearchUrl 105
+int styleable AppCompatTheme_toolbarNavigationButtonStyle 106
+int styleable AppCompatTheme_toolbarStyle 107
+int styleable AppCompatTheme_tooltipForegroundColor 108
+int styleable AppCompatTheme_tooltipFrameBackground 109
+int styleable AppCompatTheme_viewInflaterClass 110
+int styleable AppCompatTheme_windowActionBar 111
+int styleable AppCompatTheme_windowActionBarOverlay 112
+int styleable AppCompatTheme_windowActionModeOverlay 113
+int styleable AppCompatTheme_windowFixedHeightMajor 114
+int styleable AppCompatTheme_windowFixedHeightMinor 115
+int styleable AppCompatTheme_windowFixedWidthMajor 116
+int styleable AppCompatTheme_windowFixedWidthMinor 117
+int styleable AppCompatTheme_windowMinWidthMajor 118
+int styleable AppCompatTheme_windowMinWidthMinor 119
+int styleable AppCompatTheme_windowNoTitle 120
+int[] styleable ButtonBarLayout { 0x0 }
+int styleable ButtonBarLayout_allowStacking 0
+int[] styleable ColorStateListItem { 0x0, 0x101031f, 0x10101a5 }
+int styleable ColorStateListItem_alpha 0
+int styleable ColorStateListItem_android_alpha 1
+int styleable ColorStateListItem_android_color 2
+int[] styleable CompoundButton { 0x1010107, 0x0, 0x0 }
+int styleable CompoundButton_android_button 0
+int styleable CompoundButton_buttonTint 1
+int styleable CompoundButton_buttonTintMode 2
+int[] styleable CoordinatorLayout { 0x0, 0x0 }
+int styleable CoordinatorLayout_keylines 0
+int styleable CoordinatorLayout_statusBarBackground 1
+int[] styleable CoordinatorLayout_Layout { 0x10100b3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }
+int styleable CoordinatorLayout_Layout_android_layout_gravity 0
+int styleable CoordinatorLayout_Layout_layout_anchor 1
+int styleable CoordinatorLayout_Layout_layout_anchorGravity 2
+int styleable CoordinatorLayout_Layout_layout_behavior 3
+int styleable CoordinatorLayout_Layout_layout_dodgeInsetEdges 4
+int styleable CoordinatorLayout_Layout_layout_insetEdge 5
+int styleable CoordinatorLayout_Layout_layout_keyline 6
+int[] styleable DrawerArrowToggle { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }
+int styleable DrawerArrowToggle_arrowHeadLength 0
+int styleable DrawerArrowToggle_arrowShaftLength 1
+int styleable DrawerArrowToggle_barLength 2
+int styleable DrawerArrowToggle_color 3
+int styleable DrawerArrowToggle_drawableSize 4
+int styleable DrawerArrowToggle_gapBetweenBars 5
+int styleable DrawerArrowToggle_spinBars 6
+int styleable DrawerArrowToggle_thickness 7
+int[] styleable FontFamily { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }
+int styleable FontFamily_fontProviderAuthority 0
+int styleable FontFamily_fontProviderCerts 1
+int styleable FontFamily_fontProviderFetchStrategy 2
+int styleable FontFamily_fontProviderFetchTimeout 3
+int styleable FontFamily_fontProviderPackage 4
+int styleable FontFamily_fontProviderQuery 5
+int[] styleable FontFamilyFont { 0x1010532, 0x101053f, 0x1010570, 0x1010533, 0x101056f, 0x0, 0x0, 0x0, 0x0, 0x0 }
+int styleable FontFamilyFont_android_font 0
+int styleable FontFamilyFont_android_fontStyle 1
+int styleable FontFamilyFont_android_fontVariationSettings 2
+int styleable FontFamilyFont_android_fontWeight 3
+int styleable FontFamilyFont_android_ttcIndex 4
+int styleable FontFamilyFont_font 5
+int styleable FontFamilyFont_fontStyle 6
+int styleable FontFamilyFont_fontVariationSettings 7
+int styleable FontFamilyFont_fontWeight 8
+int styleable FontFamilyFont_ttcIndex 9
+int[] styleable GradientColor { 0x101020b, 0x10101a2, 0x10101a3, 0x101019e, 0x1010512, 0x1010513, 0x10101a4, 0x101019d, 0x1010510, 0x1010511, 0x1010201, 0x10101a1 }
+int styleable GradientColor_android_centerColor 0
+int styleable GradientColor_android_centerX 1
+int styleable GradientColor_android_centerY 2
+int styleable GradientColor_android_endColor 3
+int styleable GradientColor_android_endX 4
+int styleable GradientColor_android_endY 5
+int styleable GradientColor_android_gradientRadius 6
+int styleable GradientColor_android_startColor 7
+int styleable GradientColor_android_startX 8
+int styleable GradientColor_android_startY 9
+int styleable GradientColor_android_tileMode 10
+int styleable GradientColor_android_type 11
+int[] styleable GradientColorItem { 0x10101a5, 0x1010514 }
+int styleable GradientColorItem_android_color 0
+int styleable GradientColorItem_android_offset 1
+int[] styleable LinearLayoutCompat { 0x1010126, 0x1010127, 0x10100af, 0x10100c4, 0x1010128, 0x0, 0x0, 0x0, 0x0 }
+int styleable LinearLayoutCompat_android_baselineAligned 0
+int styleable LinearLayoutCompat_android_baselineAlignedChildIndex 1
+int styleable LinearLayoutCompat_android_gravity 2
+int styleable LinearLayoutCompat_android_orientation 3
+int styleable LinearLayoutCompat_android_weightSum 4
+int styleable LinearLayoutCompat_divider 5
+int styleable LinearLayoutCompat_dividerPadding 6
+int styleable LinearLayoutCompat_measureWithLargestChild 7
+int styleable LinearLayoutCompat_showDividers 8
+int[] styleable LinearLayoutCompat_Layout { 0x10100b3, 0x10100f5, 0x1010181, 0x10100f4 }
+int styleable LinearLayoutCompat_Layout_android_layout_gravity 0
+int styleable LinearLayoutCompat_Layout_android_layout_height 1
+int styleable LinearLayoutCompat_Layout_android_layout_weight 2
+int styleable LinearLayoutCompat_Layout_android_layout_width 3
+int[] styleable ListPopupWindow { 0x10102ac, 0x10102ad }
+int styleable ListPopupWindow_android_dropDownHorizontalOffset 0
+int styleable ListPopupWindow_android_dropDownVerticalOffset 1
+int[] styleable MenuGroup { 0x10101e0, 0x101000e, 0x10100d0, 0x10101de, 0x10101df, 0x1010194 }
+int styleable MenuGroup_android_checkableBehavior 0
+int styleable MenuGroup_android_enabled 1
+int styleable MenuGroup_android_id 2
+int styleable MenuGroup_android_menuCategory 3
+int styleable MenuGroup_android_orderInCategory 4
+int styleable MenuGroup_android_visible 5
+int[] styleable MenuItem { 0x0, 0x0, 0x0, 0x0, 0x10101e3, 0x10101e5, 0x1010106, 0x101000e, 0x1010002, 0x10100d0, 0x10101de, 0x10101e4, 0x101026f, 0x10101df, 0x10101e1, 0x10101e2, 0x1010194, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }
+int styleable MenuItem_actionLayout 0
+int styleable MenuItem_actionProviderClass 1
+int styleable MenuItem_actionViewClass 2
+int styleable MenuItem_alphabeticModifiers 3
+int styleable MenuItem_android_alphabeticShortcut 4
+int styleable MenuItem_android_checkable 5
+int styleable MenuItem_android_checked 6
+int styleable MenuItem_android_enabled 7
+int styleable MenuItem_android_icon 8
+int styleable MenuItem_android_id 9
+int styleable MenuItem_android_menuCategory 10
+int styleable MenuItem_android_numericShortcut 11
+int styleable MenuItem_android_onClick 12
+int styleable MenuItem_android_orderInCategory 13
+int styleable MenuItem_android_title 14
+int styleable MenuItem_android_titleCondensed 15
+int styleable MenuItem_android_visible 16
+int styleable MenuItem_contentDescription 17
+int styleable MenuItem_iconTint 18
+int styleable MenuItem_iconTintMode 19
+int styleable MenuItem_numericModifiers 20
+int styleable MenuItem_showAsAction 21
+int styleable MenuItem_tooltipText 22
+int[] styleable MenuView { 0x101012f, 0x101012d, 0x1010130, 0x1010131, 0x101012c, 0x101012e, 0x10100ae, 0x0, 0x0 }
+int styleable MenuView_android_headerBackground 0
+int styleable MenuView_android_horizontalDivider 1
+int styleable MenuView_android_itemBackground 2
+int styleable MenuView_android_itemIconDisabledAlpha 3
+int styleable MenuView_android_itemTextAppearance 4
+int styleable MenuView_android_verticalDivider 5
+int styleable MenuView_android_windowAnimationStyle 6
+int styleable MenuView_preserveIconSpacing 7
+int styleable MenuView_subMenuArrow 8
+int[] styleable PopupWindow { 0x10102c9, 0x1010176, 0x0 }
+int styleable PopupWindow_android_popupAnimationStyle 0
+int styleable PopupWindow_android_popupBackground 1
+int styleable PopupWindow_overlapAnchor 2
+int[] styleable PopupWindowBackgroundState { 0x0 }
+int styleable PopupWindowBackgroundState_state_above_anchor 0
+int[] styleable RecycleListView { 0x0, 0x0 }
+int styleable RecycleListView_paddingBottomNoButtons 0
+int styleable RecycleListView_paddingTopNoTitle 1
+int[] styleable SearchView { 0x10100da, 0x1010264, 0x1010220, 0x101011f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }
+int styleable SearchView_android_focusable 0
+int styleable SearchView_android_imeOptions 1
+int styleable SearchView_android_inputType 2
+int styleable SearchView_android_maxWidth 3
+int styleable SearchView_closeIcon 4
+int styleable SearchView_commitIcon 5
+int styleable SearchView_defaultQueryHint 6
+int styleable SearchView_goIcon 7
+int styleable SearchView_iconifiedByDefault 8
+int styleable SearchView_layout 9
+int styleable SearchView_queryBackground 10
+int styleable SearchView_queryHint 11
+int styleable SearchView_searchHintIcon 12
+int styleable SearchView_searchIcon 13
+int styleable SearchView_submitBackground 14
+int styleable SearchView_suggestionRowLayout 15
+int styleable SearchView_voiceIcon 16
+int[] styleable Spinner { 0x1010262, 0x10100b2, 0x1010176, 0x101017b, 0x0 }
+int styleable Spinner_android_dropDownWidth 0
+int styleable Spinner_android_entries 1
+int styleable Spinner_android_popupBackground 2
+int styleable Spinner_android_prompt 3
+int styleable Spinner_popupTheme 4
+int[] styleable StateListDrawable { 0x1010196, 0x101011c, 0x101030c, 0x101030d, 0x1010195, 0x1010194 }
+int styleable StateListDrawable_android_constantSize 0
+int styleable StateListDrawable_android_dither 1
+int styleable StateListDrawable_android_enterFadeDuration 2
+int styleable StateListDrawable_android_exitFadeDuration 3
+int styleable StateListDrawable_android_variablePadding 4
+int styleable StateListDrawable_android_visible 5
+int[] styleable StateListDrawableItem { 0x1010199 }
+int styleable StateListDrawableItem_android_drawable 0
+int[] styleable SwitchCompat { 0x1010125, 0x1010124, 0x1010142, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }
+int styleable SwitchCompat_android_textOff 0
+int styleable SwitchCompat_android_textOn 1
+int styleable SwitchCompat_android_thumb 2
+int styleable SwitchCompat_showText 3
+int styleable SwitchCompat_splitTrack 4
+int styleable SwitchCompat_switchMinWidth 5
+int styleable SwitchCompat_switchPadding 6
+int styleable SwitchCompat_switchTextAppearance 7
+int styleable SwitchCompat_thumbTextPadding 8
+int styleable SwitchCompat_thumbTint 9
+int styleable SwitchCompat_thumbTintMode 10
+int styleable SwitchCompat_track 11
+int styleable SwitchCompat_trackTint 12
+int styleable SwitchCompat_trackTintMode 13
+int[] styleable TextAppearance { 0x10103ac, 0x1010161, 0x1010162, 0x1010163, 0x1010164, 0x1010098, 0x101009a, 0x101009b, 0x1010095, 0x1010097, 0x1010096, 0x0, 0x0 }
+int styleable TextAppearance_android_fontFamily 0
+int styleable TextAppearance_android_shadowColor 1
+int styleable TextAppearance_android_shadowDx 2
+int styleable TextAppearance_android_shadowDy 3
+int styleable TextAppearance_android_shadowRadius 4
+int styleable TextAppearance_android_textColor 5
+int styleable TextAppearance_android_textColorHint 6
+int styleable TextAppearance_android_textColorLink 7
+int styleable TextAppearance_android_textSize 8
+int styleable TextAppearance_android_textStyle 9
+int styleable TextAppearance_android_typeface 10
+int styleable TextAppearance_fontFamily 11
+int styleable TextAppearance_textAllCaps 12
+int[] styleable Toolbar { 0x10100af, 0x1010140, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }
+int styleable Toolbar_android_gravity 0
+int styleable Toolbar_android_minHeight 1
+int styleable Toolbar_buttonGravity 2
+int styleable Toolbar_collapseContentDescription 3
+int styleable Toolbar_collapseIcon 4
+int styleable Toolbar_contentInsetEnd 5
+int styleable Toolbar_contentInsetEndWithActions 6
+int styleable Toolbar_contentInsetLeft 7
+int styleable Toolbar_contentInsetRight 8
+int styleable Toolbar_contentInsetStart 9
+int styleable Toolbar_contentInsetStartWithNavigation 10
+int styleable Toolbar_logo 11
+int styleable Toolbar_logoDescription 12
+int styleable Toolbar_maxButtonHeight 13
+int styleable Toolbar_navigationContentDescription 14
+int styleable Toolbar_navigationIcon 15
+int styleable Toolbar_popupTheme 16
+int styleable Toolbar_subtitle 17
+int styleable Toolbar_subtitleTextAppearance 18
+int styleable Toolbar_subtitleTextColor 19
+int styleable Toolbar_title 20
+int styleable Toolbar_titleMargin 21
+int styleable Toolbar_titleMarginBottom 22
+int styleable Toolbar_titleMarginEnd 23
+int styleable Toolbar_titleMarginStart 24
+int styleable Toolbar_titleMarginTop 25
+int styleable Toolbar_titleMargins 26
+int styleable Toolbar_titleTextAppearance 27
+int styleable Toolbar_titleTextColor 28
+int[] styleable View { 0x10100da, 0x1010000, 0x0, 0x0, 0x0 }
+int styleable View_android_focusable 0
+int styleable View_android_theme 1
+int styleable View_paddingEnd 2
+int styleable View_paddingStart 3
+int styleable View_theme 4
+int[] styleable ViewBackgroundHelper { 0x10100d4, 0x0, 0x0 }
+int styleable ViewBackgroundHelper_android_background 0
+int styleable ViewBackgroundHelper_backgroundTint 1
+int styleable ViewBackgroundHelper_backgroundTintMode 2
+int[] styleable ViewStubCompat { 0x10100d0, 0x10100f3, 0x10100f2 }
+int styleable ViewStubCompat_android_id 0
+int styleable ViewStubCompat_android_inflatedId 1
+int styleable ViewStubCompat_android_layout 2
diff --git a/app/libs/_osr_out/asr-one-sentence.zip b/app/libs/_osr_out/asr-one-sentence.zip
new file mode 100644
index 0000000..fdafba2
Binary files /dev/null and b/app/libs/_osr_out/asr-one-sentence.zip differ
diff --git a/app/libs/_osr_out/classes.jar b/app/libs/_osr_out/classes.jar
new file mode 100644
index 0000000..2d18e19
Binary files /dev/null and b/app/libs/_osr_out/classes.jar differ
diff --git a/app/libs/_osr_out/proguard.txt b/app/libs/_osr_out/proguard.txt
new file mode 100644
index 0000000..23fe6e1
--- /dev/null
+++ b/app/libs/_osr_out/proguard.txt
@@ -0,0 +1,90 @@
+-optimizationpasses 5 # 指定代码的压缩级别
+-allowaccessmodification #优化时允许访问并修改有修饰符的类和类的成员
+-dontusemixedcaseclassnames # 是否使用大小写混合
+-dontskipnonpubliclibraryclasses # 是否混淆第三方jar
+-dontpreverify # 混淆时是否做预校验
+-verbose # 混淆时是否记录日志
+-ignorewarnings # 忽略警告,避免打包时某些警告出现
+-optimizations !code/simplification/arithmetic,!code/simplification/cast,!field/*,!class/merging/* # 混淆时所采用的算法
+
+-keepattributes *Annotation*
+-keepclasseswithmembernames class * { # 保持 native 方法不被混淆
+ native ;
+}
+
+-keepclassmembers public class * extends android.view.View {
+ void set*(***);
+ *** get*();
+}
+
+-keepclassmembers class * extends android.app.Activity {
+ public void *(android.view.View);
+}
+
+-keepclassmembers enum * { # 保持枚举 enum 类不被混淆
+ public static **[] values();
+ public static ** valueOf(java.lang.String);
+}
+
+-keep class * implements android.os.Parcelable { # 保持 Parcelable 不被混淆
+ public static final android.os.Parcelable$Creator *;
+}
+
+-keepclassmembers class **.R$* { #不混淆R文件
+ public static ;
+}
+
+-dontwarn android.support.**
+##--- End android默认 ---
+
+##--- For:不能被混淆的 ---
+-keep public class * extends android.app.Activity
+-keep public class * extends android.app.Fragment
+-keep public class * extends android.app.Application
+-keep public class * extends android.app.Service
+-keep public class * extends android.content.BroadcastReceiver
+-keep public class * extends android.content.ContentProvider
+-keep public class * extends android.app.backup.BackupAgentHelper
+-keep public class * extends android.preference.Preference
+
+##--- For:保持自定义控件类不被混淆 ---
+-keepclasseswithmembers class * {
+ public (android.content.Context, android.util.AttributeSet);
+}
+-keepclasseswithmembers class * {
+ public (android.content.Context, android.util.AttributeSet, int);
+}
+##--- For:android-support-v4 ---
+-dontwarn android.support.v4.**
+-keep class android.support.v4.** { *; }
+-keep interface android.support.v4.app.** { *; }
+-keep class * extends android.support.v4.** { *; }
+-keep public class * extends android.support.v4.**
+-keep class * extends android.support.v4.app.** {*;}
+-keep class * extends android.support.v4.view.** {*;}
+
+##--- For:Serializable ---
+-keep class * implements java.io.Serializable {*;}
+-keepnames class * implements java.io.Serializable
+-keepclassmembers class * implements java.io.Serializable {*;}
+
+##--- For:Gson ---
+-keepattributes *Annotation*
+-keep class com.google.gson.stream.** { *; }
+
+
+##--- For:Remove log ---
+-assumenosideeffects class android.util.Log {
+ public static boolean isLoggable(java.lang.String, int);
+ public static int v(...);
+ public static int i(...);
+ public static int w(...);
+ public static int d(...);
+ public static int e(...);
+}
+
+##--- For:attributes(未启用) ---
+#-keepattributes SourceFile,LineNumberTable # 保持反编译工具能看到代码的行数,以及release包安装后出现异常信息可以知道在哪行代码出现异常,建议不启用
+-keepattributes *Annotation* #使用注解
+-keepattributes Signature #过滤泛型 出现类型转换错误时,启用这个
+#-keepattributes *Exceptions*,EnclosingMethod #没试过,未知效果
diff --git a/app/libs/_tts.zip b/app/libs/_tts.zip
new file mode 100644
index 0000000..59afdf8
Binary files /dev/null and b/app/libs/_tts.zip differ
diff --git a/app/libs/_tts_out/AndroidManifest.xml b/app/libs/_tts_out/AndroidManifest.xml
new file mode 100644
index 0000000..ea80047
--- /dev/null
+++ b/app/libs/_tts_out/AndroidManifest.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/libs/_tts_out/R.txt b/app/libs/_tts_out/R.txt
new file mode 100644
index 0000000..e69de29
diff --git a/app/libs/_tts_out/classes.jar b/app/libs/_tts_out/classes.jar
new file mode 100644
index 0000000..78ba3a8
Binary files /dev/null and b/app/libs/_tts_out/classes.jar differ
diff --git a/app/libs/_tts_out/proguard.txt b/app/libs/_tts_out/proguard.txt
new file mode 100644
index 0000000..e69de29
diff --git a/app/libs/asr-one-sentence-release.aar b/app/libs/asr-one-sentence-release.aar
new file mode 100644
index 0000000..fdafba2
Binary files /dev/null and b/app/libs/asr-one-sentence-release.aar differ
diff --git a/app/libs/asr-realtime-release.aar b/app/libs/asr-realtime-release.aar
new file mode 100644
index 0000000..c1e2c26
Binary files /dev/null and b/app/libs/asr-realtime-release.aar differ
diff --git a/app/libs/classes.jar b/app/libs/classes.jar
new file mode 100644
index 0000000..1e71a13
Binary files /dev/null and b/app/libs/classes.jar differ
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
index 481bb43..5b5739f 100644
--- a/app/proguard-rules.pro
+++ b/app/proguard-rules.pro
@@ -18,4 +18,11 @@
# If you keep the line number information, uncomment this to
# hide the original source file name.
-#-renamesourcefileattribute SourceFile
\ No newline at end of file
+#-renamesourcefileattribute SourceFile
+
+# 腾讯云实时语音识别 SDK(asr-realtime-release.aar)
+-keepclasseswithmembernames class * {
+ native ;
+}
+-keep public class com.tencent.aai.** { *; }
+-keep public class com.qq.wx.voice.** { *; }
\ No newline at end of file
diff --git a/app/src/androidTest/java/com/digitalperson/embedding/RefImageMatcherEmulatorRegressionTest.kt b/app/src/androidTest/java/com/digitalperson/embedding/RefImageMatcherEmulatorRegressionTest.kt
new file mode 100644
index 0000000..620d867
--- /dev/null
+++ b/app/src/androidTest/java/com/digitalperson/embedding/RefImageMatcherEmulatorRegressionTest.kt
@@ -0,0 +1,132 @@
+package com.digitalperson.embedding
+
+import android.content.Context
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.digitalperson.config.AppConfig
+import org.junit.Assert.assertNotNull
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * 模拟器参考图匹配(路径关键词 + 子串 + 编辑距离)回归测试。
+ *
+ * 运行方式(需设备/模拟器,且 assets 含 ref 语料):
+ * ./gradlew :app:connectedDebugAndroidTest --tests com.digitalperson.embedding.RefImageMatcherEmulatorRegressionTest
+ */
+@RunWith(AndroidJUnit4::class)
+class RefImageMatcherEmulatorRegressionTest {
+
+ private lateinit var context: Context
+
+ @Before
+ fun setUp() {
+ context = ApplicationProvider.getApplicationContext()
+ }
+
+ data class ManualCase(
+ val label: String,
+ val query: String,
+ /** 期望命中的 txt 路径中应包含该子串(如文件名一段) */
+ val expectedPathContains: String,
+ )
+
+ @Test
+ fun manualQueries_shouldMatchExpectedAsset() {
+ val cases = listOf(
+ ManualCase(
+ label = "上厕所指引(LLM 寒暄前缀)",
+ query = "嗨小朋友,可以帮老师个忙吗?同学们能指引图中的小朋友们进入正确的厕所吗?",
+ expectedPathContains = "上厕所18",
+ ),
+ ManualCase(
+ label = "刷牙看图(题干在讲卫生6,人1 为认读「人」非刷牙)",
+ query = "嗨小朋友,可以帮老师个忙吗?这个男生在做什么?",
+ expectedPathContains = "讲卫生6",
+ ),
+ ManualCase(
+ label = "元旦短句(生活适应)",
+ query = "元旦到了,小朋友可以对爸爸妈妈说:'爸爸妈妈,新年快乐!'",
+ expectedPathContains = "元旦14",
+ ),
+ )
+ val failures = mutableListOf()
+ for (c in cases) {
+ val m = RefImageMatcher.findBestMatchEditDistance(context, c.query)
+ when {
+ m == null -> failures += "${c.label}: 无匹配,期望路径含「${c.expectedPathContains}」 query=${c.query.take(80)}"
+ !m.txtAssetPath.contains(c.expectedPathContains) ->
+ failures += "${c.label}: 得到 ${m.txtAssetPath} score=${m.score},期望路径含「${c.expectedPathContains}」"
+ }
+ }
+ assertTrue(
+ "以下用例未命中预期资源:\n${failures.joinToString("\n")}",
+ failures.isEmpty(),
+ )
+ }
+
+ /**
+ * 语料自检:每条带 png 的 txt,用「寒暄 + 正文长片段」构造 query,应匹配回该 txt。
+ *
+ * 仅用首行会失败两类情况:(1)多篇课文首句相同(如男生女生 5/6);(2)首句拆出「小朋友」等
+ * 极短片段在多篇里子串命中同分,先遍历到的文件胜出。故这里用去空白后的正文前缀(约 400 字)提高区分度。
+ */
+ @Test
+ fun corpus_bodyPrefix_withGreetingPrefix_shouldMatchSameTxt() {
+ val root = AppConfig.RefCorpus.ASSETS_ROOT
+ val paths = RefCorpusAssetScanner.listTxtFilesUnder(context, root)
+ val greeting = "嗨小朋友,可以帮老师个忙吗?"
+ /** 与真实 LLM 提问长度同量级;更长更易唯一,但单测耗时略增 */
+ val maxBodyChars = 400
+ val failures = mutableListOf()
+ var skippedNoPng = 0
+ var skippedNoBody = 0
+ var checked = 0
+
+ for (txtPath in paths) {
+ val pngPath = if (txtPath.endsWith(".txt", ignoreCase = true)) {
+ txtPath.dropLast(4) + ".png"
+ } else {
+ "$txtPath.png"
+ }
+ val pngOk = try {
+ context.assets.open(pngPath).close()
+ true
+ } catch (_: Throwable) {
+ false
+ }
+ if (!pngOk) {
+ skippedNoPng++
+ continue
+ }
+ val raw = context.assets.open(txtPath).bufferedReader(Charsets.UTF_8).use { it.readText() }
+ val body = RefTxtEmbedText.fromRawFileContent(raw).trim()
+ if (body.length < 8) {
+ skippedNoBody++
+ continue
+ }
+ checked++
+ val compact = body.replace(Regex("\\s+"), " ").trim()
+ val core = compact.take(maxBodyChars)
+ val query = "$greeting$core"
+ val m = RefImageMatcher.findBestMatchEditDistance(context, query)
+ if (m == null) {
+ failures += "无匹配: $txtPath | core=${core.take(50)}"
+ continue
+ }
+ if (m.txtAssetPath != txtPath) {
+ failures += "错配: 期望 $txtPath | 得到 ${m.txtAssetPath} score=${m.score} | core=${core.take(50)}"
+ }
+ }
+
+ assertNotNull("语料为空或未打包进 assets", paths.takeIf { it.isNotEmpty() })
+ assertTrue(
+ "语料自检失败条数=${failures.size}(已检查=$checked,skip无png=$skippedNoPng,skip正文过短=$skippedNoBody):\n" +
+ failures.take(50).joinToString("\n") +
+ if (failures.size > 50) "\n... 共 ${failures.size} 条" else "",
+ failures.isEmpty(),
+ )
+ }
+}
diff --git a/app/src/androidTest/java/com/digitalperson/question/QuestionGenerationAgentTest.kt b/app/src/androidTest/java/com/digitalperson/question/QuestionGenerationAgentTest.kt
new file mode 100644
index 0000000..ed076fe
--- /dev/null
+++ b/app/src/androidTest/java/com/digitalperson/question/QuestionGenerationAgentTest.kt
@@ -0,0 +1,204 @@
+package com.digitalperson.question
+
+import android.content.Context
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.digitalperson.data.AppDatabase
+import com.digitalperson.data.entity.Question
+import com.digitalperson.interaction.UserMemoryStore
+import kotlinx.coroutines.runBlocking
+import org.json.JSONObject
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import java.io.InputStream
+
+/**
+ * 题目生成智能体测试
+ * 可以在模拟器或本地运行,不需要完整启动应用
+ */
+@RunWith(AndroidJUnit4::class)
+class QuestionGenerationAgentTest {
+
+ private lateinit var context: Context
+ private lateinit var database: AppDatabase
+ private lateinit var userMemoryStore: UserMemoryStore
+
+ @Before
+ fun setUp() {
+ context = ApplicationProvider.getApplicationContext()
+ database = AppDatabase.getInstance(context)
+ userMemoryStore = UserMemoryStore(context)
+ }
+
+ @After
+ fun tearDown() {
+ // 清理测试数据
+ // database.clearAllTables()
+ }
+
+ @Test
+ fun testLoadPromptPoolFromJson() {
+ // 测试JSON提示词池加载
+ val inputStream: InputStream = context.assets.open("question_prompts.json")
+ val jsonString = inputStream.bufferedReader().use { it.readText() }
+ val jsonArray = org.json.JSONArray(jsonString)
+
+ println("✅ Loaded ${jsonArray.length()} prompts from JSON")
+
+ // 验证每个提示词的格式
+ for (i in 0 until jsonArray.length()) {
+ val json = jsonArray.getJSONObject(i)
+ assert(json.has("subject")) { "Missing subject in prompt $i" }
+ assert(json.has("grade")) { "Missing grade in prompt $i" }
+ assert(json.has("topic")) { "Missing topic in prompt $i" }
+ assert(json.has("difficulty")) { "Missing difficulty in prompt $i" }
+ assert(json.has("promptTemplate")) { "Missing promptTemplate in prompt $i" }
+
+ println(" - Prompt $i: ${json.getString("subject")} / ${json.getString("topic")}")
+ }
+
+ assert(jsonArray.length() > 0) { "Should have at least 1 prompt" }
+ }
+
+ @Test
+ fun testQuestionDatabaseOperations() = runBlocking {
+ // 测试数据库操作
+ val questionDao = database.questionDao()
+
+ // 插入测试题目
+ val testQuestion = Question(
+ id = 0,
+ content = "测试题目:苹果和香蕉哪个大?",
+ answer = "香蕉",
+ subject = "生活数学",
+ grade = 1,
+ difficulty = 1,
+ createdAt = System.currentTimeMillis()
+ )
+
+ val questionId = questionDao.insert(testQuestion)
+ println("✅ Inserted question with ID: $questionId")
+
+ // 查询题目
+ val retrievedQuestion = questionDao.getQuestionById(questionId)
+ assert(retrievedQuestion != null) { "Should retrieve inserted question" }
+ assert(retrievedQuestion?.content == testQuestion.content) { "Content should match" }
+ println("✅ Retrieved question: ${retrievedQuestion?.content}")
+
+ // 测试未答题计数
+ val userId = "test_user_001"
+ val count = questionDao.countUnansweredQuestions(userId)
+ println("✅ Unanswered questions count: $count")
+
+ // 测试获取随机未答题
+ val randomQuestion = questionDao.getRandomUnansweredQuestion(userId)
+ println("✅ Random unanswered question: ${randomQuestion?.content?.take(20)}...")
+ }
+
+ @Test
+ fun testJsonResponseParsing() {
+ // 测试LLM JSON响应解析
+ val testResponses = listOf(
+ """{"content": "苹果和香蕉哪个大?", "answer": "香蕉", "explanation": "香蕉通常比苹果大"}""",
+ """
+ {
+ "content": "2个苹果和5个苹果比,谁多?",
+ "answer": "5个苹果多",
+ "explanation": "5大于2"
+ }
+ """,
+ """Some text before {"content": "测试题目", "answer": "答案"} some text after"""
+ )
+
+ testResponses.forEachIndexed { index, response ->
+ val json = extractJsonFromResponse(response)
+ if (json != null) {
+ println("✅ Test $index: Parsed successfully")
+ println(" Content: ${json.getString("content")}")
+ println(" Answer: ${json.getString("answer")}")
+ } else {
+ println("❌ Test $index: Failed to parse JSON")
+ }
+ }
+ }
+
+ @Test
+ fun testUserMemoryOperations() = runBlocking {
+ // 测试用户记忆操作
+ val userId = "test_user_001"
+
+ // 创建或更新用户
+ userMemoryStore.upsertUserSeen(userId, "测试小朋友")
+ println("✅ Created/updated user: $userId")
+
+ // 获取用户信息
+ val memory = userMemoryStore.getMemory(userId)
+ println("✅ User memory: displayName=${memory?.displayName}")
+
+ // 测试未答题计数
+ val unansweredCount = userMemoryStore.countUnansweredQuestions(userId)
+ println("✅ Unanswered questions for $userId: $unansweredCount")
+ }
+
+ @Test
+ fun testPromptTemplateBuilding() = runBlocking {
+ // 测试提示词模板构建
+ val userProfile = userMemoryStore.getMemory("test_user_001")
+
+ val promptTemplate = """
+ 你是一个专门为特殊教育儿童设计题目的教育专家。请根据以下要求生成一个题目:
+
+ 用户信息:
+ ${userProfile?.displayName?.let { "姓名:$it," } ?: ""}
+ ${userProfile?.age?.let { "年龄:$it," } ?: ""}
+
+ 学科:生活数学
+ 年级:1
+ 主题:比大小
+ 难度:1
+
+ 具体要求:
+ 基于以下学习目标,针对一年级小学生出1道题目:
+ 1. 初步感知物品的大小
+ 2. 会比较2个物品的大小
+
+ 通用要求:
+ 1. 题目要贴近生活,适合智力障碍儿童理解
+ 2. 语言简单明了,避免复杂句式
+ 3. 题目内容积极向上
+ 4. 提供标准答案
+ 5. 确保题目没有重复
+ 6. 题目要有趣味性,能吸引学生注意力
+
+ 请以JSON格式返回,格式如下:
+ {
+ "content": "题目内容",
+ "answer": "标准答案",
+ "explanation": "题目解析(可选)"
+ }
+
+ 只返回JSON,不要其他内容。
+ """.trimIndent()
+
+ println("✅ Generated prompt template:")
+ println(promptTemplate)
+ println("\n✅ Prompt length: ${promptTemplate.length} characters")
+ }
+
+ /**
+ * 从响应中提取JSON
+ */
+ private fun extractJsonFromResponse(response: String): JSONObject? {
+ val trimmed = response.trim()
+ val start = trimmed.indexOf('{')
+ val end = trimmed.lastIndexOf('}')
+
+ if (start >= 0 && end > start) {
+ val jsonStr = trimmed.substring(start, end + 1)
+ return JSONObject(jsonStr)
+ }
+ return null
+ }
+}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 39194bc..e98c6c1 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -4,6 +4,7 @@
+
diff --git a/app/src/main/assets/question_generation_flow.md b/app/src/main/assets/question_generation_flow.md
new file mode 100644
index 0000000..a14b67d
--- /dev/null
+++ b/app/src/main/assets/question_generation_flow.md
@@ -0,0 +1,264 @@
+# Question Generation Flow - Pre-generation Strategy
+
+## Problem Solved
+
+**Before:** First question would fail because database was empty.
+
+**After:** Questions are pre-generated when face appears, giving 20+ seconds before first question is asked.
+
+## Timeline Flow
+
+```
+Time: 0s 2s 20s 40s 60s
+ | | | | |
+ ↓ ↓ ↓ ↓ ↓
+ Face Greeting First Second Third
+Appears Ends Question Question Question
+ | | | | |
+ | | | | |
+ └─► Start │ │ │ │
+ Gen │ │ │ │
+ Questions│ │ │ │
+ (~10-20s)│ │ │ │
+ │ │ │ │
+ └───────────┴───────────┴───────────┘
+ Questions already
+ in database!
+```
+
+## Detailed Flow
+
+### 1. Face Detection (T=0s)
+```
+User shows face to camera
+ ↓
+FaceDetectionPipeline detects frontal face
+ ↓
+onFaceSignal(present=true, isFrontal=true)
+ ↓
+DigitalHumanInteractionController.onFacePresenceChanged()
+ ↓
+Handler.onFaceAppeared(userId) ← NEW TRIGGER
+```
+
+### 2. Pre-generation (T=0-20s)
+```
+QuestionGenerationAgent.onQuestionAsked(userId)
+ ↓
+Check unanswered count:
+ - If 0 questions → Generate 20 questions (2x minUnansweredQuestions)
+ - If < 10 questions → Generate enough to reach 10
+ - If >= 10 questions → Skip generation
+ ↓
+For each question needed:
+ 1. Get next prompt from pool (avoiding recently used)
+ 2. Build generation prompt with user profile
+ 3. LLM generates question (JSON)
+ 4. LLM reviews question quality
+ 5. If passed → Save to database
+ ↓
+Questions ready in database! ✓
+```
+
+### 3. Greeting Phase (T=2-5s)
+```
+Enter greeting state
+ ↓
+Digital person waves and says hello
+ ↓
+~3 seconds
+```
+
+### 4. First Question (T=20s)
+```
+enterProactive() → askProactiveTopic()
+ ↓
+Get random unanswered question from database
+ ↓
+✓ QUESTION EXISTS! (Pre-generated in step 2)
+ ↓
+Ask question to user
+ ↓
+onQuestionAsked(userId) ← Check again
+ ↓
+If count < 10 → Generate more questions
+```
+
+### 5. Subsequent Questions (T=40s, 60s, ...)
+```
+Each time a question is asked:
+ ↓
+onQuestionAsked(userId) triggered
+ ↓
+Check unanswered count
+ ↓
+If low → Generate more
+ ↓
+Database always stays stocked! ✓
+```
+
+## Key Benefits
+
+### ✅ No Empty Database
+- Questions generated BEFORE first question is needed
+- 20+ second head start during greeting phase
+
+### ✅ Smart Quantity
+- **First time:** Generate 20 questions (double the minimum)
+- **Replenishment:** Generate only what's needed to reach 10
+
+### ✅ Continuous Supply
+- Every question asked triggers a check
+- Database never runs empty
+
+### ✅ User-Aware
+- Uses user profile for personalization
+- Tracks unanswered questions per user
+
+## Configuration
+
+```kotlin
+data class AgentConfig(
+ val minUnansweredQuestions: Int = 10, // Minimum threshold
+ val batchSize: Int = 5, // (Deprecated - now calculates dynamically)
+ val generationTimeoutMs: Long = 30000 // LLM timeout
+)
+```
+
+### Calculated Values
+
+| Scenario | Unanswered Count | Action | Questions Generated |
+|----------|-----------------|--------|---------------------|
+| First time | 0 | Initial load | 20 (2x min) |
+| Running low | 5 | Replenish | 5 (to reach 10) |
+| Running low | 8 | Replenish | 2 (to reach 10) |
+| Sufficient | 10+ | Skip | 0 |
+| After question | 9 | Replenish | 1 (to reach 10) |
+
+## Generation Time Estimates
+
+Assuming ~3 seconds per question (generate + review):
+
+| Questions Needed | Estimated Time |
+|-----------------|----------------|
+| 5 questions | ~15 seconds |
+| 10 questions | ~30 seconds |
+| 20 questions | ~60 seconds |
+
+**Good news:** Generation happens in background, doesn't block greeting!
+
+## Trigger Points
+
+### 1. Face Appears (Pre-generation)
+```kotlin
+// In DigitalHumanInteractionController.kt
+if (state == InteractionState.IDLE || state == InteractionState.MEMORY || state == InteractionState.FAREWELL) {
+ handler.onFaceAppeared(currentFaceId ?: "guest") // ← Pre-generate
+ enterGreeting()
+}
+```
+
+### 2. Question Asked (Replenishment)
+```kotlin
+// In DigitalHumanInteractionController.kt
+handler.speak("嗨小朋友,可以帮老师个忙吗?" + topic)
+handler.onQuestionAsked(currentFaceId ?: "guest") // ← Replenish
+```
+
+## Logging Output
+
+### First Time User
+```
+I/QuestionGenAgent: Face appeared, triggering question pre-generation for user: user123
+I/QuestionGenAgent: User user123 has 0 unanswered questions (initial=true), generating 20 more...
+D/QuestionGenAgent: Generating question 1/20 for user user123
+I/QuestionGenAgent: Question saved: 请说出你家里有几口人...
+D/QuestionGenAgent: Generating question 2/20 for user user123
+...
+I/QuestionGenAgent: Finished generating questions for user user123
+```
+
+### Returning User (Low Questions)
+```
+I/QuestionGenAgent: User user123 has 3 unanswered questions, generating 7 more...
+D/QuestionGenAgent: Generating question 1/7 for user user123
+...
+I/QuestionGenAgent: Finished generating questions for user user123
+```
+
+### Sufficient Questions
+```
+D/QuestionGenAgent: User user123 has 15 unanswered questions, no need to generate
+```
+
+## Edge Cases Handled
+
+### ❌ No Face Detection
+- No pre-generation triggered
+- User must show face first
+
+### ❌ LLM Generation Fails
+- Gracefully skips failed question
+- Continues with next question
+- Logs error for debugging
+
+### ❌ Review Fails
+- Question discarded
+- Not saved to database
+- Moves to next question
+
+### ❌ Database Empty at Question Time
+- Very unlikely (20s head start)
+- Falls back to default question: "你喜欢什么颜色呀?"
+
+## Performance Optimization
+
+### Asynchronous Generation
+```kotlin
+agentScope.launch {
+ checkAndGenerateForUser(userId) // Non-blocking
+}
+```
+- Doesn't block UI thread
+- Doesn't block greeting
+- Runs completely in background
+
+### Smart Prompt Selection
+- Tracks last 10 used prompts
+- Avoids repetition
+- Ensures diversity
+
+### Delay Between Questions
+```kotlin
+delay(1000) // 1 second between each question
+```
+- Prevents overwhelming LLM
+- Allows proper processing time
+
+## Testing Scenarios
+
+### Test 1: First Time User
+1. Clear database
+2. Show face to camera
+3. Check logs: Should see "generating 20 more"
+4. Wait 20 seconds
+5. First question should work ✓
+
+### Test 2: Returning User
+1. User already has 5 unanswered questions
+2. Show face to camera
+3. Check logs: Should see "generating 5 more"
+4. Questions should replenish ✓
+
+### Test 3: Continuous Usage
+1. User answers questions continuously
+2. Each question triggers check
+3. Database should stay above 10 questions ✓
+
+## Future Enhancements
+
+- [ ] Priority-based generation (generate harder questions as user progresses)
+- [ ] Track question difficulty and adjust based on user performance
+- [ ] Pre-generate during idle time (not just on face appearance)
+- [ ] Cache generated questions for offline use
+- [ ] Support multiple users with different question pools
diff --git a/app/src/main/assets/question_prompts_README.md b/app/src/main/assets/question_prompts_README.md
new file mode 100644
index 0000000..90a9e66
--- /dev/null
+++ b/app/src/main/assets/question_prompts_README.md
@@ -0,0 +1,221 @@
+# Question Generation Agent - Prompt Pool Guide
+
+## Overview
+
+The Question Generation Agent uses a JSON-based prompt pool to generate diverse educational questions for special needs children. The system automatically loads prompts from `question_prompts.json` and intelligently selects prompts to ensure diversity.
+
+## File Location
+
+```
+app/src/main/assets/question_prompts.json
+```
+
+## JSON Structure
+
+Each prompt in the pool follows this structure:
+
+```json
+{
+ "subject": "生活适应",
+ "grade": 1,
+ "topic": "认识家庭成员",
+ "difficulty": 1,
+ "promptTemplate": "生成一个关于认识家庭成员的题目,适合自闭症儿童,要求题目贴近日常生活,语言简单易懂"
+}
+```
+
+### Fields Description
+
+| Field | Type | Description | Example |
+|-------|------|-------------|---------|
+| `subject` | String | Subject category | "生活适应", "生活语文" |
+| `grade` | Integer | Grade level (1-6) | 1 |
+| `topic` | String | Specific topic name | "认识家庭成员" |
+| `difficulty` | Integer | Difficulty level (1-5) | 1 |
+| `promptTemplate` | String | Detailed instruction for LLM | "生成一个关于...的题目" |
+
+## How to Add New Prompts
+
+### Step 1: Open the JSON file
+
+Open `app/src/main/assets/question_prompts.json` in any text editor.
+
+### Step 2: Add a new prompt object
+
+Add a new object to the JSON array:
+
+```json
+{
+ "subject": "生活适应",
+ "grade": 1,
+ "topic": "你的新主题",
+ "difficulty": 2,
+ "promptTemplate": "生成一个关于[主题描述]的题目,要求[具体要求]"
+}
+```
+
+### Step 3: Save and restart
+
+The prompts are loaded when the app starts. Restart the app to load new prompts.
+
+## Prompt Template Guidelines
+
+### Good Prompt Templates
+
+✅ **Specific and detailed:**
+```json
+"promptTemplate": "生成一个关于认识家庭成员的题目,包括爸爸、妈妈、爷爷、奶奶等,要求描述家庭成员之间的关系和称呼"
+```
+
+✅ **Include context:**
+```json
+"promptTemplate": "生成一个关于交通安全的题目,例如红绿灯、斑马线、过马路等,要求强调安全规则和实际应用场景"
+```
+
+✅ **Specify requirements:**
+```json
+"promptTemplate": "生成一个关于认识颜色的题目,包括红、黄、蓝、绿等基本颜色,要求联系实际生活中的物品,如红色的苹果、黄色的香蕉等"
+```
+
+### Bad Prompt Templates
+
+❌ **Too vague:**
+```json
+"promptTemplate": "生成一个题目"
+```
+
+❌ **Too broad:**
+```json
+"promptTemplate": "生成一个关于数学的题目"
+```
+
+❌ **Not specific to special education:**
+```json
+"promptTemplate": "生成一个难一点的题目"
+```
+
+## Current Prompt Categories
+
+### 生活适应 (Life Adaptation) - 25 prompts
+- 认识家庭成员 (Family Members)
+- 认识日常用品 (Daily Items)
+- 认识天气 (Weather)
+- 认识季节 (Seasons)
+- 交通安全 (Traffic Safety)
+- 认识食物 (Food)
+- 个人卫生 (Personal Hygiene)
+- 认识动物 (Animals)
+- 情绪识别 (Emotion Recognition)
+- 社交礼仪 (Social Etiquette)
+- 时间概念 (Time Concepts)
+- 认识颜色 (Colors)
+- 认识形状 (Shapes)
+- 数数练习 (Counting)
+- 身体部位 (Body Parts)
+- 穿衣自理 (Dressing)
+- 整理物品 (Organizing)
+- 认识数字 (Numbers)
+- 简单加减法 (Basic Math)
+- 认识钱币 (Money)
+- 认识地图 (Maps)
+- 紧急情况 (Emergencies)
+- 公共场合行为 (Public Behavior)
+- 认识职业 (Professions)
+- 节日文化 (Festivals)
+
+### 生活语文 (Life Chinese) - 6 prompts
+- 认识汉字 (Chinese Characters)
+- 简单句子理解 (Sentence Understanding)
+- 词语配对 (Word Pairing)
+- 看图说话 (Picture Description)
+- 反义词 (Antonyms)
+- 量词使用 (Measure Words)
+- 标点符号 (Punctuation)
+
+## Diversity Mechanism
+
+The system ensures question diversity through:
+
+1. **Prompt Pool Rotation**: Intelligently selects prompts that haven't been used recently
+2. **Recent Usage Tracking**: Remembers the last 10 used prompts and avoids them
+3. **Topic Tracking**: Records generated topics to prevent duplicate questions
+4. **Automatic Replenishment**: Generates new questions when the unanswered count drops below threshold
+
+## Configuration
+
+You can configure the agent in `QuestionGenerationAgent.kt`:
+
+```kotlin
+data class AgentConfig(
+ val minUnansweredQuestions: Int = 10, // Minimum questions before triggering generation
+ val batchSize: Int = 5, // Number of questions to generate per batch
+ val generationTimeoutMs: Long = 30000 // LLM generation timeout
+)
+```
+
+## Trigger Mechanism
+
+Questions are generated **event-driven**, not time-driven:
+
+1. User asks a question to the child
+2. `onQuestionAsked(userId)` is triggered
+3. System checks if unanswered questions < `minUnansweredQuestions`
+4. If yes, generates `batchSize` new questions
+5. Each question goes through: Generate → Review → Save to Database
+
+## Testing
+
+To manually trigger question generation:
+
+```kotlin
+questionGenerationAgent.triggerGeneration(userId)
+```
+
+## Troubleshooting
+
+### Prompts not loading
+- Check if `question_prompts.json` exists in `app/src/main/assets/`
+- Verify JSON syntax is valid (no trailing commas, proper quotes)
+- Check Logcat for error messages
+
+### Questions not generating
+- Ensure LLM is properly initialized
+- Check if `llmManager` is not null
+- Verify the user has been seen by the face detection system
+
+### Low diversity
+- Add more prompts to the JSON file
+- Increase `maxRecentPrompts` in the agent
+- Check if prompts have varied `topic` values
+
+## Best Practices
+
+1. **Be Specific**: Write detailed prompt templates that specify the exact requirements
+2. **Include Examples**: Provide examples in the prompt template to guide the LLM
+3. **Vary Topics**: Create many different topics within each subject
+4. **Consider Difficulty**: Use appropriate difficulty levels (1-5) for special education
+5. **Test Prompts**: Test each new prompt to ensure it generates quality questions
+6. **Keep Updated**: Regularly review and update prompts based on generated question quality
+
+## Example: Adding a New Subject
+
+```json
+{
+ "subject": "生活数学",
+ "grade": 1,
+ "topic": "比较大小",
+ "difficulty": 1,
+ "promptTemplate": "生成一个关于比较大小的题目,例如哪个更大、哪个更小,要求使用具体的物品如苹果、球等作为比较对象"
+},
+{
+ "subject": "生活数学",
+ "grade": 1,
+ "topic": "简单分类",
+ "difficulty": 2,
+ "promptTemplate": "生成一个关于分类的题目,例如把水果和蔬菜分开,把动物和植物分开,要求分类标准明确"
+}
+```
+
+## Support
+
+For questions or issues, check the Logcat output for error messages and review this guide.
diff --git a/app/src/main/assets/question_prompts_example.json b/app/src/main/assets/question_prompts_example.json
new file mode 100644
index 0000000..58195f6
--- /dev/null
+++ b/app/src/main/assets/question_prompts_example.json
@@ -0,0 +1,305 @@
+{
+ "_comment": "This is an EXAMPLE file showing how to add more prompts. Copy the prompts you want to the main question_prompts.json file.",
+
+ "_examples": [
+ {
+ "_description": "Example 1: Life Math - Comparing Sizes",
+ "subject": "生活数学",
+ "grade": 1,
+ "topic": "比较大小",
+ "difficulty": 1,
+ "promptTemplate": "生成一个关于比较大小的题目,例如哪个更大、哪个更小,要求使用具体的物品如苹果、球等作为比较对象"
+ },
+ {
+ "_description": "Example 2: Life Math - Simple Classification",
+ "subject": "生活数学",
+ "grade": 1,
+ "topic": "简单分类",
+ "difficulty": 2,
+ "promptTemplate": "生成一个关于分类的题目,例如把水果和蔬菜分开,把动物和植物分开,要求分类标准明确"
+ },
+ {
+ "_description": "Example 3: Life Adaptation - School Rules",
+ "subject": "生活适应",
+ "grade": 1,
+ "topic": "学校规则",
+ "difficulty": 2,
+ "promptTemplate": "生成一个关于学校规则的题目,例如上课要举手发言、不能在走廊奔跑等,要求强调遵守规则的重要性"
+ },
+ {
+ "_description": "Example 4: Life Chinese - Simple Reading",
+ "subject": "生活语文",
+ "grade": 1,
+ "topic": "简单阅读",
+ "difficulty": 3,
+ "promptTemplate": "生成一个简单阅读理解题目,提供2-3句话的短文,然后提出一个关于短文内容的问题"
+ },
+ {
+ "_description": "Example 5: Life Adaptation - Healthy Habits",
+ "subject": "生活适应",
+ "grade": 1,
+ "topic": "健康习惯",
+ "difficulty": 2,
+ "promptTemplate": "生成一个关于健康习惯的题目,例如早睡早起、多喝水、多运动等,要求解释为什么这些习惯好"
+ },
+ {
+ "_description": "Example 6: Life Adaptation - Using Phone",
+ "subject": "生活适应",
+ "grade": 1,
+ "topic": "使用电话",
+ "difficulty": 3,
+ "promptTemplate": "生成一个关于如何使用电话的题目,包括拨号、接听、挂断等基本操作,要求在紧急情况下如何求助"
+ },
+ {
+ "_description": "Example 7: Life Chinese - Sentence Completion",
+ "subject": "生活语文",
+ "grade": 1,
+ "topic": "句子填空",
+ "difficulty": 2,
+ "promptTemplate": "生成一个句子填空题,例如'我喜欢吃___',要求提供合适的选项让学生选择"
+ },
+ {
+ "_description": "Example 8: Life Adaptation - Weather Clothing",
+ "subject": "生活适应",
+ "grade": 1,
+ "topic": "天气与穿衣",
+ "difficulty": 2,
+ "promptTemplate": "生成一个关于根据天气选择衣服的题目,例如下雨天穿雨衣、冬天穿棉袄等,要求联系实际生活场景"
+ }
+ ],
+
+ "_instructions": "To add these prompts to your system:",
+ "_step1": "1. Open app/src/main/assets/question_prompts.json",
+ "_step2": "2. Copy the objects from the _examples array (without the _description field)",
+ "_step3": "3. Paste them into the main JSON array",
+ "_step4": "4. Save the file and restart the app"
+}
+
+
+
+[
+ {
+ "subject": "生活适应",
+ "grade": 1,
+ "topic": "认识家庭成员",
+ "difficulty": 1,
+ "promptTemplate": "生成一个关于认识家庭成员的题目,适合智力障碍儿童,要求题目贴近日常生活,语言简单易懂"
+ },
+ {
+ "subject": "生活适应",
+ "grade": 1,
+ "topic": "认识日常用品",
+ "difficulty": 1,
+ "promptTemplate": "生成一个关于认识日常用品的题目,例如牙刷、毛巾、杯子等,要求描述物品的用途和使用场景"
+ },
+ {
+ "subject": "生活适应",
+ "grade": 1,
+ "topic": "认识天气",
+ "difficulty": 1,
+ "promptTemplate": "生成一个关于认识天气的题目,包括晴天、雨天、阴天等,要求联系实际生活场景"
+ },
+ {
+ "subject": "生活适应",
+ "grade": 1,
+ "topic": "认识季节",
+ "difficulty": 2,
+ "promptTemplate": "生成一个关于认识四季的题目,要求描述不同季节的特点和对应的活动"
+ },
+ {
+ "subject": "生活适应",
+ "grade": 1,
+ "topic": "交通安全",
+ "difficulty": 2,
+ "promptTemplate": "生成一个关于交通安全的题目,例如红绿灯、斑马线、过马路等,要求强调安全规则"
+ },
+ {
+ "subject": "生活适应",
+ "grade": 1,
+ "topic": "认识食物",
+ "difficulty": 1,
+ "promptTemplate": "生成一个关于认识常见食物的题目,包括水果、蔬菜、主食等,要求联系实际饮食场景"
+ },
+ {
+ "subject": "生活适应",
+ "grade": 1,
+ "topic": "个人卫生",
+ "difficulty": 1,
+ "promptTemplate": "生成一个关于个人卫生的题目,例如洗手、刷牙、洗澡等,要求强调卫生习惯的重要性"
+ },
+ {
+ "subject": "生活适应",
+ "grade": 1,
+ "topic": "认识动物",
+ "difficulty": 1,
+ "promptTemplate": "生成一个关于认识常见动物的题目,包括猫、狗、鸟、鱼等,要求描述动物的特征和习性"
+ },
+ {
+ "subject": "生活适应",
+ "grade": 1,
+ "topic": "情绪识别",
+ "difficulty": 2,
+ "promptTemplate": "生成一个关于识别情绪的题目,例如高兴、伤心、生气、害怕等,要求联系实际情境"
+ },
+ {
+ "subject": "生活适应",
+ "grade": 1,
+ "topic": "社交礼仪",
+ "difficulty": 2,
+ "promptTemplate": "生成一个关于社交礼仪的题目,例如打招呼、说谢谢、对不起等,要求强调礼貌用语的使用场景"
+ },
+ {
+ "subject": "生活适应",
+ "grade": 1,
+ "topic": "时间概念",
+ "difficulty": 2,
+ "promptTemplate": "生成一个关于时间概念的题目,例如早上、中午、晚上、昨天、今天、明天等,要求联系日常生活作息"
+ },
+ {
+ "subject": "生活适应",
+ "grade": 1,
+ "topic": "认识颜色",
+ "difficulty": 1,
+ "promptTemplate": "生成一个关于认识颜色的题目,包括红、黄、蓝、绿等基本颜色,要求联系实际生活中的物品"
+ },
+ {
+ "subject": "生活适应",
+ "grade": 1,
+ "topic": "认识形状",
+ "difficulty": 1,
+ "promptTemplate": "生成一个关于认识形状的题目,例如圆形、方形、三角形等,要求联系实际生活中的物品"
+ },
+ {
+ "subject": "生活适应",
+ "grade": 1,
+ "topic": "数数练习",
+ "difficulty": 1,
+ "promptTemplate": "生成一个关于数数的题目,要求10以内的数量,联系实际场景如水果、玩具等"
+ },
+ {
+ "subject": "生活适应",
+ "grade": 1,
+ "topic": "身体部位",
+ "difficulty": 1,
+ "promptTemplate": "生成一个关于认识身体部位的题目,例如手、脚、头、眼睛、耳朵等,要求描述部位的功能"
+ },
+ {
+ "subject": "生活适应",
+ "grade": 1,
+ "topic": "穿衣自理",
+ "difficulty": 2,
+ "promptTemplate": "生成一个关于穿衣自理的题目,要求描述穿衣的步骤和注意事项"
+ },
+ {
+ "subject": "生活适应",
+ "grade": 1,
+ "topic": "整理物品",
+ "difficulty": 2,
+ "promptTemplate": "生成一个关于整理物品的题目,例如整理书包、玩具、房间等,要求强调整理的重要性"
+ },
+ {
+ "subject": "生活语文",
+ "grade": 1,
+ "topic": "认识汉字",
+ "difficulty": 1,
+ "promptTemplate": "生成一个关于认识简单汉字的题目,要求选择日常生活中常用的高频汉字"
+ },
+ {
+ "subject": "生活语文",
+ "grade": 1,
+ "topic": "简单句子理解",
+ "difficulty": 2,
+ "promptTemplate": "生成一个关于简单句子理解的题目,要求句子简短,贴近生活场景"
+ },
+ {
+ "subject": "生活语文",
+ "grade": 1,
+ "topic": "词语配对",
+ "difficulty": 1,
+ "promptTemplate": "生成一个关于词语配对的题目,例如苹果-水果、猫-动物等,要求逻辑关系简单明了"
+ },
+ {
+ "subject": "生活语文",
+ "grade": 1,
+ "topic": "看图说话",
+ "difficulty": 2,
+ "promptTemplate": "生成一个看图说话的题目,用文字描述一个简单场景,让学生用一两句话描述看到的内容"
+ },
+ {
+ "subject": "生活语文",
+ "grade": 1,
+ "topic": "反义词",
+ "difficulty": 2,
+ "promptTemplate": "生成一个关于反义词的题目,例如大-小、高-矮、快-慢等,要求选择常用的反义词对"
+ },
+ {
+ "subject": "生活语文",
+ "grade": 1,
+ "topic": "量词使用",
+ "difficulty": 2,
+ "promptTemplate": "生成一个关于量词使用的题目,例如一个苹果、一本书、一只猫等,要求选择常见的量词搭配"
+ },
+ {
+ "subject": "生活语文",
+ "grade": 1,
+ "topic": "标点符号",
+ "difficulty": 2,
+ "promptTemplate": "生成一个关于标点符号的题目,主要涉及句号、问号、感叹号的基本使用"
+ },
+ {
+ "subject": "生活适应",
+ "grade": 1,
+ "topic": "认识数字",
+ "difficulty": 1,
+ "promptTemplate": "生成一个关于认识数字的题目,要求1-10的数字识别和书写"
+ },
+ {
+ "subject": "生活适应",
+ "grade": 1,
+ "topic": "简单加减法",
+ "difficulty": 2,
+ "promptTemplate": "生成一个关于简单加减法的题目,要求10以内的加减法,联系实际场景"
+ },
+ {
+ "subject": "生活适应",
+ "grade": 1,
+ "topic": "认识钱币",
+ "difficulty": 3,
+ "promptTemplate": "生成一个关于认识钱币的题目,包括元、角、分的认识,要求联系实际购物场景"
+ },
+ {
+ "subject": "生活适应",
+ "grade": 1,
+ "topic": "认识地图",
+ "difficulty": 3,
+ "promptTemplate": "生成一个关于认识简单地图的题目,例如家庭、学校的平面图,要求理解基本方位"
+ },
+ {
+ "subject": "生活适应",
+ "grade": 1,
+ "topic": "紧急情况",
+ "difficulty": 3,
+ "promptTemplate": "生成一个关于紧急情况处理的题目,例如着火、地震、迷路等,要求强调安全自救知识"
+ },
+ {
+ "subject": "生活适应",
+ "grade": 1,
+ "topic": "公共场合行为",
+ "difficulty": 2,
+ "promptTemplate": "生成一个关于公共场合行为规范的题目,例如图书馆、医院、公交车等场景的礼仪"
+ },
+ {
+ "subject": "生活适应",
+ "grade": 1,
+ "topic": "认识职业",
+ "difficulty": 2,
+ "promptTemplate": "生成一个关于认识常见职业的题目,例如医生、老师、警察、消防员等,要求描述职业的工作内容"
+ },
+ {
+ "subject": "生活适应",
+ "grade": 1,
+ "topic": "节日文化",
+ "difficulty": 2,
+ "promptTemplate": "生成一个关于传统节日的题目,例如春节、中秋节、端午节等,要求介绍节日的习俗和意义"
+ }
+]
diff --git a/app/src/main/java/com/digitalperson/UnityDigitalPersonActivity.kt b/app/src/main/java/com/digitalperson/UnityDigitalPersonActivity.kt
index c5ea9d1..aa3e7cc 100644
--- a/app/src/main/java/com/digitalperson/UnityDigitalPersonActivity.kt
+++ b/app/src/main/java/com/digitalperson/UnityDigitalPersonActivity.kt
@@ -6,6 +6,7 @@ import android.content.Context
import android.content.pm.PackageManager
import android.content.res.ColorStateList
import android.graphics.Color
+import android.graphics.BitmapFactory
import android.os.Build
import android.os.Bundle
import android.os.Handler
@@ -16,6 +17,7 @@ import android.util.Log
import android.view.MotionEvent
import android.view.ViewGroup
import android.widget.Button
+import android.widget.ImageView
import android.widget.TextView
import android.widget.Toast
import androidx.core.app.ActivityCompat
@@ -25,7 +27,6 @@ import android.view.View
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LifecycleRegistry
-import android.widget.ImageView
import com.unity3d.player.UnityPlayer
import com.unity3d.player.UnityPlayerActivity
import com.digitalperson.audio.AudioProcessor
@@ -33,6 +34,7 @@ import com.digitalperson.asr.AsrManager
import com.digitalperson.cloud.CloudApiManager
import com.digitalperson.cloud.CloudReflectionHelper
import com.digitalperson.config.AppConfig
+import com.digitalperson.embedding.RefImageMatcher
import com.digitalperson.question.QuestionGenerationAgent
import com.digitalperson.data.AppDatabase
import com.digitalperson.face.FaceDetectionPipeline
@@ -48,8 +50,6 @@ import com.digitalperson.tts.TtsController
import com.digitalperson.util.FileHelper
import com.digitalperson.vad.VadManager
import kotlinx.coroutines.*
-import com.digitalperson.embedding.RefImageMatcher
-import android.graphics.BitmapFactory
class UnityDigitalPersonActivity : UnityPlayerActivity(), LifecycleOwner {
@@ -260,6 +260,11 @@ class UnityDigitalPersonActivity : UnityPlayerActivity(), LifecycleOwner {
recordButtonGlow = chatLayout.findViewById(R.id.record_button_glow)
refMatchImageView = chatLayout.findViewById(R.id.ref_match_image)
+ if (!AppConfig.SHOW_DEBUG_TEXT) {
+ chatHistoryText.visibility = View.GONE
+ chatLayout.findViewById(R.id.scroll_view).visibility = View.GONE
+ }
+
// 根据配置设置按钮可见性
if (AppConfig.USE_HOLD_TO_SPEAK) {
holdToSpeakButton.visibility = View.VISIBLE
@@ -366,6 +371,8 @@ class UnityDigitalPersonActivity : UnityPlayerActivity(), LifecycleOwner {
override fun onLlmCalled(text: String) {
Log.d("UnityDigitalPerson", "LLM called with: $text")
interactionCoordinator.onUserAsrText(text)
+ // 用用户问题提前匹配:比等 LLM 回复更早显示图片(模拟器/真机通用)
+ maybeShowMatchedRefImage(text)
}
})
setAudioProcessor(audioProcessor)
@@ -664,6 +671,7 @@ class UnityDigitalPersonActivity : UnityPlayerActivity(), LifecycleOwner {
}
private fun appendChat(text: String) {
+ if (!AppConfig.SHOW_DEBUG_TEXT) return
runOnUiThread {
chatHistoryText.append(text + "\n")
}
@@ -696,6 +704,8 @@ class UnityDigitalPersonActivity : UnityPlayerActivity(), LifecycleOwner {
override fun onSpeak(text: String) {
ttsController.enqueueSegment(text)
ttsController.enqueueEnd()
+ // 主动发言(问候/主动提问)也尝试匹配参考图片
+ maybeShowMatchedRefImage(text)
}
override fun onRequestCloudReply(prompt: String) {
@@ -759,13 +769,22 @@ class UnityDigitalPersonActivity : UnityPlayerActivity(), LifecycleOwner {
private fun maybeShowMatchedRefImage(text: String) {
val imageView = refMatchImageView ?: return
- // Unity Activity already has coroutines
- CoroutineScope(SupervisorJob() + Dispatchers.IO).launch {
+ // 每次匹配前先清掉上一张图
+ runOnUiThread {
+ imageView.setImageBitmap(null)
+ imageView.visibility = View.GONE
+ }
+ ioScope.launch {
val match = RefImageMatcher.findBestMatch(applicationContext, text)
- if (match == null) return@launch
+ if (match == null) {
+ Log.d("RefImageMatch", "未找到匹配图片 query=\"${text.take(80)}\"")
+ return@launch
+ }
+ Log.d("RefImageMatch", "匹配成功 score=${match.score} path=${match.pngAssetPath} query=\"${text.take(80)}\"")
val bitmap = try {
assets.open(match.pngAssetPath).use { BitmapFactory.decodeStream(it) }
- } catch (_: Throwable) {
+ } catch (e: Throwable) {
+ Log.w("RefImageMatch", "图片加载失败 path=${match.pngAssetPath}", e)
null
}
if (bitmap == null) return@launch
diff --git a/app/src/main/java/com/digitalperson/asr/AsrManager.kt b/app/src/main/java/com/digitalperson/asr/AsrManager.kt
index cda741b..b236a67 100644
--- a/app/src/main/java/com/digitalperson/asr/AsrManager.kt
+++ b/app/src/main/java/com/digitalperson/asr/AsrManager.kt
@@ -6,6 +6,7 @@ import android.util.Log
import com.digitalperson.BuildConfig
import com.digitalperson.audio.AudioProcessor
import com.digitalperson.config.AppConfig
+import com.digitalperson.env.RuntimeEnv
import com.digitalperson.engine.SenseVoiceEngineRKNN
import com.digitalperson.util.FileHelper
import kotlinx.coroutines.Dispatchers
@@ -23,7 +24,6 @@ class AsrManager(private val context: Context) {
private var senseVoice: SenseVoiceEngineRKNN? = null
private val nativeLock = Any()
-
private val asrQueue = Channel>(capacity = Channel.UNLIMITED)
private var audioProcessor: AudioProcessor? = null
@@ -48,6 +48,10 @@ class AsrManager(private val context: Context) {
}
fun initSenseVoiceModel(): Boolean {
+ if (RuntimeEnv.isEmulator()) {
+ Log.w(TAG, "ASR: emulator detected; skip local RKNN init and use cloud ASR")
+ return false
+ }
return try {
Log.i(TAG, "ASR: init SenseVoice RKNN (scheme A)")
@@ -133,23 +137,47 @@ class AsrManager(private val context: Context) {
Log.d(TAG, "ASR started: processing audio segment")
saveAsrAudio(originalSeg, processedSeg)
-
- val raw = synchronized(nativeLock) {
+
+ val localText = synchronized(nativeLock) {
val e = senseVoice
if (e == null || !e.isInitialized) {
- Log.e(TAG, "ASR failed: SenseVoice engine not initialized")
""
} else {
try {
- e.transcribeBuffer(processedSeg)
- } catch (e: Throwable) {
- Log.e(TAG, "ASR transcribe failed: ${e.message}")
+ removeTokens(e.transcribeBuffer(processedSeg))
+ } catch (t: Throwable) {
+ Log.e(TAG, "ASR transcribe failed: ${t.message}")
""
}
}
+ }.trim()
+
+ val text = if (localText.isNotBlank()) {
+ localText
+ } else {
+ // 模拟器或本地 RKNN 未就绪:使用腾讯云「一句话识别」SDK(app/libs/asr-one-sentence-release.aar)
+ val shouldTryTencent =
+ BuildConfig.HAS_TENCENT_ASR_SDK && (RuntimeEnv.isEmulator() || !isInitialized())
+ if (!shouldTryTencent) {
+ Log.e(
+ TAG,
+ "ASR failed: local RKNN not ready and Tencent SDK unavailable " +
+ "(add libs/asr-one-sentence-release.aar or fix SenseVoice init)"
+ )
+ ""
+ } else {
+ withContext(Dispatchers.IO) {
+ try {
+ // 云端 ASR 使用原始录音(未经 AEC/NS):
+ // 模拟器上 AEC/NS 不可用,processedSeg 可能被处理成近似静音
+ TencentOneSentenceAsr.transcribePcm16Mono(originalSeg)
+ } catch (t: Throwable) {
+ Log.e(TAG, "Tencent ASR failed: ${t.message}")
+ ""
+ }
+ }.trim()
+ }
}
- Log.d(TAG, "ASR raw result: $raw")
- val text = removeTokens(raw)
val filterResult = filterText(text)
if (filterResult != null) {
@@ -220,4 +248,5 @@ class AsrManager(private val context: Context) {
}
return null
}
+
}
diff --git a/app/src/main/java/com/digitalperson/asr/TencentOneSentenceAsr.kt b/app/src/main/java/com/digitalperson/asr/TencentOneSentenceAsr.kt
new file mode 100644
index 0000000..b5363d5
--- /dev/null
+++ b/app/src/main/java/com/digitalperson/asr/TencentOneSentenceAsr.kt
@@ -0,0 +1,216 @@
+package com.digitalperson.asr
+
+import android.util.Base64
+import android.util.Log
+import com.digitalperson.config.AppConfig
+import okhttp3.MediaType.Companion.toMediaType
+import okhttp3.OkHttpClient
+import okhttp3.Request
+import okhttp3.RequestBody.Companion.toRequestBody
+import org.json.JSONObject
+import java.nio.ByteBuffer
+import java.nio.ByteOrder
+import java.security.MessageDigest
+import java.text.SimpleDateFormat
+import java.util.Date
+import java.util.Locale
+import java.util.TimeZone
+import java.util.concurrent.TimeUnit
+import javax.crypto.Mac
+import javax.crypto.spec.SecretKeySpec
+
+/**
+ * 腾讯云「一句话识别」REST API 直接实现(TC3-HMAC-SHA256 签名)。
+ *
+ * 不依赖 SDK AAR,而是用 OkHttp 自行签名并发起 HTTP 请求。
+ * 签名时间戳从服务器 Date 响应头获取,彻底规避模拟器时钟偏差导致的
+ * AuthFailure.SignatureExpire 错误。
+ *
+ * 文档:https://cloud.tencent.com/document/product/1093/35646
+ */
+object TencentOneSentenceAsr {
+
+ private const val TAG = "TencentOneSentenceAsr"
+ private const val HOST = "asr.tencentcloudapi.com"
+ private const val ACTION = "SentenceRecognition"
+ private const val VERSION = "2019-06-14"
+
+ private val client = OkHttpClient.Builder()
+ .connectTimeout(10, TimeUnit.SECONDS)
+ .readTimeout(30, TimeUnit.SECONDS)
+ .build()
+
+ /**
+ * 将 FloatArray (16kHz mono, -1..1) 通过腾讯云一句话识别转为文字。
+ * 阻塞直到 HTTP 响应返回或超时。请在 IO 线程中调用。
+ */
+ fun transcribePcm16Mono(pcmFloat: FloatArray): String {
+ val appId = AppConfig.QCloud.APP_ID.trim()
+ val sid = AppConfig.QCloud.SECRET_ID.trim()
+ val skey = AppConfig.QCloud.SECRET_KEY.trim()
+ if (appId.isEmpty() || sid.isEmpty() || skey.isEmpty()) {
+ Log.e(TAG, "APP_ID / SECRET_ID / SECRET_KEY 为空")
+ return ""
+ }
+ if (pcmFloat.isEmpty()) return ""
+
+ val pcmBytes = floatToPcm16Bytes(pcmFloat)
+ val pcmBase64 = Base64.encodeToString(pcmBytes, Base64.NO_WRAP)
+
+ // 诊断:检查音频幅度,若 RMS 接近 0 说明麦克风没采集到声音
+ val rms = kotlin.math.sqrt(pcmFloat.fold(0.0) { acc, v -> acc + v * v } / pcmFloat.size)
+ val maxAmp = pcmFloat.maxOf { kotlin.math.abs(it) }
+ Log.d(TAG, "一句话识别:${pcmFloat.size} 采样点,${pcmFloat.size / 16000.0}s,${pcmBytes.size} bytes RMS=${"%.4f".format(rms)} maxAmp=${"%.4f".format(maxAmp)}")
+ if (maxAmp < 0.01f) {
+ Log.w(TAG, "⚠ 音频幅度极低(maxAmp=${"%.5f".format(maxAmp)}),模拟器麦克风可能没有采集到声音!请检查:模拟器扩展控制 → 麦克风 → 使用宿主机麦克风")
+ }
+
+ // 从服务器取时间,修正模拟器时钟偏差
+ val timestamp = fetchServerTimestamp()
+ val date = utcDate(timestamp)
+
+ val payload = buildPayload(appId, pcmBase64, pcmBytes.size)
+ val auth = buildAuthorization(sid, skey, date, timestamp, payload)
+
+ val request = Request.Builder()
+ .url("https://$HOST")
+ .addHeader("Authorization", auth)
+ .addHeader("Content-Type", "application/json; charset=utf-8")
+ .addHeader("Host", HOST)
+ .addHeader("X-TC-Action", ACTION)
+ .addHeader("X-TC-Version", VERSION)
+ .addHeader("X-TC-Timestamp", timestamp.toString())
+ .post(payload.toRequestBody("application/json; charset=utf-8".toMediaType()))
+ .build()
+
+ return try {
+ val response = client.newCall(request).execute()
+ val body = response.body?.string().orEmpty()
+ Log.d(TAG, "API 响应: ${body.take(400)}")
+ parseResult(body)
+ } catch (e: Exception) {
+ Log.e(TAG, "HTTP 请求失败: ${e.message}", e)
+ ""
+ }
+ }
+
+ // ─── 工具方法 ──────────────────────────────────────────────────────────
+
+ /**
+ * 向服务器发送 HEAD 请求,从 Date 响应头获取精确时间戳。
+ * 若请求失败则回退到设备时钟(可能有偏差)。
+ */
+ private fun fetchServerTimestamp(): Long {
+ return try {
+ val req = Request.Builder().url("https://$HOST").head().build()
+ val resp = client.newCall(req).execute()
+ val dateHeader = resp.header("Date")
+ resp.close()
+ if (dateHeader != null) {
+ val sdf = SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.ENGLISH)
+ val serverTs = sdf.parse(dateHeader)?.time?.div(1000) ?: deviceTimestamp()
+ val deviceTs = deviceTimestamp()
+ val offset = serverTs - deviceTs
+ if (kotlin.math.abs(offset) > 60) {
+ Log.w(TAG, "设备时钟偏差 ${offset}s,使用服务器时间修正(设备=${deviceTs}, 服务器=${serverTs})")
+ }
+ serverTs
+ } else {
+ deviceTimestamp()
+ }
+ } catch (e: Exception) {
+ Log.w(TAG, "获取服务器时间失败: ${e.message},使用设备时间")
+ deviceTimestamp()
+ }
+ }
+
+ private fun deviceTimestamp() = System.currentTimeMillis() / 1000
+
+ private fun utcDate(timestamp: Long): String {
+ val sdf = SimpleDateFormat("yyyy-MM-dd", Locale.US)
+ sdf.timeZone = TimeZone.getTimeZone("UTC")
+ return sdf.format(Date(timestamp * 1000))
+ }
+
+ private fun buildPayload(appId: String, base64: String, dataLen: Int): String =
+ JSONObject().apply {
+ put("ProjectId", 0)
+ put("SubServiceType", 2)
+ put("EngSerViceType", "16k_zh")
+ put("SourceType", 1) // 1 = 数据流
+ put("VoiceFormat", "pcm")
+ put("UsrAudioKey", "digital-person-asr")
+ put("FilterDirty", 0)
+ put("FilterModal", 0)
+ put("FilterPunc", 0)
+ put("ConvertNumMode", 1)
+ put("Data", base64)
+ put("DataLen", dataLen)
+ }.toString()
+
+ // ─── TC3-HMAC-SHA256 签名 ──────────────────────────────────────────────
+
+ private fun buildAuthorization(
+ secretId: String,
+ secretKey: String,
+ date: String,
+ timestamp: Long,
+ payload: String,
+ ): String {
+ val payloadHash = sha256Hex(payload)
+ val canonicalRequest = listOf(
+ "POST", "/", "",
+ "content-type:application/json; charset=utf-8",
+ "host:$HOST",
+ "",
+ "content-type;host",
+ payloadHash,
+ ).joinToString("\n")
+
+ val credentialScope = "$date/asr/tc3_request"
+ val stringToSign = "TC3-HMAC-SHA256\n$timestamp\n$credentialScope\n${sha256Hex(canonicalRequest)}"
+
+ val signingKey = hmacSha256(
+ hmacSha256(hmacSha256("TC3$secretKey".toByteArray(), date), "asr"),
+ "tc3_request",
+ )
+ val signature = hmacSha256(signingKey, stringToSign).joinToString("") { "%02x".format(it) }
+
+ return "TC3-HMAC-SHA256 Credential=$secretId/$credentialScope, SignedHeaders=content-type;host, Signature=$signature"
+ }
+
+ private fun parseResult(json: String): String {
+ if (json.isBlank()) return ""
+ return try {
+ val response = JSONObject(json).optJSONObject("Response") ?: return ""
+ val error = response.optJSONObject("Error")
+ if (error != null) {
+ Log.e(TAG, "API 错误: ${error.optString("Code")} - ${error.optString("Message")}")
+ return ""
+ }
+ response.optString("Result").also { text ->
+ if (text.isNotBlank()) Log.d(TAG, "识别结果: \"$text\"")
+ }
+ } catch (e: Exception) {
+ Log.w(TAG, "解析响应失败: ${json.take(300)}")
+ ""
+ }
+ }
+
+ private fun sha256Hex(data: String): String {
+ val md = MessageDigest.getInstance("SHA-256")
+ return md.digest(data.toByteArray(Charsets.UTF_8)).joinToString("") { "%02x".format(it) }
+ }
+
+ private fun hmacSha256(key: ByteArray, data: String): ByteArray {
+ val mac = Mac.getInstance("HmacSHA256")
+ mac.init(SecretKeySpec(key, "HmacSHA256"))
+ return mac.doFinal(data.toByteArray(Charsets.UTF_8))
+ }
+
+ private fun floatToPcm16Bytes(samples: FloatArray): ByteArray {
+ val buf = ByteBuffer.allocate(samples.size * 2).order(ByteOrder.LITTLE_ENDIAN)
+ samples.forEach { buf.putShort((it.coerceIn(-1f, 1f) * 32767f).toInt().toShort()) }
+ return buf.array()
+ }
+}
diff --git a/app/src/main/java/com/digitalperson/config/AppConfig.kt b/app/src/main/java/com/digitalperson/config/AppConfig.kt
index ac1dc41..ced450e 100644
--- a/app/src/main/java/com/digitalperson/config/AppConfig.kt
+++ b/app/src/main/java/com/digitalperson/config/AppConfig.kt
@@ -116,6 +116,19 @@ object AppConfig {
const val MODEL_FILE = "bge-small-zh-v1.5.rknn"
}
+ /**
+ * 模拟器上 [RefImageMatcher] 使用编辑距离时的最低归一化分(与 BGE 余弦阈值不可混用)。
+ * 分数 = 1 - Levenshtein / max(len),越接近 1 越像。
+ */
+ object RefMatchEmulator {
+ /**
+ * 模拟器混合评分(路径关键词命中率 + 编辑距离)阈值。
+ * 路径关键词:1 个词命中 ≈ 0.25,已足够确认话题相关性。
+ * 原 0.82 是纯编辑距离阈值,字面差异大时根本达不到,故降至 0.20。
+ */
+ const val MIN_NORMALIZED_EDIT_SCORE = 0.20f
+ }
+
/**
* app/note/ref 通过 Gradle 额外 assets 目录打入 apk 后,在 assets 中的根路径为 `ref/`。
*/
diff --git a/app/src/main/java/com/digitalperson/embedding/EditDistanceSimilarity.kt b/app/src/main/java/com/digitalperson/embedding/EditDistanceSimilarity.kt
new file mode 100644
index 0000000..eada13e
--- /dev/null
+++ b/app/src/main/java/com/digitalperson/embedding/EditDistanceSimilarity.kt
@@ -0,0 +1,49 @@
+package com.digitalperson.embedding
+
+import kotlin.math.max
+import kotlin.math.min
+
+/**
+ * 基于 Levenshtein 的字符级相似度(模拟器兜底,无语义,仅用于联调/演示)。
+ *
+ * 分数:1 - dist / max(len1, len2),与余弦相似度不可直接对比阈值。
+ */
+object EditDistanceSimilarity {
+
+ fun normalizedScore(a: String, b: String): Float {
+ val s1 = a.trim()
+ val s2 = b.trim()
+ if (s1.isEmpty() && s2.isEmpty()) return 1f
+ if (s1.isEmpty() || s2.isEmpty()) return 0f
+ val dist = levenshtein(s1, s2)
+ val denom = max(s1.length, s2.length).coerceAtLeast(1)
+ return 1f - dist.toFloat() / denom.toFloat()
+ }
+
+ /**
+ * 经典双行 DP,O(n·m);仅适用于模拟器上中等规模语料。
+ */
+ fun levenshtein(s1: String, s2: String): Int {
+ val n = s1.length
+ val m = s2.length
+ if (n == 0) return m
+ if (m == 0) return n
+ var prev = IntArray(m + 1) { it }
+ var curr = IntArray(m + 1)
+ for (i in 1..n) {
+ curr[0] = i
+ val c1 = s1[i - 1]
+ for (j in 1..m) {
+ val cost = if (c1 == s2[j - 1]) 0 else 1
+ curr[j] = min(
+ min(prev[j] + 1, curr[j - 1] + 1),
+ prev[j - 1] + cost
+ )
+ }
+ val tmp = prev
+ prev = curr
+ curr = tmp
+ }
+ return prev[m]
+ }
+}
diff --git a/app/src/main/java/com/digitalperson/embedding/RefEmbeddingIndexer.kt b/app/src/main/java/com/digitalperson/embedding/RefEmbeddingIndexer.kt
index 3dd6b53..ceac3b9 100644
--- a/app/src/main/java/com/digitalperson/embedding/RefEmbeddingIndexer.kt
+++ b/app/src/main/java/com/digitalperson/embedding/RefEmbeddingIndexer.kt
@@ -4,6 +4,7 @@ import android.content.Context
import android.util.Log
import com.digitalperson.config.AppConfig
import com.digitalperson.data.AppDatabase
+import com.digitalperson.data.dao.QuestionDao
import com.digitalperson.data.entity.Question
import com.digitalperson.data.entity.RefTextEmbedding
import com.digitalperson.data.util.floatArrayToEmbeddingBytes
@@ -27,15 +28,15 @@ object RefEmbeddingIndexer {
val dao = db.refTextEmbeddingDao()
val questionDao = db.questionDao()
- if (!BgeEmbedding.initialize(app)) {
- Log.e(TAG, "[RefEmbed] BGE 初始化失败,跳过 ref 语料索引")
- return@withContext
- }
-
val root = AppConfig.RefCorpus.ASSETS_ROOT
val paths = RefCorpusAssetScanner.listTxtFilesUnder(app, root)
Log.i(TAG, "[RefEmbed] 发现 ${paths.size} 个 txt(root=$root)")
+ val bgeOk = BgeEmbedding.initialize(app)
+ if (!bgeOk) {
+ Log.w(TAG, "[RefEmbed] BGE 未就绪(常见于模拟器),仅扫描题库;ref 配图匹配可用编辑距离")
+ }
+
var skipped = 0
var embedded = 0
var empty = 0
@@ -50,28 +51,9 @@ object RefEmbeddingIndexer {
continue
}
- // 题库:遇到包含 ?/? 的行,写入 questions
- val subject = extractSubjectFromRaw(raw)
- val grade = extractGradeFromPath(path)
- val questionLines = extractQuestionLines(raw)
- for (line in questionLines) {
- val content = line.trim()
- if (content.isEmpty()) continue
- val exists = questionDao.findByContentSubjectGrade(content, subject, grade)
- if (exists == null) {
- questionDao.insert(
- Question(
- id = 0,
- content = content,
- answer = null,
- subject = subject,
- grade = grade,
- difficulty = 1,
- createdAt = System.currentTimeMillis()
- )
- )
- }
- }
+ ingestQuestionsFromRaw(raw, path, questionDao)
+
+ if (!bgeOk) continue
val embedText = RefTxtEmbedText.fromRawFileContent(raw)
if (embedText.isEmpty()) {
@@ -110,10 +92,34 @@ object RefEmbeddingIndexer {
Log.i(
TAG,
- "[RefEmbed] 完成 embedded=$embedded skipped=$skipped empty=$empty failed=$failed cacheSize=${RefEmbeddingMemoryCache.size()}"
+ "[RefEmbed] 完成 embedded=$embedded skipped=$skipped empty=$empty failed=$failed cacheSize=${RefEmbeddingMemoryCache.size()} bgeOk=$bgeOk"
)
}
+ private fun ingestQuestionsFromRaw(raw: String, path: String, questionDao: QuestionDao) {
+ val subject = extractSubjectFromRaw(raw)
+ val grade = extractGradeFromPath(path)
+ val questionLines = extractQuestionLines(raw)
+ for (line in questionLines) {
+ val content = line.trim()
+ if (content.isEmpty()) continue
+ val exists = questionDao.findByContentSubjectGrade(content, subject, grade)
+ if (exists == null) {
+ questionDao.insert(
+ Question(
+ id = 0,
+ content = content,
+ answer = null,
+ subject = subject,
+ grade = grade,
+ difficulty = 1,
+ createdAt = System.currentTimeMillis()
+ )
+ )
+ }
+ }
+ }
+
private fun extractSubjectFromRaw(raw: String): String? {
val line = raw.lineSequence()
.map { it.trimEnd() }
diff --git a/app/src/main/java/com/digitalperson/embedding/RefImageMatcher.kt b/app/src/main/java/com/digitalperson/embedding/RefImageMatcher.kt
index ebcf4dc..ac6da87 100644
--- a/app/src/main/java/com/digitalperson/embedding/RefImageMatcher.kt
+++ b/app/src/main/java/com/digitalperson/embedding/RefImageMatcher.kt
@@ -3,6 +3,7 @@ package com.digitalperson.embedding
import android.content.Context
import android.util.Log
import com.digitalperson.config.AppConfig
+import com.digitalperson.env.RuntimeEnv
import kotlin.math.sqrt
data class RefImageMatch(
@@ -16,7 +17,8 @@ object RefImageMatcher {
private const val TAG = AppConfig.TAG
/**
- * @param threshold 余弦相似度阈值(向量已归一化时等价于 dot product)。
+ * @param threshold 真机 BGE:余弦相似度阈值(向量已归一化时等价于 dot product)。
+ * 模拟器:忽略该参数,使用 [AppConfig.RefMatchEmulator.MIN_NORMALIZED_EDIT_SCORE](编辑距离归一化分)。
*/
fun findBestMatch(
context: Context,
@@ -26,6 +28,10 @@ object RefImageMatcher {
val query = text.trim()
if (query.isEmpty()) return null
+ if (RuntimeEnv.isEmulator()) {
+ return findBestMatchEditDistance(context, query)
+ }
+
if (!BgeEmbedding.isReady()) {
val ok = BgeEmbedding.initialize(context.applicationContext)
if (!ok) {
@@ -78,6 +84,203 @@ object RefImageMatcher {
)
}
+ /**
+ * 模拟器:不加载 BGE,用**路径关键词命中率**(主)+ 编辑距离(辅)混合评分。
+ *
+ * 路径关键词:取最深目录名按 "-" 分割,如 "一年级上-生活适应-社会生活-元旦"
+ * → ["一年级上", "生活适应", "社会生活", "元旦"]。
+ * 命中率 = 命中数 / 关键词总数(1 个词命中 ≈ 0.25,足以通过 0.20 阈值)。
+ *
+ * 纯编辑距离原先用 0.82 阈值,但 LLM 回复文本与参考短句字面差异很大,
+ * 即使话题相同也难达标;改为关键词方案后准确率大幅提升。
+ */
+ /** 模拟器混合匹配逻辑;对 [androidTest] 暴露以便回归「本应命中却未命中」的用例。 */
+ internal fun findBestMatchEditDistance(context: Context, query: String): RefImageMatch? {
+ val app = context.applicationContext
+ val root = AppConfig.RefCorpus.ASSETS_ROOT
+ val paths = RefCorpusAssetScanner.listTxtFilesUnder(app, root)
+ val minScore = AppConfig.RefMatchEmulator.MIN_NORMALIZED_EDIT_SCORE
+ val qNorm = normalizeTextForEmuMatch(query.trim())
+ if (qNorm.isEmpty()) return null
+
+ var bestPath: String? = null
+ var bestScore = -1f
+ var bestSubstr = -1f
+ var bestEdit = -1f
+
+ for (path in paths) {
+ // 主:路径关键词命中率(无 IO,O(1))
+ val kwScore = pathKeywordMatchScore(path, qNorm)
+
+ // 辅:内容匹配(有 IO,仅在关键词有命中或尚无候选时读取)
+ // 策略:① 子串包含(query 句子 ⊆ candidate 或 candidate 句子 ⊆ query 句子)
+ // ② 逐句编辑距离
+ // candidate 是 txt 去掉 # 行后的全文(可能同时含问题和答案),
+ // query 中某句若直接出现在 candidate 里,说明话题完全命中。
+ // 必须对每条 txt 做内容打分:若仅在 bestScore<0 或 kwScore>0 时才读盘,
+ // 会先被其它文件的弱编辑分「占坑」,导致题干与某文件完全一致却从未被打开(如「上厕所」目录 kw 未命中但正文含原句)。
+ var substrScore = 0f
+ var editScore = 0f
+ try {
+ val raw = app.assets.open(path).bufferedReader(Charsets.UTF_8).use { it.readText() }
+ val candidate = normalizeTextForEmuMatch(RefTxtEmbedText.fromRawFileContent(raw))
+ if (candidate.isNotEmpty()) {
+ val querySentences = splitSentences(qNorm)
+ val candidateSentences = splitSentences(candidate)
+ // ① 子串:query 句 ⊆ candidate,或 candidate 句 ⊆ query 句。
+ // 极短片段(如「小朋友」)在多篇课文里都有,一律给高分会错配;按匹配长度分级。
+ substrScore = querySentences.maxOfOrNull { qs ->
+ var s = 0f
+ if (qs.length >= 4 && candidate.contains(qs)) {
+ s = maxOf(s, emulatorSubstringScoreForLength(qs.length))
+ }
+ for (cs in candidateSentences) {
+ if (cs.length >= 6 && qs.contains(cs)) {
+ s = maxOf(s, emulatorSubstringScoreForLength(cs.length) * 0.92f)
+ }
+ }
+ s
+ } ?: 0f
+ // ② 编辑距离(逐句 vs 逐句,取最高分)
+ editScore = querySentences.maxOfOrNull { qs ->
+ candidateSentences.maxOfOrNull { cs ->
+ EditDistanceSimilarity.normalizedScore(qs, cs)
+ } ?: 0f
+ } ?: 0f
+ }
+ } catch (e: Exception) {
+ Log.w(TAG, "[RefMatchEmu] read fail $path: ${e.message}")
+ }
+
+ val score = maxOf(kwScore, substrScore, editScore)
+ if (score > 0f) {
+ Log.v(TAG, "[RefMatchEmu] candidate score=$score (kw=$kwScore substr=$substrScore edit=$editScore) path=$path")
+ }
+ if (isBetterEmulatorCandidate(score, substrScore, editScore, bestScore, bestSubstr, bestEdit)) {
+ bestScore = score
+ bestSubstr = substrScore
+ bestEdit = editScore
+ bestPath = path
+ }
+ }
+
+ val txtPath = bestPath ?: run {
+ Log.d(TAG, "[RefMatchEmu] 无候选文件 query=${qNorm.take(60)}")
+ return null
+ }
+ if (bestScore < minScore) {
+ Log.d(TAG, "[RefMatchEmu] 分数不足 bestScore=$bestScore minScore=$minScore bestPath=$txtPath query=${qNorm.take(60)}")
+ return null
+ }
+
+ val pngPath = if (txtPath.endsWith(".txt", ignoreCase = true)) {
+ txtPath.dropLast(4) + ".png"
+ } else {
+ "$txtPath.png"
+ }
+ val exists = try {
+ context.assets.open(pngPath).close()
+ true
+ } catch (_: Throwable) {
+ false
+ }
+ if (!exists) return null
+
+ Log.d(TAG, "[RefMatchEmu] best=$txtPath score=$bestScore query=${qNorm.take(30)}")
+ return RefImageMatch(
+ txtAssetPath = txtPath,
+ pngAssetPath = pngPath,
+ score = bestScore
+ )
+ }
+
+ /**
+ * 从文件路径提取话题关键词,计算与查询文本的关键词命中率。
+ * 如路径含目录 "一年级上-生活适应-社会生活-元旦" → 关键词 ["一年级上","生活适应","社会生活","元旦"]。
+ */
+ private fun pathKeywordMatchScore(path: String, query: String): Float {
+ val keywords = extractPathTopicKeywords(path)
+ if (keywords.isEmpty()) return 0f
+ val matches = keywords.count { kw -> queryMatchesPathKeyword(query, kw) }
+ return matches.toFloat() / keywords.size
+ }
+
+ /** 统一全角/半角标点后再匹配,避免代码或 ASR 里半角 `:` 与语料全角 `:` 导致长句子串匹配失败。 */
+ private fun normalizeTextForEmuMatch(s: String): String = buildString(s.length) {
+ for (ch in s) {
+ append(
+ when (ch) {
+ '\uFF1A', '\uFE55', ':' -> ':'
+ '\uFF0C' -> ','
+ '\uFF01' -> '!'
+ '\uFF1F' -> '?'
+ '\uFF1B' -> ';'
+ else -> ch
+ },
+ )
+ }
+ }
+
+ /** 总分相同时优先子串分、再比编辑分,避免「元旦到了,小朋友」等前缀在多篇课文同分却先命中排序靠前者。 */
+ private fun isBetterEmulatorCandidate(
+ score: Float,
+ substr: Float,
+ edit: Float,
+ bestScore: Float,
+ bestSubstr: Float,
+ bestEdit: Float,
+ ): Boolean {
+ if (bestScore < 0f) return true
+ when {
+ score > bestScore + 1e-5f -> return true
+ score + 1e-5f < bestScore -> return false
+ substr > bestSubstr + 1e-5f -> return true
+ substr + 1e-5f < bestSubstr -> return false
+ else -> return edit > bestEdit + 1e-5f
+ }
+ }
+
+ /** 路径片段与 query 的包含关系;题干常省略词头(如目录「上厕所」、句子里只有「厕所」)。 */
+ private fun queryMatchesPathKeyword(query: String, kw: String): Boolean {
+ if (query.contains(kw)) return true
+ // 去掉首字再匹配,避免「个人生活」用 takeLast(2) 误匹配到泛泛的「生活」
+ if (kw.length >= 3) {
+ val rest = kw.substring(1)
+ if (rest.length >= 2 && query.contains(rest)) return true
+ }
+ return false
+ }
+
+ /** 子串命中得分:越长说明越具区分度;过短(如「小朋友」)分数低,减少跨课文误配。 */
+ private fun emulatorSubstringScoreForLength(len: Int): Float = when {
+ len >= 18 -> 0.95f
+ len >= 12 -> 0.90f
+ len >= 8 -> 0.82f
+ len >= 6 -> 0.68f
+ len >= 4 -> 0.48f
+ else -> 0f
+ }
+
+ /**
+ * 按中文/英文句子分隔符拆分,返回非空句子列表。
+ * 用于模拟器编辑距离辅助评分:逐句比对,避免 LLM 前导寒暄句拉低得分。
+ */
+ private fun splitSentences(text: String): List {
+ val parts = text.split(Regex("[,。!?;,!?;\n]+"))
+ .map { it.trim() }
+ .filter { it.length >= 2 }
+ return parts.ifEmpty { listOf(text) }
+ }
+
+ /** 取最深目录名,按 "-" 分割并过滤掉纯数字和单字符片段。 */
+ private fun extractPathTopicKeywords(path: String): List {
+ val deepestDir = path.split("/").dropLast(1).lastOrNull() ?: return emptyList()
+ return deepestDir.split("-")
+ .map { it.replace(Regex("\\d+"), "").trim() }
+ .filter { it.length >= 2 }
+ .distinct()
+ }
+
private fun dot(a: FloatArray, b: FloatArray): Float {
var s = 0f
for (i in a.indices) s += a[i] * b[i]
diff --git a/app/src/main/java/com/digitalperson/env/RuntimeEnv.kt b/app/src/main/java/com/digitalperson/env/RuntimeEnv.kt
new file mode 100644
index 0000000..f95b31d
--- /dev/null
+++ b/app/src/main/java/com/digitalperson/env/RuntimeEnv.kt
@@ -0,0 +1,34 @@
+package com.digitalperson.env
+
+import android.os.Build
+
+object RuntimeEnv {
+ fun isEmulator(): Boolean {
+ val fingerprint = Build.FINGERPRINT.orEmpty()
+ val model = Build.MODEL.orEmpty()
+ val brand = Build.BRAND.orEmpty()
+ val device = Build.DEVICE.orEmpty()
+ val product = Build.PRODUCT.orEmpty()
+ val hardware = Build.HARDWARE.orEmpty()
+ val manufacturer = Build.MANUFACTURER.orEmpty()
+
+ var hits = 0
+ fun hit(b: Boolean) { if (b) hits++ }
+
+ hit(fingerprint.startsWith("generic", ignoreCase = true))
+ hit(fingerprint.contains("unknown", ignoreCase = true))
+ hit(model.contains("google_sdk", ignoreCase = true))
+ hit(model.contains("emulator", ignoreCase = true))
+ hit(model.contains("android sdk built for", ignoreCase = true))
+ hit(manufacturer.contains("genymotion", ignoreCase = true))
+ hit(brand.startsWith("generic", ignoreCase = true) && device.startsWith("generic", ignoreCase = true))
+ hit(product.contains("sdk", ignoreCase = true))
+ hit(product.contains("emulator", ignoreCase = true))
+ hit(hardware.contains("goldfish", ignoreCase = true))
+ hit(hardware.contains("ranchu", ignoreCase = true))
+
+ // Require multiple signals to avoid false positives on weird ROMs.
+ return hits >= 2
+ }
+}
+
diff --git a/app/src/main/java/com/digitalperson/face/FaceDetectionPipeline.kt b/app/src/main/java/com/digitalperson/face/FaceDetectionPipeline.kt
index 2511c28..2b5fb71 100644
--- a/app/src/main/java/com/digitalperson/face/FaceDetectionPipeline.kt
+++ b/app/src/main/java/com/digitalperson/face/FaceDetectionPipeline.kt
@@ -5,6 +5,7 @@ import android.graphics.Bitmap
import android.util.Log
import com.digitalperson.config.AppConfig
import com.digitalperson.engine.RetinaFaceEngineRKNN
+import com.digitalperson.env.RuntimeEnv
import java.util.ArrayDeque
import java.util.concurrent.atomic.AtomicBoolean
import kotlinx.coroutines.CoroutineScope
@@ -35,6 +36,12 @@ class FaceDetectionPipeline(
private val onResult: (FaceDetectionResult) -> Unit,
private val onPresenceChanged: (present: Boolean, isFrontal: Boolean, faceIdentityId: String?, recognizedName: String?) -> Unit,
) {
+ companion object {
+ /** 模拟器固定人脸 ID,对应 UserMemory 中的 userId */
+ const val EMULATOR_FACE_ID = "face_emulator"
+ /** 模拟器固定显示名,直接作为 recognizedName 传给 coordinator */
+ const val EMULATOR_FACE_NAME = "小黑"
+ }
private val appContext = context.applicationContext
private val engine = RetinaFaceEngineRKNN()
private val recognizer = FaceRecognizer(appContext)
@@ -50,6 +57,11 @@ class FaceDetectionPipeline(
private val fusionQualities = ArrayDeque()
fun initialize(): Boolean {
+ if (RuntimeEnv.isEmulator()) {
+ Log.i(AppConfig.TAG, "[Face] 模拟器模式:跳过 RKNN 初始化,固定返回身份「$EMULATOR_FACE_NAME」")
+ initialized.set(true)
+ return true
+ }
val detectorOk = engine.initialize(appContext)
val recognizerOk = recognizer.initialize()
val ok = detectorOk && recognizerOk
@@ -68,6 +80,31 @@ class FaceDetectionPipeline(
return
}
+ // 模拟器:跳过 RKNN 检测,固定上报一张居中正脸
+ if (RuntimeEnv.isEmulator()) {
+ scope.launch {
+ try {
+ val w = bitmap.width
+ val h = bitmap.height
+ val fakeBox = FaceBox(
+ left = w * 0.25f,
+ top = h * 0.15f,
+ right = w * 0.75f,
+ bottom = h * 0.85f,
+ score = 0.99f,
+ )
+ withContext(Dispatchers.Main) {
+ onPresenceChanged(true, true, EMULATOR_FACE_ID, EMULATOR_FACE_NAME)
+ onResult(FaceDetectionResult(w, h, listOf(fakeBox)))
+ }
+ } finally {
+ bitmap.recycle()
+ frameInFlight.set(false)
+ }
+ }
+ return
+ }
+
scope.launch {
try {
val width = bitmap.width
diff --git a/app/src/main/java/com/digitalperson/interaction/BaseDigitalPersonCoordinator.kt b/app/src/main/java/com/digitalperson/interaction/BaseDigitalPersonCoordinator.kt
index a253928..897a3bb 100644
--- a/app/src/main/java/com/digitalperson/interaction/BaseDigitalPersonCoordinator.kt
+++ b/app/src/main/java/com/digitalperson/interaction/BaseDigitalPersonCoordinator.kt
@@ -148,6 +148,8 @@ abstract class BaseDigitalPersonCoordinator(
* (i.e. after a cloud LLM response), NOT after greeting / farewell / proactive TTS.
*/
fun onTtsPlaybackCompleted() {
+ // Let the controller advance its own timers (greeting/proactive/dlg all count as assistant speaking).
+ controller.onAssistantTtsPlaybackCompleted()
if (pendingDialogueFinish) {
pendingDialogueFinish = false
controller.onDialogueResponseFinished()
diff --git a/app/src/main/java/com/digitalperson/interaction/DigitalHumanInteractionController.kt b/app/src/main/java/com/digitalperson/interaction/DigitalHumanInteractionController.kt
index 27a495f..437f1db 100644
--- a/app/src/main/java/com/digitalperson/interaction/DigitalHumanInteractionController.kt
+++ b/app/src/main/java/com/digitalperson/interaction/DigitalHumanInteractionController.kt
@@ -64,6 +64,10 @@ class DigitalHumanInteractionController(
private var memoryJob: Job? = null
private var farewellJob: Job? = null
+ // 让超时/间隔从 TTS 播放完成后开始计时,而不是从 speak() 调用时开始
+ private var pendingWaitReplyTimeoutAfterTts: Boolean = false
+ private var pendingProactiveFollowupAfterTts: Boolean = false
+
fun start() {
transitionTo(InteractionState.IDLE)
scheduleMemoryMode()
@@ -204,7 +208,7 @@ fun onFacePresenceChanged(present: Boolean, isFrontal: Boolean = true) { // 添
return
}
transitionTo(InteractionState.WAITING_REPLY)
- scheduleWaitingReplyTimeout()
+ scheduleWaitingReplyTimeoutAfterTts()
}
private fun enterGreeting() {
@@ -224,7 +228,7 @@ fun onFacePresenceChanged(present: Boolean, isFrontal: Boolean = true) { // 添
handler.addToChatHistory("assistant", greeting)
handler.addAssistantMessageToCloudHistory(greeting)
transitionTo(InteractionState.WAITING_REPLY)
- scheduleWaitingReplyTimeout()
+ scheduleWaitingReplyTimeoutAfterTts()
} else {
useDefaultGreeting()
}
@@ -243,7 +247,11 @@ fun onFacePresenceChanged(present: Boolean, isFrontal: Boolean = true) { // 添
handler.addAssistantMessageToCloudHistory(greeting)
transitionTo(InteractionState.WAITING_REPLY)
- scheduleWaitingReplyTimeout()
+ scheduleWaitingReplyTimeoutAfterTts()
+ }
+
+ private fun scheduleWaitingReplyTimeoutAfterTts() {
+ pendingWaitReplyTimeoutAfterTts = true
}
private fun scheduleWaitingReplyTimeout() {
@@ -282,21 +290,34 @@ fun onFacePresenceChanged(present: Boolean, isFrontal: Boolean = true) { // 添
// 触发题目生成检查
handler.onQuestionAsked(currentFaceId ?: "guest")
- proactiveJob = scope.launch {
- hasPendingUserReply = false
- delay(20_000)
- if (state != InteractionState.PROACTIVE || hasPendingUserReply) return@launch
- if (!facePresent) {
- enterFarewell()
- return@launch
- }
- proactiveRound += 1
- if (proactiveRound < 3) {
- askProactiveTopic()
- } else {
- transitionTo(InteractionState.WAITING_REPLY)
-// handler.playMotion("haru_g_m17.motion3.json")
- scheduleWaitingReplyTimeout()
+ // 不立刻开始 20s 计时;等 TTS 播放完再开始计时,避免“刚说完几秒就又问”
+ pendingProactiveFollowupAfterTts = true
+ }
+
+ /** 由 Activity 在「本轮 TTS 完整播放完成」时调用(包括问候/主动提问/对话回复)。 */
+ fun onAssistantTtsPlaybackCompleted() {
+ if (pendingWaitReplyTimeoutAfterTts && state == InteractionState.WAITING_REPLY) {
+ pendingWaitReplyTimeoutAfterTts = false
+ scheduleWaitingReplyTimeout()
+ }
+ if (pendingProactiveFollowupAfterTts && state == InteractionState.PROACTIVE) {
+ pendingProactiveFollowupAfterTts = false
+ proactiveJob?.cancel()
+ proactiveJob = scope.launch {
+ hasPendingUserReply = false
+ delay(20_000)
+ if (state != InteractionState.PROACTIVE || hasPendingUserReply) return@launch
+ if (!facePresent) {
+ enterFarewell()
+ return@launch
+ }
+ proactiveRound += 1
+ if (proactiveRound < 3) {
+ askProactiveTopic()
+ } else {
+ transitionTo(InteractionState.WAITING_REPLY)
+ scheduleWaitingReplyTimeoutAfterTts()
+ }
}
}
}
diff --git a/app/src/main/java/com/digitalperson/tts/TtsController.kt b/app/src/main/java/com/digitalperson/tts/TtsController.kt
index 4696cd2..45c91a1 100644
--- a/app/src/main/java/com/digitalperson/tts/TtsController.kt
+++ b/app/src/main/java/com/digitalperson/tts/TtsController.kt
@@ -27,6 +27,11 @@ class TtsController(private val context: Context) {
private var callback: TtsCallback? = null
+ // 防止 WebSocket 重连或多路回调导致同一段文案短时间内重复入队、重复播报
+ @Volatile private var lastEnqueuedText: String? = null
+ @Volatile private var lastEnqueuedAtMs: Long = 0L
+ private val dedupeWindowMs = 2500L
+
fun setCallback(callback: TtsCallback) {
this.callback = callback
bindCallbacksIfReady()
@@ -147,6 +152,14 @@ class TtsController(private val context: Context) {
fun enqueueSegment(seg: String) {
val cleaned = seg.replace(Regex("\\[.*?\\]"), "").trim()
if (cleaned.isEmpty()) return
+ val now = System.currentTimeMillis()
+ val lastText = lastEnqueuedText
+ if (lastText != null && lastText == cleaned && (now - lastEnqueuedAtMs) <= dedupeWindowMs) {
+ Log.w(TAG, "Skip duplicate TTS segment within ${dedupeWindowMs}ms: ${cleaned.take(60)}")
+ return
+ }
+ lastEnqueuedText = cleaned
+ lastEnqueuedAtMs = now
if (useQCloudTts) {
qcloudTts?.enqueueSegment(cleaned)
} else {