修改部分疏漏 #29

Merged
p82feo7wg merged 1 commits from wangjiaqi_branch into master 2 months ago

@ -225,7 +225,9 @@
android:background="@drawable/bg_btn_set_color" android:background="@drawable/bg_btn_set_color"
android:backgroundTint="@null" android:backgroundTint="@null"
android:focusable="false" android:focusable="false"
android:clickable="true"/> android:clickable="true"
android:src="@android:drawable/ic_btn_speak_now"
android:scaleType="center"/>
<!-- 分隔线 --> <!-- 分隔线 -->
<View <View

@ -150,5 +150,18 @@
<plurals name="search_results_title"> <plurals name="search_results_title">
<item quantity="other"><xliff:g id="NUMBER">%1$s</xliff:g> 条符合“<xliff:g id="SEARCH">%2$s</xliff:g>”的搜索结果</item> <item quantity="other"><xliff:g id="NUMBER">%1$s</xliff:g> 条符合“<xliff:g id="SEARCH">%2$s</xliff:g>”的搜索结果</item>
</plurals> </plurals>
<!-- Voice to text -->
<string name="voice_recognition_ready">请开始说话...</string>
<string name="voice_recognition_not_available">语音识别不可用</string>
<string name="voice_recognition_prompt">请说出您要输入的内容</string>
<string name="voice_recognition_error_audio">音频错误</string>
<string name="voice_recognition_error_client">客户端错误</string>
<string name="voice_recognition_error_permissions">权限不足</string>
<string name="voice_recognition_error_network">网络错误</string>
<string name="voice_recognition_error_network_timeout">网络超时</string>
<string name="voice_recognition_error_no_match">未识别到语音</string>
<string name="voice_recognition_error_busy">识别器忙</string>
<string name="voice_recognition_error_server">服务器错误</string>
<string name="voice_recognition_error_timeout">语音超时</string>
<string name="voice_recognition_error_unknown">未知错误</string>
</resources> </resources>

@ -154,7 +154,7 @@
<!-- Case of 0 or 2 or more results. --> <!-- Case of 0 or 2 or more results. -->
<item quantity="other"><xliff:g id="number" example="15">%1$s</xliff:g> results for "<xliff:g id="search" example="???">%2$s</xliff:g>"</item> <item quantity="other"><xliff:g id="number" example="15">%1$s</xliff:g> results for "<xliff:g id="search" example="???">%2$s</xliff:g>"</item>
</plurals> </plurals>
<!-- Voice to text --> <!-- Voice to text -->
<string name="voice_recognition_ready">请开始说话...</string> <string name="voice_recognition_ready">请开始说话...</string>
<string name="voice_recognition_not_available">语音识别不可用</string> <string name="voice_recognition_not_available">语音识别不可用</string>

@ -49,7 +49,7 @@
</style> </style>
<style name="HighlightTextAppearancePrimary"> <style name="HighlightTextAppearancePrimary">
<item name="android:textSize">@dimen/text_font_size_normal</item> <item name="android:textSize">@dimen/text_font_size_normal</item>
<item name="android:textColor">@color/primary_text_dark</item> <item name="android:textColor">@color/primary_text_dark</item>
</style> </style>

@ -0,0 +1,7 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Base.Theme.Notesmaster" parent="Theme.Material3.DayNight.NoActionBar">
<!-- Customize your dark theme here. -->
<!-- <item name="colorPrimary">@color/my_dark_primary</item> -->
</style>
</resources>

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<resources>
<style name="NoteTheme" parent="@android:style/Theme.Material.Light">
<item name="android:actionBarStyle">@android:style/Widget.Material.Light.ActionBar</item>
<item name="android:displayOptions">showHome|showTitle|useLogo</item>
<item name="android:windowActionBar">true</item>
<item name="android:windowNoTitle">false</item>
<item name="android:windowSoftInputMode">adjustResize</item>
<item name="android:windowContentOverlay">@null</item>
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:windowTranslucentStatus">true</item>
<item name="android:windowTranslucentNavigation">true</item>
</style>
</resources>

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<resources>
<string-array name="menu_share_ways">
<item>短信</item>
<item>邮件</item>
</string-array>
</resources>

@ -0,0 +1,154 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name">小米便签</string>
<string name="app_widget2x2">便签2x2</string>
<string name="app_widget4x4">便签4x4</string>
<string name="widget_havenot_content">没有关联内容,点击新建便签。</string>
<string name="widget_under_visit_mode">访客模式下,便签内容不可见</string>
<string name="notelist_string_info">...</string>
<string name="notelist_menu_new">新建便签</string>
<string name="delete_remind_time_message">成功删除提醒</string>
<string name="set_remind_time_message">创建提醒</string>
<string name="note_alert_expired">已过期</string>
<string name="format_date_ymd">yyyyMMdd</string>
<string name="format_datetime_mdhm">MM月dd日 kk:mm</string>
<string name="notealert_ok">知道了</string>
<string name="notealert_enter">查看</string>
<string name="note_link_tel">呼叫电话</string>
<string name="note_link_email">发送邮件</string>
<string name="note_link_web">浏览网页</string>
<string name="note_link_other">打开地图</string>
<!-- Text export file information -->
<string name="file_path">/MIUI/notes/</string>
<string name="file_name_txt_format">notes_%s.txt</string>
<!-- note list string -->
<string name="format_folder_files_count">(%d)</string>
<string name="menu_create_folder">新建文件夹</string>
<string name="menu_export_text">导出文本</string>
<string name="menu_sync">同步</string>
<string name="menu_sync_cancel">取消同步</string>
<string name="menu_setting">设置</string>
<string name="menu_search">搜索</string>
<string name="menu_trash">回收站</string>
<string name="button_return">返回</string>
<string name="menu_delete">删除</string>
<string name="button_clean_all">清空</string>
<string name="button_recovery">恢复</string>
<string name="menu_success">成功</string>
<string name="menu_move">移动到文件夹</string>
<string name="menu_select_title">选中了 %d 项</string>
<string name="menu_select_none">没有选中项,操作无效</string>
<string name="menu_select_all">全选</string>
<string name="menu_deselect_all">取消全选</string>
<string name="menu_font_size">文字大小</string>
<string name="menu_font_small"></string>
<string name="menu_font_normal">正常</string>
<string name="menu_font_large"></string>
<string name="menu_font_super">超大</string>
<string name="menu_list_mode">进入清单模式</string>
<string name="menu_normal_mode">退出清单模式</string>
<string name="menu_folder_view">查看文件夹</string>
<string name="menu_folder_delete">刪除文件夹</string>
<string name="menu_folder_change_name">修改文件夹名称</string>
<string name="folder_exist">文件夹 %1$s 已存在,请重新命名</string>
<string name="menu_share">分享</string>
<string name="menu_send_to_desktop">发送到桌面</string>
<string name="menu_alert">提醒我</string>
<string name="menu_remove_remind">删除提醒</string>
<string name="menu_title_select_folder">选择文件夹</string>
<string name="menu_move_parent_folder">上一级文件夹</string>
<string name="info_note_enter_desktop">已添加到桌面</string>
<string name="info_image_inserted">图片插入成功</string> <!-- OMO -->
<string name="alert_title_delete">删除</string>
<string name="alert_message_delete_notes">确认要删除所选的 %d 条便签吗?</string>
<string name="alert_message_delete_note">确认要删除该条便签吗?</string>
<string name="alert_message_delete_folder">确认删除文件夹及所包含的便签吗?</string>
<string name="alert_message_clear_all_trash">确认清空回收站中的所有便签吗?此操作无法撤销。</string>
<string name="format_move_notes_to_folder">已将所选 %1$d 条便签移到 %2$s 文件夹</string>
<!-- export text -->
<string name="error_sdcard_unmounted">SD卡被占用不能操作</string>
<string name="error_sdcard_export">导出文本时发生错误请检查SD卡</string>
<string name="error_note_not_exist">要查看的便签不存在</string>
<string name="error_note_empty_for_clock">不能为空便签设置闹钟提醒</string>
<string name="error_note_empty_for_send_to_desktop">不能将空便签发送到桌面</string>
<string name="error_permission_denied">权限被拒绝,无法访问图片</string>
<string name="error_image_insert_failed">插入图片失败,请重试</string>
<string name="success_sdcard_export">导出成功</string>
<string name="failed_sdcard_export">导出失败</string>
<string name="format_exported_file_location">已将文本文件(%1$s)输出至SD卡(%2$s)目录</string>
<!-- Sync -->
<string name="ticker_syncing">同步便签...</string>
<string name="ticker_success">同步成功</string>
<string name="ticker_fail">同步失败</string>
<string name="ticker_cancel">同步已取消</string>
<string name="success_sync_account">与%1$s同步成功</string>
<string name="error_sync_network">同步失败,请检查网络和帐号设置</string>
<string name="error_sync_internal">同步失败,发生内部错误</string>
<string name="error_sync_cancelled">同步已取消</string>
<string name="sync_progress_login">登录%1$s...</string>
<string name="sync_progress_init_list">正在获取服务器便签列表...</string>
<string name="sync_progress_syncing">正在同步本地便签...</string>
<!-- Preferences -->
<string name="preferences_title">设置</string>
<string name="preferences_account_title">同步账号</string>
<string name="preferences_account_summary">与google task同步便签记录</string>
<string name="preferences_last_sync_time">上次同步于 %1$s</string>
<string name="preferences_add_account">添加账号</string>
<string name="preferences_menu_change_account">更换账号</string>
<string name="preferences_menu_remove_account">删除账号</string>
<string name="preferences_menu_cancel">取消</string>
<string name="preferences_button_sync_immediately">立即同步</string>
<string name="preferences_button_sync_cancel">取消同步</string>
<string name="preferences_dialog_change_account_title">当前帐号 %1$s</string>
<string name="preferences_dialog_change_account_warn_msg">如更换同步帐号,过去的帐号同步信息将被清空,再次切换的同时可能会造成数据重复</string>
<string name="preferences_dialog_select_account_title">同步便签</string>
<string name="preferences_dialog_select_account_tips">请选择google帐号便签将与该帐号的google task内容同步。</string>
<string name="preferences_toast_cannot_change_account">正在同步中,不能修改同步帐号</string>
<string name="preferences_toast_success_set_accout">同步帐号已设置为%1$s</string>
<string name="preferences_bg_random_appear_title">新建便签背景颜色随机</string>
<string name="preferences_last_sync_time_format">yyyy-MM-dd hh:mm:ss</string>
<string name="button_delete">删除</string>
<string name="formatting_bold">粗体</string>
<string name="formatting_italic">斜体</string>
<string name="formatting_underline">下划线</string>
<string name="call_record_folder_name">通话便签</string>
<string name="hint_foler_name">请输入名称</string>
<string name="search_label">正在搜索便签</string>
<string name="search_hint">搜索便签</string>
<string name="search_setting_description">便签中的文字</string>
<string name="search">便签</string>
<string name="datetime_dialog_ok">设置</string>
<string name="datetime_dialog_cancel">取消</string>
<!-- Password protection -->
<string name="password_input_title">请输入密码</string>
<string name="password_hint">密码</string>
<string name="return_button">返回</string>
<string name="confirm_button">确认</string>
<string name="encrypt_note_dialog_title">加密便签</string>
<string name="encrypt_note_dialog_message">是否将此便签设置为加密便签?</string>
<string name="password_set_dialog_title">设置密码</string>
<string name="password_set_dialog_message">请输入密码</string>
<string name="password_error">密码错误,请重试</string>
<string name="password_empty">密码不能为空</string>
<plurals name="search_results_title">
<item quantity="other"><xliff:g id="NUMBER">%1$s</xliff:g> 条符合“<xliff:g id="SEARCH">%2$s</xliff:g>”的搜索结果</item>
</plurals>
</resources>

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<resources>
<string-array name="menu_share_ways">
<item>短信</item>
<item>郵件</item>
</string-array>
</resources>

@ -0,0 +1,127 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name">便簽</string>
<string name="app_widget2x2">便簽2x2</string>
<string name="app_widget4x4">便簽4x4</string>
<string name="widget_havenot_content">沒有關聯內容,點擊新建便簽。</string>
<string name="widget_under_visit_mode">訪客模式下,便籤內容不可見</string>
<string name="notelist_string_info">...</string>
<string name="notelist_menu_new">新建便簽</string>
<string name="delete_remind_time_message">成功刪除提醒</string>
<string name="set_remind_time_message">創建提醒</string>
<string name="note_alert_expired">已過期</string>
<string name="format_date_ymd">yyyyMMdd</string>
<string name="format_datetime_mdhm">MM月dd日 kk:mm</string>
<string name="notealert_ok">知道了</string>
<string name="notealert_enter">查看</string>
<string name="note_link_tel">呼叫電話</string>
<string name="note_link_email">發送郵件</string>
<string name="note_link_web">浏覽網頁</string>
<string name="note_link_other">打開地圖</string>
<string name="format_move_notes_to_folder">已將所選 %1$d 便籤移到 %2$s 文件夾</string>
<!-- note list string -->
<string name="menu_create_folder">新建文件夾</string>
<string name="menu_export_text">導出文本</string>
<string name="menu_sync">同步</string>
<string name="menu_sync_cancel">取消同步</string>
<string name="menu_setting">設置</string>
<string name="menu_search">搜尋</string>
<string name="menu_delete">刪除</string>
<string name="menu_move">移動到文件夾</string>
<string name="menu_select_title">選中了 %d 項</string>
<string name="menu_select_none">沒有選中項,操作無效</string>
<string name="menu_select_all">全選</string>
<string name="menu_deselect_all">取消全選</string>
<string name="menu_font_size">文字大小</string>
<string name="menu_font_small"></string>
<string name="menu_font_normal">正常</string>
<string name="menu_font_large"></string>
<string name="menu_font_super">超大</string>
<string name="menu_list_mode">進入清單模式</string>
<string name="menu_normal_mode">退出清單模式</string>
<string name="menu_folder_view">查看文件夾</string>
<string name="menu_folder_delete">刪除文件夾</string>
<string name="menu_folder_change_name">修改文件夾名稱</string>
<string name="folder_exist">文件夾 %1$s 已存在,請重新命名</string>
<string name="menu_share">分享</string>
<string name="menu_send_to_desktop">發送到桌面</string>
<string name="menu_alert">提醒我</string>
<string name="menu_remove_remind">刪除提醒</string>
<string name="menu_title_select_folder">選擇文件夾</string>
<string name="menu_move_parent_folder">上一級文件夾</string>
<string name="info_note_enter_desktop">已添加到桌面</string>
<string name="alert_title_delete">刪除</string>
<string name="alert_message_delete_notes">确认要刪除所選的 %d 條便籤嗎?</string>
<string name="alert_message_delete_note">确认要删除該條便籤嗎?</string>
<string name="alert_message_delete_folder">確認刪除檔夾及所包含的便簽嗎?</string>
<string name="error_sdcard_unmounted">SD卡被佔用不能操作</string>
<string name="error_sdcard_export">導出TXT時發生錯誤請檢查SD卡</string>
<string name="error_note_not_exist">要查看的便籤不存在</string>
<string name="error_note_empty_for_clock">不能爲空便籤設置鬧鐘提醒</string>
<string name="error_note_empty_for_send_to_desktop">不能將空便籤發送到桌面</string>
<string name="success_sdcard_export">導出成功</string>
<string name="failed_sdcard_export">導出失敗</string>
<string name="format_exported_file_location">已將文本文件(%1$s)導出至SD(%2$s)目錄</string>
<!-- Sync -->
<string name="ticker_syncing">同步便簽...</string>
<string name="ticker_success">同步成功</string>
<string name="ticker_fail">同步失敗</string>
<string name="ticker_cancel">同步已取消</string>
<string name="success_sync_account">與%1$s同步成功</string>
<string name="error_sync_network">同步失敗,請檢查網絡和帳號設置</string>
<string name="error_sync_internal">同步失敗,發生內部錯誤</string>
<string name="error_sync_cancelled">同步已取消</string>
<string name="sync_progress_login">登陸%1$s...</string>
<string name="sync_progress_init_list">正在獲取服務器便籤列表...</string>
<string name="sync_progress_syncing">正在同步本地便籤...</string>
<!-- Preferences -->
<string name="preferences_title">設置</string>
<string name="preferences_account_title">同步賬號</string>
<string name="preferences_account_summary">与google task同步便簽記錄</string>
<string name="preferences_last_sync_time">上次同步于 %1$s</string>
<string name="preferences_add_account">添加賬號</string>
<string name="preferences_menu_change_account">更換賬號</string>
<string name="preferences_menu_remove_account">刪除賬號</string>
<string name="preferences_menu_cancel">取消</string>
<string name="preferences_button_sync_immediately">立即同步</string>
<string name="preferences_button_sync_cancel">取消同步</string>
<string name="preferences_dialog_change_account_title">當前帳號 %1$s</string>
<string name="preferences_dialog_change_account_warn_msg">如更換同步帳號,過去的帳號同步信息將被清空,再次切換的同時可能會造成數據重復</string>
<string name="preferences_dialog_select_account_title">同步便簽</string>
<string name="preferences_dialog_select_account_tips">請選擇google帳號便簽將與該帳號的google task內容同步。</string>
<string name="preferences_toast_cannot_change_account">正在同步中,不能修改同步帳號</string>
<string name="preferences_toast_success_set_accout">同步帳號已設置為%1$s</string>
<string name="preferences_bg_random_appear_title">新建便籤背景顏色隨機</string>
<string name="button_delete">刪除</string>
<string name="call_record_folder_name">通話便籤</string>
<string name="hint_foler_name">請輸入名稱</string>
<string name="search_label">正在搜索便籤</string>
<string name="search_hint">搜索便籤</string>
<string name="search_setting_description">便籤中的文字</string>
<string name="search">便籤</string>
<string name="datetime_dialog_ok">設置</string>
<string name="datetime_dialog_cancel">取消</string>
<plurals name="search_results_title">
<item quantity="other"><xliff:g id="NUMBER">%1$s</xliff:g> 條符合”<xliff:g id="SEARCH">%2$s</xliff:g>“的搜尋結果</item>
</plurals>
</resources>

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<resources>
<!-- Backup format -->
<string-array name="format_for_exported_note">
<item>-%s</item> <!-- format_folder_name -->
<item>--%s</item> <!-- format_folder_note_date -->
<item>--%s</item> <!-- format_note_date -->
<item>--%s</item> <!-- format_note_content -->
</string-array>
<string-array name="menu_share_ways">
<item>Messaging</item>
<item>Email</item>
</string-array>
</resources>

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<resources>
<color name="user_query_highlight">#335b5b5b</color>
</resources>

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<resources>
<dimen name="text_font_size_super">33sp</dimen>
<dimen name="text_font_size_large">26sp</dimen>
<dimen name="text_font_size_medium">20sp</dimen>
<dimen name="text_font_size_normal">17sp</dimen>
<dimen name="text_font_size_small">14sp</dimen>
</resources>

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- 定义退出搜索菜单项的ID -->
<item name="menu_exit_search" type="id" />
</resources>

@ -0,0 +1,173 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name">Notes</string>
<string name="app_widget2x2">Notes 2x2</string>
<string name="app_widget4x4">Notes 4x4</string>
<string name="widget_havenot_content">No associated note found, click to create associated note.</string>
<string name="widget_under_visit_mode">Privacy modecan not see note content</string>
<string name="notelist_string_info">...</string>
<string name="notelist_menu_new">Add note</string>
<string name="delete_remind_time_message">Delete reminder successfully</string>
<string name="set_remind_time_message">Set reminder</string>
<string name="note_alert_expired">Expired</string>
<string name="format_date_ymd">yyyyMMdd</string>
<string name="format_datetime_mdhm">MMMd kk:mm</string>
<string name="notealert_ok">Got it</string>
<string name="notealert_enter">Take a look</string>
<string name="note_link_tel">Call</string>
<string name="note_link_email">Send email</string>
<string name="note_link_web">Browse web</string>
<string name="note_link_other">Open map</string>
<!-- Text export file information -->
<string name="file_path">/MIUI/notes/</string>
<string name="file_name_txt_format">notes_%s.txt</string>
<!-- notes list string -->
<string name="format_folder_files_count">(%d)</string>
<string name="menu_create_folder">New Folder</string>
<string name="menu_export_text">Export text</string>
<string name="menu_sync">Sync</string>
<string name="menu_sync_cancel">Cancel syncing</string>
<string name="menu_setting">Settings</string>
<string name="menu_search">Search</string>
<string name="menu_delete">Delete</string>
<string name="menu_move">Move to folder</string>
<string name="menu_trash">Trash</string>
<string name="button_return">Return</string>
<string name="button_clean_all">Clean All</string>
<string name="button_recovery">Recovery</string>
<string name="menu_success">Success</string>
<string name="menu_select_title">%d selected</string>
<string name="menu_select_none">Nothing selected, the operation is invalid</string>
<string name="menu_select_all">Select all</string>
<string name="menu_deselect_all">Deselect all</string>
<string name="menu_font_size">Font size</string>
<string name="menu_font_small">Small</string>
<string name="menu_font_normal">Medium</string>
<string name="menu_font_large">Large</string>
<string name="menu_font_super">Super</string>
<string name="menu_list_mode">Enter check list</string>
<string name="menu_normal_mode">Leave check list</string>
<string name="menu_folder_view">View folder</string>
<string name="menu_folder_delete">Delete folder</string>
<string name="menu_folder_change_name">Change folder name</string>
<string name="folder_exist">The folder %1$s exist, please rename</string>
<string name="menu_share">Share</string>
<string name="menu_send_to_desktop">Send to home</string>
<string name="menu_alert">Remind me</string>
<string name="menu_remove_remind">Delete reminder</string>
<string name="menu_title_select_folder">Select folder</string>
<string name="menu_move_parent_folder">Parent folder</string>
<string name="info_note_enter_desktop">Note added to home</string>
<string name="info_image_inserted">Image inserted successfully</string> <!-- OMO -->
<string name="alert_message_delete_folder">Confirm to delete folder and its notes?</string>
<string name="alert_title_delete">Delete selected notes</string>
<string name="alert_message_clear_all_trash">Confirm to clear all notes from trash? This action cannot be undone.</string>
<string name="alert_message_delete_notes">Confirm to delete the selected %d notes?</string>
<string name="alert_message_delete_note">Confirm to delete this note?</string>
<string name="format_move_notes_to_folder">Have moved selected %1$d notes to %2$s folder</string>
<!-- Error information -->
<string name="error_sdcard_unmounted">SD card busy, not available now</string>
<string name="error_sdcard_export">Export failed, please check SD card</string>
<string name="error_note_not_exist">The note is not exist</string>
<string name="error_note_empty_for_clock">Sorry, can not set clock on empty note</string>
<string name="error_note_empty_for_send_to_desktop">Sorry, can not send and empty note to home</string>
<string name="error_permission_denied">Permission denied, cannot access images</string>
<string name="error_image_insert_failed">Failed to insert image, please try again</string>
<string name="success_sdcard_export">Export successful</string>
<string name="failed_sdcard_export">Export fail</string>
<string name="format_exported_file_location">Export text file (%1$s) to SD (%2$s) directory</string>
<!-- Sync -->
<string name="ticker_syncing">Syncing notes...</string>
<string name="ticker_success">Sync is successful</string>
<string name="ticker_fail">Sync is failed</string>
<string name="ticker_cancel">Sync is canceled</string>
<string name="success_sync_account">Sync is successful with account %1$s</string>
<string name="error_sync_network">Sync failed, please check network and account settings</string>
<string name="error_sync_internal">Sync failed, internal error occurs</string>
<string name="error_sync_cancelled">Sync is canceled</string>
<string name="sync_progress_login">Logging into %1$s...</string>
<string name="sync_progress_init_list">Getting remote note list...</string>
<string name="sync_progress_syncing">Synchronize local notes with Google Task...</string>
<!-- Preferences -->
<string name="preferences_title">Settings</string>
<string name="preferences_account_title">Sync account</string>
<string name="preferences_account_summary">Sync notes with google task</string>
<string name="preferences_last_sync_time">Last sync time %1$s</string>
<string name="preferences_last_sync_time_format">yyyy-MM-dd hh:mm:ss</string>
<string name="preferences_add_account">Add account</string>
<string name="preferences_menu_change_account">Change sync account</string>
<string name="preferences_menu_remove_account">Remove sync account</string>
<string name="preferences_menu_cancel">Cancel</string>
<string name="preferences_button_sync_immediately">Sync immediately</string>
<string name="preferences_button_sync_cancel">Cancel syncing</string>
<string name="preferences_dialog_change_account_title">Current account %1$s</string>
<string name="preferences_dialog_change_account_warn_msg">All sync related information will be deleted, which may result in duplicated items sometime</string>
<string name="preferences_dialog_select_account_title">Sync notes</string>
<string name="preferences_dialog_select_account_tips">Please select a google account. Local notes will be synced with google task.</string>
<string name="preferences_toast_cannot_change_account">Cannot change the account because sync is in progress</string>
<string name="preferences_toast_success_set_accout">%1$s has been set as the sync account</string>
<string name="preferences_bg_random_appear_title">New note background color random</string>
<string name="button_delete">Delete</string>
<string name="call_record_folder_name">Call notes</string>
<string name="hint_foler_name">Input name</string>
<string name="search_label">Searching Notes</string>
<string name="search_hint">Search notes</string>
<string name="search_setting_description">Text in your notes</string>
<string name="search">Notes</string>
<string name="datetime_dialog_ok">set</string>
<string name="datetime_dialog_cancel">cancel</string>
<!-- Password protection -->
<string name="password_input_title">Enter your password please</string>
<string name="password_hint">password</string>
<string name="return_button">Return</string>
<string name="confirm_button">Confirm</string>
<string name="encrypt_note_dialog_title">Encrypted note</string>
<string name="encrypt_note_dialog_message">Would you like to set the note as an encrypted note?</string>
<string name="password_set_dialog_title">Set password</string>
<string name="password_set_dialog_message">Enter password please</string>
<string name="password_error">Error, try again</string>
<string name="password_empty">The password can not be empty</string>
<string name="formatting_bold">Bold</string>
<string name="formatting_italic">Italic</string>
<string name="formatting_underline">Underline</string>
<plurals name="search_results_title">
<item quantity="one"><xliff:g id="number" example="1">%1$s</xliff:g> result for "<xliff:g id="search" example="???">%2$s</xliff:g>"</item>
<!-- Case of 0 or 2 or more results. -->
<item quantity="other"><xliff:g id="number" example="15">%1$s</xliff:g> results for "<xliff:g id="search" example="???">%2$s</xliff:g>"</item>
</plurals>
<!-- Voice to text -->
<string name="voice_recognition_ready">请开始说话...</string>
<string name="voice_recognition_not_available">语音识别不可用</string>
<string name="voice_recognition_prompt">请说出您要输入的内容</string>
<string name="voice_recognition_error_audio">音频错误</string>
<string name="voice_recognition_error_client">客户端错误</string>
<string name="voice_recognition_error_permissions">权限不足</string>
<string name="voice_recognition_error_network">网络错误</string>
<string name="voice_recognition_error_network_timeout">网络超时</string>
<string name="voice_recognition_error_no_match">未识别到语音</string>
<string name="voice_recognition_error_busy">识别器忙</string>
<string name="voice_recognition_error_server">服务器错误</string>
<string name="voice_recognition_error_timeout">语音超时</string>
<string name="voice_recognition_error_unknown">未知错误</string>
</resources>

@ -0,0 +1,74 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<resources>
<style name="TextAppearanceSuper">
<item name="android:textSize">@dimen/text_font_size_super</item>
<item name="android:textColorLink">#0000ff</item>
</style>
<style name="TextAppearanceLarge">
<item name="android:textSize">@dimen/text_font_size_large</item>
<item name="android:textColorLink">#0000ff</item>
</style>
<style name="TextAppearanceMedium">
<item name="android:textSize">@dimen/text_font_size_medium</item>
<item name="android:textColorLink">#0000ff</item>
</style>
<style name="TextAppearanceNormal">
<item name="android:textSize">@dimen/text_font_size_normal</item>
<item name="android:textColorLink">#0000ff</item>
</style>
<style name="TextAppearancePrimaryItem">
<item name="android:textSize">@dimen/text_font_size_normal</item>
<item name="android:textColor">@color/primary_text_dark</item>
</style>
<style name="TextAppearanceSecondaryItem">
<item name="android:textSize">@dimen/text_font_size_small</item>
<item name="android:textColor">@color/secondary_text_dark</item>
</style>
<style name="TextAppearanceUnderMenuIcon">
<item name="android:textSize">@dimen/text_font_size_normal</item>
<item name="android:textColor">@android:color/black</item>
</style>
<style name="HighlightTextAppearancePrimary">
<item name="android:textSize">@dimen/text_font_size_normal</item>
<item name="android:textColor">@color/primary_text_dark</item>
</style>
<style name="HighlightTextAppearanceSecondary">
<item name="android:textSize">@dimen/text_font_size_small</item>
<item name="android:textColor">@color/secondary_text_dark</item>
</style>
<style name="NoteTheme" parent="@android:style/Theme.Holo.Light">
<item name="android:actionBarStyle">@android:style/Widget.Holo.Light.ActionBar</item>
<item name="android:displayOptions">showHome|showTitle|useLogo</item>
<item name="android:windowActionBar">true</item>
<item name="android:windowNoTitle">false</item>
<item name="android:windowSoftInputMode">adjustResize</item>
<item name="android:windowContentOverlay">@null</item>
</style>
<style name="NoteActionBarStyle" parent="@android:style/Widget.Holo.Light.ActionBar.Solid">
<item name="android:displayOptions" />
<item name="android:visibility">gone</item>
</style>
</resources>

@ -0,0 +1,9 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Base.Theme.Notesmaster" parent="Theme.Material3.DayNight.NoActionBar">
<!-- Customize your light theme here. -->
<!-- <item name="colorPrimary">@color/my_light_primary</item> -->
</style>
<style name="Theme.Notesmaster" parent="Base.Theme.Notesmaster" />
</resources>

@ -1,6 +0,0 @@
Chinese Vosk model for mobile
CER results
23.54% speechio_02
38.29% speechio_06

@ -1,8 +0,0 @@
--use-energy=false
--sample-frequency=16000
--num-mel-bins=40
--num-ceps=40
--low-freq=40
--high-freq=-200
--allow-upsample=true
--allow-downsample=true

@ -1,10 +0,0 @@
--min-active=200
--max-active=5000
--beam=12.0
--lattice-beam=4.0
--acoustic-scale=1.0
--frame-subsampling-factor=3
--endpoint.silence-phones=1:2:3:4:5:6:7:8:9:10
--endpoint.rule2.min-trailing-silence=0.5
--endpoint.rule3.min-trailing-silence=1.0
--endpoint.rule4.min-trailing-silence=2.0

@ -1,39 +0,0 @@
11845
11846
11847
11848
11849
11850
11851
11852
11853
11854
11855
11856
11857
11858
11859
11860
11861
11862
11863
11864
11865
11866
11867
11868
11869
11870
11871
11872
11873
11874
11875
11876
11877
11878
11879
11880
11881
11882
11883

@ -1,646 +0,0 @@
1 nonword
2 begin
3 end
4 internal
5 singleton
6 nonword
7 begin
8 end
9 internal
10 singleton
11 begin
12 end
13 internal
14 singleton
15 begin
16 end
17 internal
18 singleton
19 begin
20 end
21 internal
22 singleton
23 begin
24 end
25 internal
26 singleton
27 begin
28 end
29 internal
30 singleton
31 begin
32 end
33 internal
34 singleton
35 begin
36 end
37 internal
38 singleton
39 begin
40 end
41 internal
42 singleton
43 begin
44 end
45 internal
46 singleton
47 begin
48 end
49 internal
50 singleton
51 begin
52 end
53 internal
54 singleton
55 begin
56 end
57 internal
58 singleton
59 begin
60 end
61 internal
62 singleton
63 begin
64 end
65 internal
66 singleton
67 begin
68 end
69 internal
70 singleton
71 begin
72 end
73 internal
74 singleton
75 begin
76 end
77 internal
78 singleton
79 begin
80 end
81 internal
82 singleton
83 begin
84 end
85 internal
86 singleton
87 begin
88 end
89 internal
90 singleton
91 begin
92 end
93 internal
94 singleton
95 begin
96 end
97 internal
98 singleton
99 begin
100 end
101 internal
102 singleton
103 begin
104 end
105 internal
106 singleton
107 begin
108 end
109 internal
110 singleton
111 begin
112 end
113 internal
114 singleton
115 begin
116 end
117 internal
118 singleton
119 begin
120 end
121 internal
122 singleton
123 begin
124 end
125 internal
126 singleton
127 begin
128 end
129 internal
130 singleton
131 begin
132 end
133 internal
134 singleton
135 begin
136 end
137 internal
138 singleton
139 begin
140 end
141 internal
142 singleton
143 begin
144 end
145 internal
146 singleton
147 begin
148 end
149 internal
150 singleton
151 begin
152 end
153 internal
154 singleton
155 begin
156 end
157 internal
158 singleton
159 begin
160 end
161 internal
162 singleton
163 begin
164 end
165 internal
166 singleton
167 begin
168 end
169 internal
170 singleton
171 begin
172 end
173 internal
174 singleton
175 begin
176 end
177 internal
178 singleton
179 begin
180 end
181 internal
182 singleton
183 begin
184 end
185 internal
186 singleton
187 begin
188 end
189 internal
190 singleton
191 begin
192 end
193 internal
194 singleton
195 begin
196 end
197 internal
198 singleton
199 begin
200 end
201 internal
202 singleton
203 begin
204 end
205 internal
206 singleton
207 begin
208 end
209 internal
210 singleton
211 begin
212 end
213 internal
214 singleton
215 begin
216 end
217 internal
218 singleton
219 begin
220 end
221 internal
222 singleton
223 begin
224 end
225 internal
226 singleton
227 begin
228 end
229 internal
230 singleton
231 begin
232 end
233 internal
234 singleton
235 begin
236 end
237 internal
238 singleton
239 begin
240 end
241 internal
242 singleton
243 begin
244 end
245 internal
246 singleton
247 begin
248 end
249 internal
250 singleton
251 begin
252 end
253 internal
254 singleton
255 begin
256 end
257 internal
258 singleton
259 begin
260 end
261 internal
262 singleton
263 begin
264 end
265 internal
266 singleton
267 begin
268 end
269 internal
270 singleton
271 begin
272 end
273 internal
274 singleton
275 begin
276 end
277 internal
278 singleton
279 begin
280 end
281 internal
282 singleton
283 begin
284 end
285 internal
286 singleton
287 begin
288 end
289 internal
290 singleton
291 begin
292 end
293 internal
294 singleton
295 begin
296 end
297 internal
298 singleton
299 begin
300 end
301 internal
302 singleton
303 begin
304 end
305 internal
306 singleton
307 begin
308 end
309 internal
310 singleton
311 begin
312 end
313 internal
314 singleton
315 begin
316 end
317 internal
318 singleton
319 begin
320 end
321 internal
322 singleton
323 begin
324 end
325 internal
326 singleton
327 begin
328 end
329 internal
330 singleton
331 begin
332 end
333 internal
334 singleton
335 begin
336 end
337 internal
338 singleton
339 begin
340 end
341 internal
342 singleton
343 begin
344 end
345 internal
346 singleton
347 begin
348 end
349 internal
350 singleton
351 begin
352 end
353 internal
354 singleton
355 begin
356 end
357 internal
358 singleton
359 begin
360 end
361 internal
362 singleton
363 begin
364 end
365 internal
366 singleton
367 begin
368 end
369 internal
370 singleton
371 begin
372 end
373 internal
374 singleton
375 begin
376 end
377 internal
378 singleton
379 begin
380 end
381 internal
382 singleton
383 begin
384 end
385 internal
386 singleton
387 begin
388 end
389 internal
390 singleton
391 begin
392 end
393 internal
394 singleton
395 begin
396 end
397 internal
398 singleton
399 begin
400 end
401 internal
402 singleton
403 begin
404 end
405 internal
406 singleton
407 begin
408 end
409 internal
410 singleton
411 begin
412 end
413 internal
414 singleton
415 begin
416 end
417 internal
418 singleton
419 begin
420 end
421 internal
422 singleton
423 begin
424 end
425 internal
426 singleton
427 begin
428 end
429 internal
430 singleton
431 begin
432 end
433 internal
434 singleton
435 begin
436 end
437 internal
438 singleton
439 begin
440 end
441 internal
442 singleton
443 begin
444 end
445 internal
446 singleton
447 begin
448 end
449 internal
450 singleton
451 begin
452 end
453 internal
454 singleton
455 begin
456 end
457 internal
458 singleton
459 begin
460 end
461 internal
462 singleton
463 begin
464 end
465 internal
466 singleton
467 begin
468 end
469 internal
470 singleton
471 begin
472 end
473 internal
474 singleton
475 begin
476 end
477 internal
478 singleton
479 begin
480 end
481 internal
482 singleton
483 begin
484 end
485 internal
486 singleton
487 begin
488 end
489 internal
490 singleton
491 begin
492 end
493 internal
494 singleton
495 begin
496 end
497 internal
498 singleton
499 begin
500 end
501 internal
502 singleton
503 begin
504 end
505 internal
506 singleton
507 begin
508 end
509 internal
510 singleton
511 begin
512 end
513 internal
514 singleton
515 begin
516 end
517 internal
518 singleton
519 begin
520 end
521 internal
522 singleton
523 begin
524 end
525 internal
526 singleton
527 begin
528 end
529 internal
530 singleton
531 begin
532 end
533 internal
534 singleton
535 begin
536 end
537 internal
538 singleton
539 begin
540 end
541 internal
542 singleton
543 begin
544 end
545 internal
546 singleton
547 begin
548 end
549 internal
550 singleton
551 begin
552 end
553 internal
554 singleton
555 begin
556 end
557 internal
558 singleton
559 begin
560 end
561 internal
562 singleton
563 begin
564 end
565 internal
566 singleton
567 begin
568 end
569 internal
570 singleton
571 begin
572 end
573 internal
574 singleton
575 begin
576 end
577 internal
578 singleton
579 begin
580 end
581 internal
582 singleton
583 begin
584 end
585 internal
586 singleton
587 begin
588 end
589 internal
590 singleton
591 begin
592 end
593 internal
594 singleton
595 begin
596 end
597 internal
598 singleton
599 begin
600 end
601 internal
602 singleton
603 begin
604 end
605 internal
606 singleton
607 begin
608 end
609 internal
610 singleton
611 begin
612 end
613 internal
614 singleton
615 begin
616 end
617 internal
618 singleton
619 begin
620 end
621 internal
622 singleton
623 begin
624 end
625 internal
626 singleton
627 begin
628 end
629 internal
630 singleton
631 begin
632 end
633 internal
634 singleton
635 begin
636 end
637 internal
638 singleton
639 begin
640 end
641 internal
642 singleton
643 begin
644 end
645 internal
646 singleton

@ -1,3 +0,0 @@
[
1.117107e+11 -7.827721e+08 -1.101398e+10 -2.193934e+09 -1.347332e+10 -1.613916e+10 -1.199561e+10 -1.255081e+10 -1.638895e+10 -3.821099e+09 -1.372833e+10 -5.244242e+09 -1.098187e+10 -3.655235e+09 -9.364579e+09 -4.285302e+09 -6.296873e+09 -1.552953e+09 -3.176746e+09 -1.202976e+08 -9.857023e+08 2.316555e+08 -1.61059e+08 -5.891868e+07 3.465849e+08 -1.842054e+08 3.248211e+08 -1.483965e+08 3.739239e+08 -6.672061e+08 4.442288e+08 -9.274889e+08 5.142684e+08 4.292036e+07 2.206386e+08 -4.532715e+08 -2.092499e+08 -3.70488e+08 -8.079404e+07 -8.425977e+07 1.344125e+09
9.982632e+12 1.02635e+12 8.634624e+11 9.06451e+11 9.652096e+11 1.12772e+12 9.468372e+11 9.141218e+11 9.670484e+11 6.936961e+11 8.141006e+11 6.256321e+11 6.087707e+11 4.616898e+11 4.212042e+11 2.862872e+11 2.498089e+11 1.470856e+11 1.099197e+11 5.780894e+10 3.118114e+10 1.060667e+10 1.466199e+09 4.173056e+08 5.257362e+09 1.277714e+10 2.114478e+10 2.974502e+10 3.587691e+10 4.078971e+10 4.247745e+10 4.382608e+10 4.62521e+10 4.575282e+10 3.546206e+10 3.041531e+10 2.838562e+10 2.258604e+10 1.715295e+10 1.303227e+10 0 ]

@ -794,7 +794,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
contentView.setPadding(0, getStatusBarHeight(), 0, getNavigationBarHeight()); contentView.setPadding(0, getStatusBarHeight(), 0, getNavigationBarHeight());
} }
} }
mHeadViewPanel = findViewById(R.id.note_title); mHeadViewPanel = findViewById(R.id.note_title);
mNoteHeaderHolder = new HeadViewHolder(); mNoteHeaderHolder = new HeadViewHolder();
mNoteHeaderHolder.tvModified = (TextView) findViewById(R.id.tv_modified_date); mNoteHeaderHolder.tvModified = (TextView) findViewById(R.id.tv_modified_date);
@ -873,19 +873,19 @@ public class NoteEditActivity extends Activity implements OnClickListener,
if (mBtnSetBgColor != null) { if (mBtnSetBgColor != null) {
mBtnSetBgColor.setOnClickListener(this); mBtnSetBgColor.setOnClickListener(this);
} }
// 初始化插入图片按钮 // 初始化插入图片按钮
mBtnInsertImage = (Button) findViewById(R.id.btn_insert_image); mBtnInsertImage = (Button) findViewById(R.id.btn_insert_image);
if (mBtnInsertImage != null) { if (mBtnInsertImage != null) {
mBtnInsertImage.setOnClickListener(this); mBtnInsertImage.setOnClickListener(this);
} }
// 初始化语音转文字按钮 // 初始化语音转文字按钮
mBtnVoiceToText = (Button) findViewById(R.id.btn_voice_to_text); mBtnVoiceToText = (Button) findViewById(R.id.btn_voice_to_text);
if (mBtnVoiceToText != null) { if (mBtnVoiceToText != null) {
mBtnVoiceToText.setOnClickListener(this); mBtnVoiceToText.setOnClickListener(this);
} }
// 初始化Vosk离线语音识别器 // 初始化Vosk离线语音识别器
mVoskRecognizer = new VoskRecognitionService(this); mVoskRecognizer = new VoskRecognitionService(this);
mVoskRecognizer.addListener(this); mVoskRecognizer.addListener(this);
@ -1336,7 +1336,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
private void switchToListMode(String text) { private void switchToListMode(String text) {
long startTime = System.currentTimeMillis(); long startTime = System.currentTimeMillis();
Log.d(TAG, "switchToListMode started, text length: " + (text != null ? text.length() : 0)); Log.d(TAG, "switchToListMode started, text length: " + (text != null ? text.length() : 0));
// 处理null文本避免空指针异常OMO // 处理null文本避免空指针异常OMO
final String processedText = text != null ? text : ""; final String processedText = text != null ? text : "";
@ -1346,17 +1346,17 @@ public class NoteEditActivity extends Activity implements OnClickListener,
public void run() { public void run() {
final String[] items = processedText.split("\n"); // 按行分割文本 final String[] items = processedText.split("\n"); // 按行分割文本
Log.d(TAG, "switchToListMode: split into " + items.length + " items"); Log.d(TAG, "switchToListMode: split into " + items.length + " items");
// 切换回主线程更新UI // 切换回主线程更新UI
runOnUiThread(new Runnable() { runOnUiThread(new Runnable() {
@Override @Override
public void run() { public void run() {
// 批量创建和添加视图,减少布局重绘 // 批量创建和添加视图,减少布局重绘
mEditTextList.removeAllViews(); // 清空编辑文本列表 mEditTextList.removeAllViews(); // 清空编辑文本列表
int index = 0; int index = 0;
View lastItem = null; View lastItem = null;
// 先创建所有视图 // 先创建所有视图
ArrayList<View> viewsToAdd = new ArrayList<>(); ArrayList<View> viewsToAdd = new ArrayList<>();
for (String item : items) { for (String item : items) {
@ -1366,23 +1366,23 @@ public class NoteEditActivity extends Activity implements OnClickListener,
index++; index++;
} }
} }
// 添加最后一个空列表项 // 添加最后一个空列表项
lastItem = getListItem("", index); lastItem = getListItem("", index);
viewsToAdd.add(lastItem); viewsToAdd.add(lastItem);
// 一次性添加所有视图到容器 // 一次性添加所有视图到容器
for (View view : viewsToAdd) { for (View view : viewsToAdd) {
mEditTextList.addView(view); mEditTextList.addView(view);
} }
// 设置焦点 // 设置焦点
ListItemViewHolder holder = (ListItemViewHolder) lastItem.getTag(); ListItemViewHolder holder = (ListItemViewHolder) lastItem.getTag();
holder.etEditText.requestFocus(); holder.etEditText.requestFocus();
mNoteEditor.setVisibility(View.GONE); // 隐藏普通编辑器 mNoteEditor.setVisibility(View.GONE); // 隐藏普通编辑器
mEditTextList.setVisibility(View.VISIBLE); // 显示清单编辑器 mEditTextList.setVisibility(View.VISIBLE); // 显示清单编辑器
long endTime = System.currentTimeMillis(); long endTime = System.currentTimeMillis();
Log.d(TAG, "switchToListMode completed in " + (endTime - startTime) + "ms"); Log.d(TAG, "switchToListMode completed in " + (endTime - startTime) + "ms");
} }
@ -1400,7 +1400,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
*/ */
private Spannable getHighlightQueryResult(String fullText, String userQuery) { private Spannable getHighlightQueryResult(String fullText, String userQuery) {
long startTime = System.currentTimeMillis(); long startTime = System.currentTimeMillis();
String text = fullText == null ? "" : fullText; String text = fullText == null ? "" : fullText;
SpannableString spannable = new SpannableString(text); SpannableString spannable = new SpannableString(text);
@ -1419,7 +1419,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
Spannable.SPAN_INCLUSIVE_EXCLUSIVE); Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
start = m.end(); start = m.end();
matchCount++; matchCount++;
// 限制匹配数量,避免处理过多匹配项导致性能问题 // 限制匹配数量,避免处理过多匹配项导致性能问题
if (matchCount > 100) { if (matchCount > 100) {
Log.w(TAG, "getHighlightQueryResult: too many matches, stopping at 100"); Log.w(TAG, "getHighlightQueryResult: too many matches, stopping at 100");
@ -1431,10 +1431,10 @@ public class NoteEditActivity extends Activity implements OnClickListener,
Log.e(TAG, "Error in getHighlightQueryResult: " + e.getMessage()); Log.e(TAG, "Error in getHighlightQueryResult: " + e.getMessage());
} }
} }
long endTime = System.currentTimeMillis(); long endTime = System.currentTimeMillis();
Log.d(TAG, "getHighlightQueryResult completed in " + (endTime - startTime) + "ms"); Log.d(TAG, "getHighlightQueryResult completed in " + (endTime - startTime) + "ms");
return spannable; return spannable;
} }
@ -1447,13 +1447,13 @@ public class NoteEditActivity extends Activity implements OnClickListener,
*/ */
private View getListItem(String item, int index) { private View getListItem(String item, int index) {
View view = LayoutInflater.from(this).inflate(R.layout.note_edit_list_item, null); View view = LayoutInflater.from(this).inflate(R.layout.note_edit_list_item, null);
// 创建并缓存ViewHolder // 创建并缓存ViewHolder
ListItemViewHolder holder = new ListItemViewHolder(); ListItemViewHolder holder = new ListItemViewHolder();
holder.etEditText = (NoteEditText) view.findViewById(R.id.et_edit_text); holder.etEditText = (NoteEditText) view.findViewById(R.id.et_edit_text);
holder.cbEditItem = (CheckBox) view.findViewById(R.id.cb_edit_item); holder.cbEditItem = (CheckBox) view.findViewById(R.id.cb_edit_item);
view.setTag(holder); view.setTag(holder);
final NoteEditText edit = holder.etEditText; final NoteEditText edit = holder.etEditText;
edit.setTextAppearance(this, TextAppearanceResources.getTexAppearanceResource(mFontSizeId)); edit.setTextAppearance(this, TextAppearanceResources.getTexAppearanceResource(mFontSizeId));
@ -1497,10 +1497,10 @@ public class NoteEditActivity extends Activity implements OnClickListener,
Log.e(TAG, "Wrong index, should not happen"); Log.e(TAG, "Wrong index, should not happen");
return; return;
} }
View view = mEditTextList.getChildAt(index); View view = mEditTextList.getChildAt(index);
ListItemViewHolder holder = (ListItemViewHolder) view.getTag(); ListItemViewHolder holder = (ListItemViewHolder) view.getTag();
if(hasText) { if(hasText) {
holder.cbEditItem.setVisibility(View.VISIBLE); holder.cbEditItem.setVisibility(View.VISIBLE);
} else { } else {
@ -1750,7 +1750,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
private void showToast(int resId) { private void showToast(int resId) {
showToast(resId, Toast.LENGTH_SHORT); showToast(resId, Toast.LENGTH_SHORT);
} }
/** /**
* *
* @param message * @param message
@ -1768,13 +1768,13 @@ public class NoteEditActivity extends Activity implements OnClickListener,
showToast("语音识别正在初始化,请稍候..."); showToast("语音识别正在初始化,请稍候...");
return; return;
} }
// 检查权限 // 检查权限
if (checkSelfPermission(Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) { if (checkSelfPermission(Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
requestPermissions(new String[]{Manifest.permission.RECORD_AUDIO}, REQUEST_CODE_PERMISSION_RECORD_AUDIO); requestPermissions(new String[]{Manifest.permission.RECORD_AUDIO}, REQUEST_CODE_PERMISSION_RECORD_AUDIO);
return; return;
} }
// 切换监听状态 // 切换监听状态
if (mVoskRecognizer.isListening()) { if (mVoskRecognizer.isListening()) {
mVoskRecognizer.stopListening(); mVoskRecognizer.stopListening();
@ -1782,7 +1782,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
mVoskRecognizer.startListening(); mVoskRecognizer.startListening();
} }
} }
/** /**
* *
* @param text * @param text
@ -1804,44 +1804,42 @@ public class NoteEditActivity extends Activity implements OnClickListener,
mNoteEditor.requestFocus(); mNoteEditor.requestFocus();
} }
} }
@Override @Override
public void onReadyForSpeech() { public void onReadyForSpeech() {
isListening = true; isListening = true;
showToast(R.string.voice_recognition_ready); showToast(R.string.voice_recognition_ready);
} }
@Override @Override
public void onBeginningOfSpeech() { public void onBeginningOfSpeech() {
isListening = true; isListening = true;
} }
@Override @Override
public void onEndOfSpeech() { public void onEndOfSpeech() {
isListening = false; isListening = false;
} }
@Override @Override
public void onResult(String text) { public void onResult(String text) {
if (text != null && !text.isEmpty()) { if (text != null && !text.isEmpty()) {
insertVoiceText(text); insertVoiceText(text);
} }
} }
@Override @Override
public void onPartialResult(String text) { public void onPartialResult(String text) {
// 可选:显示部分识别结果 // 可选:显示部分识别结果
} }
@Override @Override
public void onError(String error) { public void onError(String error) {
isListening = false; isListening = false;
showToast("语音识别错误: " + error); showToast("语音识别错误: " + error);
} }
/** /**
* OMO
*/
* *
* @param resId ID * @param resId ID
* @param duration * @param duration
@ -2455,7 +2453,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
}); });
} }
/** /**
* *
*/ */
@ -2467,7 +2465,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
} }
return result; return result;
} }
/** /**
* *
*/ */

@ -9,10 +9,10 @@ import android.os.Looper;
import android.util.Log; import android.util.Log;
import org.json.JSONObject; import org.json.JSONObject;
import org.vosk.Model; import net.micode.notes.vosk.Model;
import org.vosk.Recognizer; import net.micode.notes.vosk.Recognizer;
import org.vosk.LibVosk; import net.micode.notes.vosk.LibVosk;
import org.vosk.LogLevel; import net.micode.notes.vosk.LogLevel;
import java.io.File; import java.io.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;

@ -0,0 +1,62 @@
package net.micode.notes.vosk;
import com.sun.jna.Native;
import com.sun.jna.Library;
import com.sun.jna.Platform;
import com.sun.jna.Pointer;
import java.io.File;
import java.io.InputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
public class LibVosk {
static {
Native.register(LibVosk.class, "vosk");
}
public static native void vosk_set_log_level(int level);
public static native Pointer vosk_model_new(String path);
public static native void vosk_model_free(Pointer model);
public static native Pointer vosk_spk_model_new(String path);
public static native void vosk_spk_model_free(Pointer model);
public static native Pointer vosk_recognizer_new(Model model, float sample_rate);
public static native Pointer vosk_recognizer_new_spk(Pointer model, float sample_rate, Pointer spk_model);
public static native Pointer vosk_recognizer_new_grm(Pointer model, float sample_rate, String grammar);
public static native void vosk_recognizer_set_max_alternatives(Pointer recognizer, int max_alternatives);
public static native void vosk_recognizer_set_words(Pointer recognizer, boolean words);
public static native void vosk_recognizer_set_partial_words(Pointer recognizer, boolean partial_words);
public static native void vosk_recognizer_set_spk_model(Pointer recognizer, Pointer spk_model);
public static native boolean vosk_recognizer_accept_waveform(Pointer recognizer, byte[] data, int len);
public static native boolean vosk_recognizer_accept_waveform_s(Pointer recognizer, short[] data, int len);
public static native boolean vosk_recognizer_accept_waveform_f(Pointer recognizer, float[] data, int len);
public static native String vosk_recognizer_result(Pointer recognizer);
public static native String vosk_recognizer_final_result(Pointer recognizer);
public static native String vosk_recognizer_partial_result(Pointer recognizer);
public static native void vosk_recognizer_reset(Pointer recognizer);
public static native void vosk_recognizer_free(Pointer recognizer);
public static void setLogLevel(LogLevel loglevel) {
vosk_set_log_level(loglevel.getValue());
}
}

@ -0,0 +1,17 @@
package net.micode.notes.vosk;
public enum LogLevel {
WARNINGS(-1), // Print warning and errors
INFO(0), // Print info, along with warning and error messages, but no debug
DEBUG(1); // Print debug info
private final int value;
LogLevel(int value) {
this.value = value;
}
public int getValue() {
return this.value;
}
}

@ -0,0 +1,17 @@
package net.micode.notes.vosk;
import com.sun.jna.PointerType;
public class Model extends PointerType implements AutoCloseable {
public Model() {
}
public Model(String path) {
super(LibVosk.vosk_model_new(path));
}
@Override
public void close() {
LibVosk.vosk_model_free(this.getPointer());
}
}

@ -0,0 +1,66 @@
package net.micode.notes.vosk;
import com.sun.jna.PointerType;
public class Recognizer extends PointerType implements AutoCloseable {
public Recognizer(Model model, float sampleRate) {
super(LibVosk.vosk_recognizer_new(model, sampleRate));
}
public Recognizer(Model model, float sampleRate, SpeakerModel spkModel) {
super(LibVosk.vosk_recognizer_new_spk(model.getPointer(), sampleRate, spkModel.getPointer()));
}
public Recognizer(Model model, float sampleRate, String grammar) {
super(LibVosk.vosk_recognizer_new_grm(model.getPointer(), sampleRate, grammar));
}
public void setMaxAlternatives(int maxAlternatives) {
LibVosk.vosk_recognizer_set_max_alternatives(this.getPointer(), maxAlternatives);
}
public void setWords(boolean words) {
LibVosk.vosk_recognizer_set_words(this.getPointer(), words);
}
public void setPartialWords(boolean partial_words) {
LibVosk.vosk_recognizer_set_partial_words(this.getPointer(), partial_words);
}
public void setSpeakerModel(SpeakerModel spkModel) {
LibVosk.vosk_recognizer_set_spk_model(this.getPointer(), spkModel.getPointer());
}
public boolean acceptWaveForm(byte[] data, int len) {
return LibVosk.vosk_recognizer_accept_waveform(this.getPointer(), data, len);
}
public boolean acceptWaveForm(short[] data, int len) {
return LibVosk.vosk_recognizer_accept_waveform_s(this.getPointer(), data, len);
}
public boolean acceptWaveForm(float[] data, int len) {
return LibVosk.vosk_recognizer_accept_waveform_f(this.getPointer(), data, len);
}
public String getResult() {
return LibVosk.vosk_recognizer_result(this.getPointer());
}
public String getPartialResult() {
return LibVosk.vosk_recognizer_partial_result(this.getPointer());
}
public String getFinalResult() {
return LibVosk.vosk_recognizer_final_result(this.getPointer());
}
public void reset() {
LibVosk.vosk_recognizer_reset(this.getPointer());
}
@Override
public void close() {
LibVosk.vosk_recognizer_free(this.getPointer());
}
}

@ -0,0 +1,17 @@
package net.micode.notes.vosk;
import com.sun.jna.PointerType;
public class SpeakerModel extends PointerType implements AutoCloseable {
public SpeakerModel() {
}
public SpeakerModel(String path) {
super(LibVosk.vosk_spk_model_new(path));
}
@Override
public void close() {
LibVosk.vosk_spk_model_free(this.getPointer());
}
}

@ -0,0 +1,46 @@
// Copyright 2019 Alpha Cephei Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package net.micode.notes.vosk.android;
/**
* Interface to receive recognition results
*/
public interface RecognitionListener {
/**
* Called when partial recognition result is available.
*/
void onPartialResult(String hypothesis);
/**
* Called after silence occured.
*/
void onResult(String hypothesis);
/**
* Called after stream end.
*/
void onFinalResult(String hypothesis);
/**
* Called when an error occurs.
*/
void onError(Exception exception);
/**
* Called after timeout expired
*/
void onTimeout();
}

@ -0,0 +1,257 @@
// Copyright 2019 Alpha Cephei Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package net.micode.notes.vosk.android;
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder.AudioSource;
import android.os.Handler;
import android.os.Looper;
import net.micode.notes.vosk.Recognizer;
import java.io.IOException;
/**
* Service that records audio in a thread, passes it to a recognizer and emits
* recognition results. Recognition events are passed to a client using
* {@link RecognitionListener}
*/
public class SpeechService {
private final Recognizer recognizer;
private final int sampleRate;
private final static float BUFFER_SIZE_SECONDS = 0.2f;
private final int bufferSize;
private final AudioRecord recorder;
private RecognizerThread recognizerThread;
private final Handler mainHandler = new Handler(Looper.getMainLooper());
/**
* Creates speech service. Service holds the AudioRecord object, so you
* need to call {@link #shutdown()} in order to properly finalize it.
*
* @throws IOException thrown if audio recorder can not be created for some reason.
*/
public SpeechService(Recognizer recognizer, float sampleRate) throws IOException {
this.recognizer = recognizer;
this.sampleRate = (int) sampleRate;
bufferSize = Math.round(this.sampleRate * BUFFER_SIZE_SECONDS);
recorder = new AudioRecord(
AudioSource.VOICE_RECOGNITION, this.sampleRate,
AudioFormat.CHANNEL_IN_MONO,
AudioFormat.ENCODING_PCM_16BIT, bufferSize * 2);
if (recorder.getState() == AudioRecord.STATE_UNINITIALIZED) {
recorder.release();
throw new IOException(
"Failed to initialize recorder. Microphone might be already in use.");
}
}
/**
* Starts recognition. Does nothing if recognition is active.
*
* @return true if recognition was actually started
*/
public boolean startListening(RecognitionListener listener) {
if (null != recognizerThread)
return false;
recognizerThread = new RecognizerThread(listener);
recognizerThread.start();
return true;
}
/**
* Starts recognition. After specified timeout listening stops and the
* endOfSpeech signals about that. Does nothing if recognition is active.
* <p>
* timeout - timeout in milliseconds to listen.
*
* @return true if recognition was actually started
*/
public boolean startListening(RecognitionListener listener, int timeout) {
if (null != recognizerThread)
return false;
recognizerThread = new RecognizerThread(listener, timeout);
recognizerThread.start();
return true;
}
private boolean stopRecognizerThread() {
if (null == recognizerThread)
return false;
try {
recognizerThread.interrupt();
recognizerThread.join();
} catch (InterruptedException e) {
// Restore the interrupted status.
Thread.currentThread().interrupt();
}
recognizerThread = null;
return true;
}
/**
* Stops recognition. Listener should receive final result if there is
* any. Does nothing if recognition is not active.
*
* @return true if recognition was actually stopped
*/
public boolean stop() {
return stopRecognizerThread();
}
/**
* Cancel recognition. Do not post any new events, simply cancel processing.
* Does nothing if recognition is not active.
*
* @return true if recognition was actually stopped
*/
public boolean cancel() {
if (recognizerThread != null) {
recognizerThread.setPause(true);
}
return stopRecognizerThread();
}
/**
* Shutdown the recognizer and release the recorder
*/
public void shutdown() {
recorder.release();
}
public void setPause(boolean paused) {
if (recognizerThread != null) {
recognizerThread.setPause(paused);
}
}
/**
* Resets recognizer in a thread, starts recognition over again
*/
public void reset() {
if (recognizerThread != null) {
recognizerThread.reset();
}
}
private final class RecognizerThread extends Thread {
private int remainingSamples;
private final int timeoutSamples;
private final static int NO_TIMEOUT = -1;
private volatile boolean paused = false;
private volatile boolean reset = false;
RecognitionListener listener;
public RecognizerThread(RecognitionListener listener, int timeout) {
this.listener = listener;
if (timeout != NO_TIMEOUT)
this.timeoutSamples = timeout * sampleRate / 1000;
else
this.timeoutSamples = NO_TIMEOUT;
this.remainingSamples = this.timeoutSamples;
}
public RecognizerThread(RecognitionListener listener) {
this(listener, NO_TIMEOUT);
}
/**
* When we are paused, don't process audio by the recognizer and don't emit
* any listener results
*
* @param paused the status of pause
*/
public void setPause(boolean paused) {
this.paused = paused;
}
/**
* Set reset state to signal reset of the recognizer and start over
*/
public void reset() {
this.reset = true;
}
@Override
public void run() {
recorder.startRecording();
if (recorder.getRecordingState() == AudioRecord.RECORDSTATE_STOPPED) {
recorder.stop();
IOException ioe = new IOException(
"Failed to start recording. Microphone might be already in use.");
mainHandler.post(() -> listener.onError(ioe));
}
short[] buffer = new short[bufferSize];
while (!interrupted()
&& ((timeoutSamples == NO_TIMEOUT) || (remainingSamples > 0))) {
int nread = recorder.read(buffer, 0, buffer.length);
if (paused) {
continue;
}
if (reset) {
recognizer.reset();
reset = false;
}
if (nread < 0)
throw new RuntimeException("error reading audio buffer");
if (recognizer.acceptWaveForm(buffer, nread)) {
final String result = recognizer.getResult();
mainHandler.post(() -> listener.onResult(result));
} else {
final String partialResult = recognizer.getPartialResult();
mainHandler.post(() -> listener.onPartialResult(partialResult));
}
if (timeoutSamples != NO_TIMEOUT) {
remainingSamples = remainingSamples - nread;
}
}
recorder.stop();
if (!paused) {
// If we met timeout signal that speech ended
if (timeoutSamples != NO_TIMEOUT && remainingSamples <= 0) {
mainHandler.post(() -> listener.onTimeout());
} else {
final String finalResult = recognizer.getFinalResult();
mainHandler.post(() -> listener.onFinalResult(finalResult));
}
}
}
}
}

@ -0,0 +1,165 @@
// Copyright 2019 Alpha Cephei Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package net.micode.notes.vosk.android;
import android.os.Handler;
import android.os.Looper;
import net.micode.notes.vosk.Recognizer;
import java.io.IOException;
import java.io.InputStream;
/**
* Service that recognizes stream audio in a thread, passes it to a recognizer and emits
* recognition results. Recognition events are passed to a client using
* {@link RecognitionListener}
*/
public class SpeechStreamService {
private final Recognizer recognizer;
private final InputStream inputStream;
private final int sampleRate;
private final static float BUFFER_SIZE_SECONDS = 0.2f;
private final int bufferSize;
private Thread recognizerThread;
private final Handler mainHandler = new Handler(Looper.getMainLooper());
/**
* Creates speech service.
**/
public SpeechStreamService(Recognizer recognizer, InputStream inputStream, float sampleRate) {
this.recognizer = recognizer;
this.sampleRate = (int) sampleRate;
this.inputStream = inputStream;
bufferSize = Math.round(this.sampleRate * BUFFER_SIZE_SECONDS * 2);
}
/**
* Starts recognition. Does nothing if recognition is active.
*
* @return true if recognition was actually started
*/
public boolean start(RecognitionListener listener) {
if (null != recognizerThread)
return false;
recognizerThread = new RecognizerThread(listener);
recognizerThread.start();
return true;
}
/**
* Starts recognition. After specified timeout listening stops and the
* endOfSpeech signals about that. Does nothing if recognition is active.
* <p>
* timeout - timeout in milliseconds to listen.
*
* @return true if recognition was actually started
*/
public boolean start(RecognitionListener listener, int timeout) {
if (null != recognizerThread)
return false;
recognizerThread = new RecognizerThread(listener, timeout);
recognizerThread.start();
return true;
}
/**
* Stops recognition. All listeners should receive final result if there is
* any. Does nothing if recognition is not active.
*
* @return true if recognition was actually stopped
*/
public boolean stop() {
if (null == recognizerThread)
return false;
try {
recognizerThread.interrupt();
recognizerThread.join();
} catch (InterruptedException e) {
// Restore the interrupted status.
Thread.currentThread().interrupt();
}
recognizerThread = null;
return true;
}
private final class RecognizerThread extends Thread {
private int remainingSamples;
private final int timeoutSamples;
private final static int NO_TIMEOUT = -1;
RecognitionListener listener;
public RecognizerThread(RecognitionListener listener, int timeout) {
this.listener = listener;
if (timeout != NO_TIMEOUT)
this.timeoutSamples = timeout * sampleRate / 1000;
else
this.timeoutSamples = NO_TIMEOUT;
this.remainingSamples = this.timeoutSamples;
}
public RecognizerThread(RecognitionListener listener) {
this(listener, NO_TIMEOUT);
}
@Override
public void run() {
byte[] buffer = new byte[bufferSize];
while (!interrupted()
&& ((timeoutSamples == NO_TIMEOUT) || (remainingSamples > 0))) {
try {
int nread = inputStream.read(buffer, 0, buffer.length);
if (nread < 0) {
break;
} else {
boolean isSilence = recognizer.acceptWaveForm(buffer, nread);
if (isSilence) {
final String result = recognizer.getResult();
mainHandler.post(() -> listener.onResult(result));
} else {
final String partialResult = recognizer.getPartialResult();
mainHandler.post(() -> listener.onPartialResult(partialResult));
}
}
if (timeoutSamples != NO_TIMEOUT) {
remainingSamples = remainingSamples - nread;
}
} catch (IOException e) {
mainHandler.post(() -> listener.onError(e));
}
}
// If we met timeout signal that speech ended
if (timeoutSamples != NO_TIMEOUT && remainingSamples <= 0) {
mainHandler.post(() -> listener.onTimeout());
} else {
final String finalResult = recognizer.getFinalResult();
mainHandler.post(() -> listener.onFinalResult(finalResult));
}
}
}
}

@ -0,0 +1,150 @@
// Copyright 2019 Alpha Cephei Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package net.micode.notes.vosk.android;
import android.content.Context;
import android.content.res.AssetManager;
import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import net.micode.notes.vosk.Model;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
/**
* Provides utility methods to sync model files to external storage to allow
* C++ code access them. Relies on file named "uuid" to track updates.
*/
public class StorageService {
protected static final String TAG = StorageService.class.getSimpleName();
public interface Callback<R> {
void onComplete(R result);
}
public static void unpack(Context context, String sourcePath, final String targetPath, final Callback<Model> completeCallback, final Callback<IOException> errorCallback) {
Executor executor = Executors.newSingleThreadExecutor(); // change according to your requirements
Handler handler = new Handler(Looper.getMainLooper());
executor.execute(() -> {
try {
final String outputPath = sync(context, sourcePath, targetPath);
Model model = new Model(outputPath);
handler.post(() -> completeCallback.onComplete(model));
} catch (final IOException e) {
handler.post(() -> errorCallback.onComplete(e));
}
});
}
public static String sync(Context context, String sourcePath, String targetPath) throws IOException {
AssetManager assetManager = context.getAssets();
File externalFilesDir = context.getExternalFilesDir(null);
if (externalFilesDir == null) {
throw new IOException("cannot get external files dir, "
+ "external storage state is " + Environment.getExternalStorageState());
}
File targetDir = new File(externalFilesDir, targetPath);
String resultPath = new File(targetDir, sourcePath).getAbsolutePath();
String sourceUUID = readLine(assetManager.open(sourcePath + "/uuid"));
try {
String targetUUID = readLine(new FileInputStream(new File(targetDir, sourcePath + "/uuid")));
if (targetUUID.equals(sourceUUID)) return resultPath;
} catch (FileNotFoundException e) {
// ignore
}
deleteContents(targetDir);
copyAssets(assetManager, sourcePath, targetDir);
// Copy uuid
copyFile(assetManager, sourcePath + "/uuid", targetDir);
return resultPath;
}
private static String readLine(InputStream is) throws IOException {
return new BufferedReader(new InputStreamReader(is)).readLine();
}
private static boolean deleteContents(File dir) {
File[] files = dir.listFiles();
boolean success = true;
if (files != null) {
for (File file : files) {
if (file.isDirectory()) {
success &= deleteContents(file);
}
if (!file.delete()) {
success = false;
}
}
}
return success;
}
private static void copyAssets(AssetManager assetManager, String path, File outPath) throws IOException {
String[] assets = assetManager.list(path);
if (assets == null) {
return;
}
if (assets.length == 0) {
if (!path.endsWith("uuid"))
copyFile(assetManager, path, outPath);
} else {
File dir = new File(outPath, path);
if (!dir.exists()) {
Log.v(TAG, "Making directory " + dir.getAbsolutePath());
if (!dir.mkdirs()) {
Log.v(TAG, "Failed to create directory " + dir.getAbsolutePath());
}
}
for (String asset : assets) {
copyAssets(assetManager, path + "/" + asset, outPath);
}
}
}
private static void copyFile(AssetManager assetManager, String fileName, File outPath) throws IOException {
InputStream in;
Log.v(TAG, "Copy " + fileName + " to " + outPath);
in = assetManager.open(fileName);
OutputStream out = new FileOutputStream(outPath + "/" + fileName);
byte[] buffer = new byte[4000];
int read;
while ((read = in.read(buffer)) != -1) {
out.write(buffer, 0, read);
}
in.close();
out.close();
}
}
Loading…
Cancel
Save