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 {