├── .classpath
├── .gitignore
├── .project
├── .settings
├── org.eclipse.core.resources.prefs
└── org.eclipse.jdt.core.prefs
├── AndroidManifest.xml
├── LICENSE
├── README.md
├── assets
├── databases
│ └── mcpdict.zip
└── help
│ ├── context_menu.png
│ ├── ex_da_ren.png
│ ├── ex_gu.png
│ ├── ex_gwon.png
│ ├── ex_hak.png
│ ├── ex_le.png
│ ├── ex_liang.png
│ ├── ex_xin.png
│ ├── favorite.png
│ ├── ic_overflow.png
│ └── index.htm
├── bin
└── MCPDict.apk
├── libs
├── android-sqlite-asset-helper.jar
└── android-support-v4.jar
├── proguard-project.txt
├── project.properties
├── res
├── drawable
│ ├── ic_alert.png
│ ├── ic_cancel.png
│ ├── ic_edit.png
│ ├── ic_error.png
│ ├── ic_info.png
│ ├── ic_question.png
│ ├── ic_search.png
│ ├── ic_star_white.png
│ ├── ic_star_yellow.png
│ ├── lang_ct.png
│ ├── lang_jp_go.png
│ ├── lang_jp_kan.png
│ ├── lang_jp_kwan.png
│ ├── lang_jp_other.png
│ ├── lang_jp_tou.png
│ ├── lang_kr.png
│ ├── lang_mc.png
│ ├── lang_mn.png
│ ├── lang_pu.png
│ ├── lang_sh.png
│ └── lang_vn.png
├── layout-land
│ └── dictionary_fragment.xml
├── layout
│ ├── custom_search_view.xml
│ ├── custom_spinner_dropdown_item.xml
│ ├── dictionary_fragment.xml
│ ├── favorite_fragment.xml
│ ├── favorite_item.xml
│ ├── help_activity.xml
│ ├── main_activity.xml
│ ├── search_result_fragment.xml
│ ├── search_result_item.xml
│ └── settings_activity.xml
├── menu
│ ├── favorite_manage_popup_menu.xml
│ ├── main_menu.xml
│ └── search_result_context_menu.xml
├── mipmap-hdpi
│ └── ic_launcher.png
├── mipmap-mdpi
│ └── ic_launcher.png
├── mipmap-xhdpi
│ └── ic_launcher.png
├── mipmap-xxhdpi
│ └── ic_launcher.png
├── raw
│ ├── orthography_ct_finals.tsv
│ ├── orthography_ct_initials.tsv
│ ├── orthography_hz_variants.txt
│ ├── orthography_jp.tsv
│ ├── orthography_mc_bieng_sjyix.tsv
│ ├── orthography_mc_finals.tsv
│ ├── orthography_mc_initials.tsv
│ ├── orthography_pu_bopomofo.tsv
│ ├── orthography_pu_pinyin.tsv
│ └── orthography_vn.tsv
├── values-land
│ └── strings.xml
├── values
│ ├── strings.xml
│ └── styles.xml
└── xml
│ └── preferences.xml
└── src
├── com
└── mobiRic
│ └── ui
│ └── widget
│ └── Boast.java
└── maigosoft
└── mcpdict
├── ActivityWithOptionsMenu.java
├── CustomListPreference.java
├── CustomSearchView.java
├── DictionaryFragment.java
├── FavoriteCursorAdapter.java
├── FavoriteDialogs.java
├── FavoriteFragment.java
├── FileUtils.java
├── HelpActivity.java
├── MCPDatabase.java
├── MainActivity.java
├── Masks.java
├── Orthography.java
├── RefreshableFragment.java
├── SearchResultCursorAdapter.java
├── SearchResultFragment.java
├── SettingsActivity.java
├── SettingsFragment.java
└── UserDatabase.java
/.classpath:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | bin/*
2 | !bin/MCPDict.apk
3 | gen
4 | prepare
5 | publish
6 | jarlist.cache
7 | lint.xml
8 | pretty.py
9 |
--------------------------------------------------------------------------------
/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | MCPDict
4 |
5 |
6 |
7 |
8 |
9 | com.android.ide.eclipse.adt.ResourceManagerBuilder
10 |
11 |
12 |
13 |
14 | com.android.ide.eclipse.adt.PreCompilerBuilder
15 |
16 |
17 |
18 |
19 | org.eclipse.jdt.core.javabuilder
20 |
21 |
22 |
23 |
24 | com.android.ide.eclipse.adt.ApkBuilder
25 |
26 |
27 |
28 |
29 |
30 | com.android.ide.eclipse.adt.AndroidNature
31 | org.eclipse.jdt.core.javanature
32 |
33 |
34 |
--------------------------------------------------------------------------------
/.settings/org.eclipse.core.resources.prefs:
--------------------------------------------------------------------------------
1 | eclipse.preferences.version=1
2 | encoding/=UTF-8
3 |
--------------------------------------------------------------------------------
/.settings/org.eclipse.jdt.core.prefs:
--------------------------------------------------------------------------------
1 | eclipse.preferences.version=1
2 | org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
3 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
4 | org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
5 | org.eclipse.jdt.core.compiler.compliance=1.7
6 | org.eclipse.jdt.core.compiler.debug.lineNumber=generate
7 | org.eclipse.jdt.core.compiler.debug.localVariable=generate
8 | org.eclipse.jdt.core.compiler.debug.sourceFile=generate
9 | org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
10 | org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
11 | org.eclipse.jdt.core.compiler.source=1.7
12 |
--------------------------------------------------------------------------------
/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
10 |
11 |
16 |
17 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
31 |
32 |
33 |
37 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Yun Wang (Maigo)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # MCPDict: 漢字古今中外讀音查詢
2 |
3 | 這是一個Android應用程序,可以查詢漢字在古今中外多種語言中的讀音(包括中古漢語、普通話、粵語、吳語(上海話)、閩南語、朝鮮語、越南語、日語),也可以通過這些語言中的讀音來反查漢字。
4 |
5 | ## 版本歷史
6 |
7 | ### V3.0 (2016.05.03)
8 | 添加功能:
9 | 1) 添加吳語(上海話)及閩南語讀音;
10 | 2) 添加《平水韻》韻部;
11 | 3) 添加生字本導出、導入功能。
12 | 大幅修訂簡繁、異體轉換表。
13 | 不再支持Android 4.0以下版本。
14 |
15 | ### V2.1 (2015.03.24)
16 | 支持用注音符號輸入和顯示普通話讀音。
17 |
18 | ### V2.0.1 (2015.03.09)
19 | 去掉了236個日本國字因數據庫處理失誤產生的錯誤音讀。
20 | 給“畠”字增加了普通話讀音tián。
21 |
22 | ### V2.0 (2015.02.07)
23 | 添加功能:
24 | 1) 添加了複製漢字讀音的功能;
25 | 2) 添加了生字本功能;
26 | 修訂數據庫:
27 | 1) 改用poem的《廣韻字音表》(2014年9月30日版)作爲中古音數據庫;
28 | 2) 更新了越南語讀音數據庫;
29 | 3) 修正了簡繁、異體字轉換表中的個別條目。
30 |
31 | ### V1.3 (2014.11.24)
32 | 兼容低版本的Android(最低支持版本由3.0改爲1.6,但僅在2.2上做過測試)。
33 |
34 | ### V1.2.1 (2014.08.04)
35 | 修正了普通話音節“lü蔓nüè”的錯誤顯示。
36 |
37 | ### V1.2 (2014.07.30)
38 | 精簡了簡繁、異體字轉換表的格式,減小了程序體積。
39 | 對漢字音數據庫做了如下修正:
40 | 1) 粵語:增加了一些字的異讀音(它們在粵語審音配詞字庫中被錯誤地標爲自身的異讀);
41 | 2) 日語:加粗了“双雙搭撘踏”幾字的吳音(它們本在常用漢字表中,但因爲歷史假名遣與漢音不同,原數據庫中未加粗);
42 | 3) 朝鮮語:去掉了“娘怒拏異异笝諾诺”幾字由頭音法則滋生的錯誤讀音;
43 | 4) 越南語:增加了“贇赟”兩字的讀音。
44 |
45 | ### V1.1 (2014.04.16)
46 | 在以“漢字”模式查詢,並勾選“簡繁、異體轉換”選項時,簡繁、異體字下方會用灰色括號顯示它對應着查詢的哪一個字。
47 |
48 | ### V1.0 (2014.04.02)
49 | 初始版本
50 |
--------------------------------------------------------------------------------
/assets/databases/mcpdict.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MaigoAkisame/MCPDict/30ca984e5fbf6b36073e6c43af7ad265ee052c78/assets/databases/mcpdict.zip
--------------------------------------------------------------------------------
/assets/help/context_menu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MaigoAkisame/MCPDict/30ca984e5fbf6b36073e6c43af7ad265ee052c78/assets/help/context_menu.png
--------------------------------------------------------------------------------
/assets/help/ex_da_ren.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MaigoAkisame/MCPDict/30ca984e5fbf6b36073e6c43af7ad265ee052c78/assets/help/ex_da_ren.png
--------------------------------------------------------------------------------
/assets/help/ex_gu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MaigoAkisame/MCPDict/30ca984e5fbf6b36073e6c43af7ad265ee052c78/assets/help/ex_gu.png
--------------------------------------------------------------------------------
/assets/help/ex_gwon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MaigoAkisame/MCPDict/30ca984e5fbf6b36073e6c43af7ad265ee052c78/assets/help/ex_gwon.png
--------------------------------------------------------------------------------
/assets/help/ex_hak.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MaigoAkisame/MCPDict/30ca984e5fbf6b36073e6c43af7ad265ee052c78/assets/help/ex_hak.png
--------------------------------------------------------------------------------
/assets/help/ex_le.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MaigoAkisame/MCPDict/30ca984e5fbf6b36073e6c43af7ad265ee052c78/assets/help/ex_le.png
--------------------------------------------------------------------------------
/assets/help/ex_liang.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MaigoAkisame/MCPDict/30ca984e5fbf6b36073e6c43af7ad265ee052c78/assets/help/ex_liang.png
--------------------------------------------------------------------------------
/assets/help/ex_xin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MaigoAkisame/MCPDict/30ca984e5fbf6b36073e6c43af7ad265ee052c78/assets/help/ex_xin.png
--------------------------------------------------------------------------------
/assets/help/favorite.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MaigoAkisame/MCPDict/30ca984e5fbf6b36073e6c43af7ad265ee052c78/assets/help/favorite.png
--------------------------------------------------------------------------------
/assets/help/ic_overflow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MaigoAkisame/MCPDict/30ca984e5fbf6b36073e6c43af7ad265ee052c78/assets/help/ic_overflow.png
--------------------------------------------------------------------------------
/bin/MCPDict.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MaigoAkisame/MCPDict/30ca984e5fbf6b36073e6c43af7ad265ee052c78/bin/MCPDict.apk
--------------------------------------------------------------------------------
/libs/android-sqlite-asset-helper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MaigoAkisame/MCPDict/30ca984e5fbf6b36073e6c43af7ad265ee052c78/libs/android-sqlite-asset-helper.jar
--------------------------------------------------------------------------------
/libs/android-support-v4.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MaigoAkisame/MCPDict/30ca984e5fbf6b36073e6c43af7ad265ee052c78/libs/android-support-v4.jar
--------------------------------------------------------------------------------
/proguard-project.txt:
--------------------------------------------------------------------------------
1 | # To enable ProGuard in your project, edit project.properties
2 | # to define the proguard.config property as described in that file.
3 | #
4 | # Add project specific ProGuard rules here.
5 | # By default, the flags in this file are appended to flags specified
6 | # in ${sdk.dir}/tools/proguard/proguard-android.txt
7 | # You can edit the include path and order by changing the ProGuard
8 | # include property in project.properties.
9 | #
10 | # For more details, see
11 | # http://developer.android.com/guide/developing/tools/proguard.html
12 |
13 | # Add any project specific keep options here:
14 |
15 | # If your project uses WebView with JS, uncomment the following
16 | # and specify the fully qualified class name to the JavaScript interface
17 | # class:
18 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
19 | # public *;
20 | #}
21 |
--------------------------------------------------------------------------------
/project.properties:
--------------------------------------------------------------------------------
1 | # This file is automatically generated by Android Tools.
2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED!
3 | #
4 | # This file must be checked in Version Control Systems.
5 | #
6 | # To customize properties used by the Ant build system edit
7 | # "ant.properties", and override values to adapt the script to your
8 | # project structure.
9 | #
10 | # To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
11 | #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
12 |
13 | # Project target.
14 | target=android-19
15 |
--------------------------------------------------------------------------------
/res/drawable/ic_alert.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MaigoAkisame/MCPDict/30ca984e5fbf6b36073e6c43af7ad265ee052c78/res/drawable/ic_alert.png
--------------------------------------------------------------------------------
/res/drawable/ic_cancel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MaigoAkisame/MCPDict/30ca984e5fbf6b36073e6c43af7ad265ee052c78/res/drawable/ic_cancel.png
--------------------------------------------------------------------------------
/res/drawable/ic_edit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MaigoAkisame/MCPDict/30ca984e5fbf6b36073e6c43af7ad265ee052c78/res/drawable/ic_edit.png
--------------------------------------------------------------------------------
/res/drawable/ic_error.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MaigoAkisame/MCPDict/30ca984e5fbf6b36073e6c43af7ad265ee052c78/res/drawable/ic_error.png
--------------------------------------------------------------------------------
/res/drawable/ic_info.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MaigoAkisame/MCPDict/30ca984e5fbf6b36073e6c43af7ad265ee052c78/res/drawable/ic_info.png
--------------------------------------------------------------------------------
/res/drawable/ic_question.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MaigoAkisame/MCPDict/30ca984e5fbf6b36073e6c43af7ad265ee052c78/res/drawable/ic_question.png
--------------------------------------------------------------------------------
/res/drawable/ic_search.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MaigoAkisame/MCPDict/30ca984e5fbf6b36073e6c43af7ad265ee052c78/res/drawable/ic_search.png
--------------------------------------------------------------------------------
/res/drawable/ic_star_white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MaigoAkisame/MCPDict/30ca984e5fbf6b36073e6c43af7ad265ee052c78/res/drawable/ic_star_white.png
--------------------------------------------------------------------------------
/res/drawable/ic_star_yellow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MaigoAkisame/MCPDict/30ca984e5fbf6b36073e6c43af7ad265ee052c78/res/drawable/ic_star_yellow.png
--------------------------------------------------------------------------------
/res/drawable/lang_ct.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MaigoAkisame/MCPDict/30ca984e5fbf6b36073e6c43af7ad265ee052c78/res/drawable/lang_ct.png
--------------------------------------------------------------------------------
/res/drawable/lang_jp_go.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MaigoAkisame/MCPDict/30ca984e5fbf6b36073e6c43af7ad265ee052c78/res/drawable/lang_jp_go.png
--------------------------------------------------------------------------------
/res/drawable/lang_jp_kan.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MaigoAkisame/MCPDict/30ca984e5fbf6b36073e6c43af7ad265ee052c78/res/drawable/lang_jp_kan.png
--------------------------------------------------------------------------------
/res/drawable/lang_jp_kwan.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MaigoAkisame/MCPDict/30ca984e5fbf6b36073e6c43af7ad265ee052c78/res/drawable/lang_jp_kwan.png
--------------------------------------------------------------------------------
/res/drawable/lang_jp_other.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MaigoAkisame/MCPDict/30ca984e5fbf6b36073e6c43af7ad265ee052c78/res/drawable/lang_jp_other.png
--------------------------------------------------------------------------------
/res/drawable/lang_jp_tou.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MaigoAkisame/MCPDict/30ca984e5fbf6b36073e6c43af7ad265ee052c78/res/drawable/lang_jp_tou.png
--------------------------------------------------------------------------------
/res/drawable/lang_kr.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MaigoAkisame/MCPDict/30ca984e5fbf6b36073e6c43af7ad265ee052c78/res/drawable/lang_kr.png
--------------------------------------------------------------------------------
/res/drawable/lang_mc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MaigoAkisame/MCPDict/30ca984e5fbf6b36073e6c43af7ad265ee052c78/res/drawable/lang_mc.png
--------------------------------------------------------------------------------
/res/drawable/lang_mn.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MaigoAkisame/MCPDict/30ca984e5fbf6b36073e6c43af7ad265ee052c78/res/drawable/lang_mn.png
--------------------------------------------------------------------------------
/res/drawable/lang_pu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MaigoAkisame/MCPDict/30ca984e5fbf6b36073e6c43af7ad265ee052c78/res/drawable/lang_pu.png
--------------------------------------------------------------------------------
/res/drawable/lang_sh.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MaigoAkisame/MCPDict/30ca984e5fbf6b36073e6c43af7ad265ee052c78/res/drawable/lang_sh.png
--------------------------------------------------------------------------------
/res/drawable/lang_vn.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MaigoAkisame/MCPDict/30ca984e5fbf6b36073e6c43af7ad265ee052c78/res/drawable/lang_vn.png
--------------------------------------------------------------------------------
/res/layout-land/dictionary_fragment.xml:
--------------------------------------------------------------------------------
1 |
10 |
11 |
14 |
15 |
17 |
21 |
23 |
24 |
25 |
27 |
31 |
33 |
34 |
35 |
38 |
42 |
47 |
48 |
49 |
51 |
56 |
57 |
58 |
61 |
66 |
67 |
68 |
69 |
70 |
76 |
77 |
84 |
85 |
86 |
--------------------------------------------------------------------------------
/res/layout/custom_search_view.xml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
12 |
13 |
23 |
24 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/res/layout/custom_spinner_dropdown_item.xml:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/res/layout/dictionary_fragment.xml:
--------------------------------------------------------------------------------
1 |
10 |
11 |
15 |
16 |
18 |
21 |
23 |
24 |
25 |
27 |
30 |
32 |
33 |
34 |
37 |
40 |
44 |
45 |
46 |
48 |
53 |
54 |
55 |
58 |
63 |
64 |
65 |
66 |
67 |
71 |
72 |
77 |
78 |
79 |
--------------------------------------------------------------------------------
/res/layout/favorite_fragment.xml:
--------------------------------------------------------------------------------
1 |
11 |
12 |
17 |
18 |
23 |
24 |
31 |
32 |
37 |
38 |
39 |
40 |
44 |
45 |
46 |
47 |
52 |
53 |
61 |
62 |
63 |
--------------------------------------------------------------------------------
/res/layout/favorite_item.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
13 |
14 |
21 |
22 |
27 |
28 |
34 |
35 |
42 |
43 |
52 |
53 |
58 |
59 |
60 |
61 |
69 |
70 |
74 |
75 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
--------------------------------------------------------------------------------
/res/layout/help_activity.xml:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/res/layout/main_activity.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
11 |
12 |
16 |
17 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/res/layout/search_result_fragment.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
13 |
14 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/res/layout/search_result_item.xml:
--------------------------------------------------------------------------------
1 |
11 |
12 |
17 |
18 |
25 |
26 |
31 |
32 |
37 |
38 |
43 |
44 |
45 |
46 |
52 |
53 |
55 |
58 |
63 |
68 |
74 |
78 |
79 |
80 |
81 |
83 |
86 |
90 |
93 |
97 |
98 |
99 |
101 |
104 |
108 |
111 |
115 |
116 |
117 |
119 |
122 |
126 |
129 |
133 |
134 |
135 |
137 |
140 |
144 |
147 |
151 |
152 |
153 |
155 |
158 |
162 |
165 |
169 |
170 |
171 |
173 |
176 |
180 |
181 |
182 |
183 |
184 |
189 |
190 |
191 |
--------------------------------------------------------------------------------
/res/layout/settings_activity.xml:
--------------------------------------------------------------------------------
1 |
9 |
--------------------------------------------------------------------------------
/res/menu/favorite_manage_popup_menu.xml:
--------------------------------------------------------------------------------
1 |
13 |
--------------------------------------------------------------------------------
/res/menu/main_menu.xml:
--------------------------------------------------------------------------------
1 |
13 |
--------------------------------------------------------------------------------
/res/menu/search_result_context_menu.xml:
--------------------------------------------------------------------------------
1 |
89 |
--------------------------------------------------------------------------------
/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MaigoAkisame/MCPDict/30ca984e5fbf6b36073e6c43af7ad265ee052c78/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MaigoAkisame/MCPDict/30ca984e5fbf6b36073e6c43af7ad265ee052c78/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MaigoAkisame/MCPDict/30ca984e5fbf6b36073e6c43af7ad265ee052c78/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MaigoAkisame/MCPDict/30ca984e5fbf6b36073e6c43af7ad265ee052c78/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/raw/orthography_ct_finals.tsv:
--------------------------------------------------------------------------------
1 | #Jyutping, CantonesePinyin, Yale, SidneyLau
2 | aa aa a a
3 | aai aai aai aai
4 | aau aau aau aau
5 | aam aam aam aam
6 | aan aan aan aan
7 | aang aang aang aang
8 | aap aap aap aap
9 | aat aat aat aat
10 | aak aak aak aak
11 | ai ai ai ai
12 | au au au au
13 | am am am am
14 | an an an an
15 | ang ang ang ang
16 | ap ap ap ap
17 | at at at at
18 | ak ak ak ak
19 | e e e e
20 | ei ei ei ei
21 | eu eu eo eu #仅限“調、掉”二字异读。耶鲁与刘锡祥式拼音中无此韵母。刘锡祥式拼音暂用eu。耶鲁式拼音中eu已被“靴”字的韵母占用,暂用eo。
22 | em em em em #仅限“舐”字异读,耶鲁与刘锡祥式拼音暂用em
23 | eng eng eng eng
24 | ep ep ep ep #仅限“夾”字异读,耶鲁与刘锡祥式拼音暂用ep
25 | ek ek ek ek
26 | i i i i
27 | iu iu iu iu
28 | im im im im
29 | in in in in
30 | ing ing ing ing
31 | ip ip ip ip
32 | it it it it
33 | ik ik ik ik
34 | o o o oh
35 | oi oi oi oi
36 | ou ou ou o
37 | on on on on
38 | ong ong ong ong
39 | ot ot ot ot
40 | ok ok ok ok
41 | oe oe eu euh
42 | oeng oeng eung eung
43 | oek oek euk euk
44 | eoi oey eui ui
45 | eon oen eun un
46 | eot oet eut ut
47 | u u u oo
48 | ui ui ui ooi
49 | un un un oon
50 | ung ung ung ung
51 | ut ut ut oot
52 | uk uk uk uk
53 | yu y yu ue
54 | yun yn yun uen
55 | yut yt yut uet
56 | m m m m
57 | ng ng ng ng
58 |
--------------------------------------------------------------------------------
/res/raw/orthography_ct_initials.tsv:
--------------------------------------------------------------------------------
1 | #Jyutping, CantonesePinyin, Yale, SidneyLau
2 | b b b b
3 | c ts ch ch
4 | d d d d
5 | f f f f
6 | g g g g
7 | gw gw gw gw
8 | h h h h
9 | j j y y
10 | k k k k
11 | kw kw kw kw
12 | l l l l
13 | m m m m
14 | n n n n
15 | ng ng ng ng
16 | p p p p
17 | s s s s
18 | t t t t
19 | w w w w
20 | z dz j j
21 |
--------------------------------------------------------------------------------
/res/raw/orthography_jp.tsv:
--------------------------------------------------------------------------------
1 | #Hiragana, Katakana, Nippon-shiki, Hepburn-shiki
2 | あ ア a a
3 | い イ i i
4 | う ウ u u
5 | え エ e e
6 | お オ o o
7 | や ヤ ya ya
8 | ゆ ユ yu yu
9 | よ ヨ yo yo
10 | か カ ka ka
11 | き キ ki ki
12 | く ク ku ku
13 | け ケ ke ke
14 | こ コ ko ko
15 | きゃ キャ kya kya
16 | きゅ キュ kyu kyu
17 | きょ キョ kyo kyo
18 | さ サ sa sa
19 | し シ si shi
20 | す ス su su
21 | せ セ se se
22 | そ ソ so so
23 | しゃ シャ sya sha
24 | しゅ シュ syu shu
25 | しょ ショ syo sho
26 | た タ ta ta
27 | ち チ ti chi
28 | つ ツ tu tsu
29 | て テ te te
30 | と ト to to
31 | ちゃ チャ tya cha
32 | ちゅ チュ tyu chu
33 | ちょ チョ tyo cho
34 | な ナ na na
35 | に ニ ni ni
36 | ぬ ヌ nu nu
37 | ね ネ ne ne
38 | の ノ no no
39 | にゃ ニャ nya nya
40 | にゅ ニュ nyu nyu
41 | にょ ニョ nyo nyo
42 | は ハ ha ha
43 | ひ ヒ hi hi
44 | ふ フ hu fu
45 | へ ヘ he he
46 | ほ ホ ho ho
47 | ひゃ ヒャ hya hya
48 | ひゅ ヒュ hyu hyu
49 | ひょ ヒョ hyo hyo
50 | ま マ ma ma
51 | み ミ mi mi
52 | む ム mu mu
53 | め メ me me
54 | も モ mo mo
55 | みゃ ミャ mya mya
56 | みゅ ミュ myu myu
57 | みょ ミョ myo myo
58 | ら ラ ra ra
59 | り リ ri ri
60 | る ル ru ru
61 | れ レ re re
62 | ろ ロ ro ro
63 | りゃ リャ rya rya
64 | りゅ リュ ryu ryu
65 | りょ リョ ryo ryo
66 | わ ワ wa wa
67 | ゐ ヰ wi wi
68 | ゑ ヱ we we
69 | を ヲ wo wo
70 | ゐゃ ヰャ wya wya
71 | ゐゅ ヰュ wyu wyu
72 | ゐょ ヰョ wyo wyo
73 | が ガ ga ga
74 | ぎ ギ gi gi
75 | ぐ グ gu gu
76 | げ ゲ ge ge
77 | ご ゴ go go
78 | ぎゃ ギャ gya gya
79 | ぎゅ ギュ gyu gyu
80 | ぎょ ギョ gyo gyo
81 | ざ ザ za za
82 | じ ジ zi ji
83 | ず ズ zu zu
84 | ぜ ゼ ze ze
85 | ぞ ゾ zo zo
86 | じゃ ジャ zya ja
87 | じゅ ジュ zyu ju
88 | じょ ジョ zyo jo
89 | だ ダ da da
90 | ぢ ヂ di dji
91 | づ ヅ du dzu
92 | で デ de de
93 | ど ド do do
94 | ぢゃ ヂャ dya dja
95 | ぢゅ ヂュ dyu dju
96 | ぢょ ヂョ dyo djo
97 | ば バ ba ba
98 | び ビ bi bi
99 | ぶ ブ bu bu
100 | べ ベ be be
101 | ぼ ボ bo bo
102 | びゃ ビャ bya bya
103 | びゅ ビュ byu byu
104 | びょ ビョ byo byo
105 | ぱ パ pa pa
106 | ぴ ピ pi pi
107 | ぷ プ pu pu
108 | ぺ ペ pe pe
109 | ぽ ポ po po
110 | ぴゃ ピャ pya pya
111 | ぴゅ ピュ pyu pyu
112 | ぴょ ピョ pyo pyo
113 | くゎ クヮ kwa kwa
114 | くゐ クヰ kwi kwi
115 | くゑ クヱ kwe kwe
116 | くを クヲ kwo kwo
117 | くゐゃ クヰャ kwya kwya
118 | くゐゅ クヰュ kwyu kwyu
119 | くゐょ クヰョ kwyo kwyo
120 | ぐゎ グヮ gwa gwa
121 | ぐゐ グヰ gwi gwi
122 | ぐゑ グヱ gwe gwe
123 | ぐを グヲ gwo gwo
124 | ぐゐゃ グヰャ gwya gwya
125 | ぐゐゅ グヰュ gwyu gwyu
126 | ぐゐょ グヰョ gwyo gwyo
127 | ん ン n n
128 | っ ッ q q
129 |
--------------------------------------------------------------------------------
/res/raw/orthography_mc_bieng_sjyix.tsv:
--------------------------------------------------------------------------------
1 | #平水韻韻目,廣韻韻目
2 | 上平1東 東
3 | 上平2冬 冬鍾
4 | 上平3江 江
5 | 上平4支 支脂之
6 | 上平5微 微
7 | 上平6魚 魚
8 | 上平7虞 虞模
9 | 上平8齊 齊
10 | 上平9佳 佳皆
11 | 上平10灰 灰咍
12 | 上平11眞 眞諄臻
13 | 上平12文 文欣
14 | 上平13元 元魂痕
15 | 上平14寒 寒桓
16 | 上平15刪 刪山
17 | 下平1先 先仙
18 | 下平2蕭 蕭宵
19 | 下平3肴 肴
20 | 下平4豪 豪
21 | 下平5歌 歌戈
22 | 下平6麻 麻
23 | 下平7陽 陽唐
24 | 下平8庚 庚耕清
25 | 下平9青 青
26 | 下平10蒸 蒸登
27 | 下平11尤 尤侯幽
28 | 下平12侵 侵
29 | 下平13覃 覃談
30 | 下平14鹽 鹽添
31 | 下平15咸 咸銜嚴凡
32 | 上声1董 董
33 | 上声2腫 湩腫
34 | 上声3講 講
35 | 上声4紙 紙旨止
36 | 上声5尾 尾
37 | 上声6語 語
38 | 上声7麌 麌姥
39 | 上声8薺 薺
40 | 上声9蟹 蟹駭
41 | 上声10賄 賄海
42 | 上声11軫 軫準齔
43 | 上声12吻 吻隱
44 | 上声13阮 阮混很
45 | 上声14旱 旱緩
46 | 上声15潸 潸産
47 | 上声16銑 銑獮
48 | 上声17篠 篠小
49 | 上声18巧 巧
50 | 上声19皓 晧 #廣韻作晧,但平水韻一般作皓
51 | 上声20哿 哿果
52 | 上声21馬 馬
53 | 上声22養 養蕩
54 | 上声23梗 梗耿靜
55 | 上声24迥 迥拯等
56 | 上声25有 有厚黝
57 | 上声26寢 寑 #廣韻作寑,但平水韻一般作寢
58 | 上声27感 感敢
59 | 上声28琰 琰忝儼
60 | 上声29豏 豏檻范
61 | 去声1送 送
62 | 去声2宋 宋用
63 | 去声3絳 絳
64 | 去声4寘 寘至志
65 | 去声5未 未
66 | 去声6御 御
67 | 去声7遇 遇暮
68 | 去声8霽 霽祭
69 | 去声9泰 泰
70 | 去声10卦 卦怪夬
71 | 去声11隊 隊代廢
72 | 去声12震 震稕櫬
73 | 去声13問 問焮
74 | 去声14願 願慁恨
75 | 去声15翰 翰換
76 | 去声16諫 諫襉
77 | 去声17霰 霰線
78 | 去声18嘯 嘯笑
79 | 去声19效 效
80 | 去声20號 号 #廣韻作号,但平水韻一般作號
81 | 去声21箇 箇過
82 | 去声22禡 禡
83 | 去声23漾 漾宕
84 | 去声24敬 映諍勁 #廣韻韻目爲映,但平水韻韻目爲敬
85 | 去声25徑 徑證嶝
86 | 去声26宥 宥候幼
87 | 去声27沁 沁
88 | 去声28勘 勘闞
89 | 去声29豔 豔㮇釅
90 | 去声30陷 陷鑑梵
91 | 入声1屋 屋
92 | 入声2沃 沃燭
93 | 入声3覺 覺
94 | 入声4質 質術櫛
95 | 入声5物 物迄
96 | 入声6月 月沒麧
97 | 入声7曷 曷末
98 | 入声8黠 黠鎋
99 | 入声9屑 屑薛
100 | 入声10藥 藥鐸
101 | 入声11陌 陌麥昔
102 | 入声12錫 錫
103 | 入声13職 職德
104 | 入声14緝 緝
105 | 入声15合 合盍
106 | 入声16葉 葉怗
107 | 入声17洽 洽狎業乏
108 |
--------------------------------------------------------------------------------
/res/raw/orthography_mc_finals.tsv:
--------------------------------------------------------------------------------
1 | #韻母拼音,攝,等,呼,韻母名稱(平上去入)
2 | a 果 一 開 歌哿箇
3 | ua 果 一 合 戈果過
4 | Ia 果 三 開 戈果過 #与假攝麻韻重韻,但較少見,用Ia代替
5 | ya 果 三 合 戈果過
6 | ra 假 二 開 麻馬禡
7 | rua 假 二 合 麻馬禡
8 | ia 假 三 開 麻馬禡
9 | ai 蟹 一 開 咍海代
10 | uai 蟹 一 合 灰賄隊
11 | rai 蟹 二 開 皆駭怪
12 | ruai 蟹 二 合 皆駭怪
13 | re 蟹 二 開 佳蟹卦
14 | rue 蟹 二 合 佳蟹卦
15 | e 蟹 四 開 齊薺霽
16 | ue 蟹 四 合 齊薺霽
17 | ad 蟹 一 開 泰
18 | uad 蟹 一 合 泰
19 | rad 蟹 二 開 夬
20 | ruad 蟹 二 合 夬
21 | iad 蟹 三 開 廢
22 | yad 蟹 三 合 廢
23 | ied 蟹 三 開 祭
24 | yed 蟹 三 合 祭
25 | au 效 一 開 豪晧号
26 | rau 效 二 開 肴巧效
27 | ieu 效 三 開 宵小笑
28 | eu 效 四 開 蕭篠嘯
29 | ie 止 三 開 支紙寘
30 | ye 止 三 合 支紙寘
31 | ii 止 三 開 脂旨至
32 | yi 止 三 合 脂旨至
33 | i 止 三 開 之止志
34 | ioi 止 三 開 微尾未
35 | yoi 止 三 合 微尾未
36 | o 遇 一 開 模姥暮
37 | io 遇 三 開 魚語御
38 | yo 遇 三 合 虞麌遇
39 | u 流 一 開 侯厚候
40 | iu 流 三 開 尤有宥
41 | y 流 三 開 幽黝幼
42 | ang 宕 一 開 唐蕩宕鐸
43 | uang 宕 一 合 唐蕩宕鐸
44 | iang 宕 三 開 陽養漾藥
45 | yang 宕 三 合 陽養漾藥
46 | rang 梗 二 開 庚梗映陌
47 | ruang 梗 二 合 庚梗映陌
48 | reng 梗 二 開 耕耿諍麥
49 | rueng 梗 二 合 耕耿諍麥
50 | Ieng 梗 三 開 庚梗映陌 #与清韻重韻,只拼幫、見組重紐B類与莊組,用Ieng代替
51 | Yeng 梗 三 合 庚梗映陌 #与清韻重韻,只拼幫、見組重紐B類与莊組,用Yeng代替
52 | ieng 梗 三 開 清靜勁昔
53 | yeng 梗 三 合 清靜勁昔
54 | eng 梗 四 開 青迥徑錫
55 | ueng 梗 四 合 青迥徑錫
56 | ong 曾 一 開 登等嶝德
57 | uong 曾 一 合 登等嶝德
58 | ing 曾 三 開 蒸拯證職
59 | yng 曾 三 合 蒸拯證職 #其實只有入声
60 | ung 通 一 開 東董送屋
61 | uung 通 一 開 冬湩宋沃 #湩韻字少,廣韻中歸腫韻,依韻典網單列
62 | yung 通 三 開 鍾腫用燭
63 | iung 通 三 開 東董送屋
64 | rung 江 二 開 江講絳覺
65 | an 山 一 開 寒旱翰曷
66 | uan 山 一 合 桓緩換末
67 | ran 山 二 開 刪潸諫鎋
68 | ruan 山 二 合 刪潸諫鎋
69 | ren 山 二 開 山産襉黠
70 | ruen 山 二 合 山産襉黠
71 | ian 山 三 開 元阮願月
72 | yan 山 三 合 元阮願月
73 | ien 山 三 開 仙獮線薛
74 | yen 山 三 合 仙獮線薛
75 | en 山 四 開 先銑霰屑
76 | uen 山 四 合 先銑霰屑
77 | on 臻 一 開 痕很恨麧 #麧韻字少,廣韻中歸沒韻,依韻典網單列
78 | uon 臻 一 合 魂混慁沒
79 | ion 臻 三 開 欣隱焮迄
80 | yon 臻 三 合 文吻問物
81 | in 臻 三 開 眞軫震質
82 | yn 臻 三 合 諄準稕術
83 | In 臻 三 開 臻齔櫬櫛 #与眞韻重韻,只拼莊組,用In代替。齔韻字少,廣韻中歸隱韻,韻典網單列作𧤛韻,但此字不在BMP內,以齔字代替。櫬韻字少,廣韻中歸焮韻,依韻典網單列
84 | Yn 臻 三 合 眞軫震質 #与諄韻重韻,只拼幫、見組重紐B類,用Yn代替
85 | am 咸 一 開 談敢闞盍
86 | om 咸 一 開 覃感勘合
87 | ram 咸 二 開 銜檻鑑狎
88 | rem 咸 二 開 咸豏陷洽
89 | iam 咸 三 開 嚴儼釅業
90 | yam 咸 三 合 凡范梵乏
91 | iem 咸 三 開 鹽琰豔葉
92 | em 咸 四 開 添忝㮇怗
93 | im 深 三 開 侵寑沁緝
94 |
--------------------------------------------------------------------------------
/res/raw/orthography_mc_initials.tsv:
--------------------------------------------------------------------------------
1 | #声母拼音,声母名稱
2 | p 幫
3 | ph 滂
4 | b 並
5 | m 明
6 | t 端
7 | th 透
8 | d 定
9 | n 泥
10 | tr 知
11 | thr 徹
12 | dr 澄
13 | nr 娘
14 | l 來
15 | c 精
16 | ch 清
17 | z 從
18 | s 心
19 | zs 邪
20 | cr 莊
21 | chr 初
22 | zr 崇
23 | sr 生
24 | zsr 俟
25 | cj 章
26 | chj 昌
27 | zj 常
28 | sj 書
29 | zsj 船
30 | nj 日
31 | k 見
32 | kh 谿
33 | g 羣
34 | ng 疑
35 | h 曉
36 | gh 匣
37 | q 影
38 | _ 云 #拼作零声母
39 | j 以
40 |
--------------------------------------------------------------------------------
/res/raw/orthography_pu_bopomofo.tsv:
--------------------------------------------------------------------------------
1 | #注音符號,聲母或韻母,整體認讀音節
2 | ㄅ b
3 | ㄆ p
4 | ㄇ m m
5 | ㄈ f
6 | ㄉ d
7 | ㄊ t
8 | ㄋ n n
9 | ㄌ l
10 | ㄍ g
11 | ㄎ k
12 | ㄏ h
13 | ㄐ j
14 | ㄑ q
15 | ㄒ x
16 | ㄓ zh zhi
17 | ㄔ ch chi
18 | ㄕ sh shi
19 | ㄖ r ri
20 | ㄗ z zi
21 | ㄘ c ci
22 | ㄙ s si
23 | ㄫ ng ng
24 | ㄚ a a
25 | ㄛ o o
26 | ㄜ e e
27 | ㄧ i yi
28 | ㄨ u wu
29 | ㄩ v yu
30 | ㄞ ai ai
31 | ㄟ ei ei
32 | ㄠ ao ao
33 | ㄡ ou ou
34 | ㄢ an an
35 | ㄣ en en
36 | ㄤ ang ang
37 | ㄥ eng eng
38 | ㄦ er er
39 | ㄧㄚ ia ya
40 | ㄧㄛ io yo
41 | ㄧㄝ ie ye
42 | ㄧㄠ iao yao
43 | ㄧㄡ iu you
44 | ㄧㄢ ian yan
45 | ㄧㄣ in yin
46 | ㄧㄤ iang yang
47 | ㄧㄥ ing ying
48 | ㄨㄚ ua wa
49 | ㄨㄛ uo wo
50 | ㄨㄞ uai wai
51 | ㄨㄟ ui wei
52 | ㄨㄢ uan wan
53 | ㄨㄣ un wen
54 | ㄨㄤ uang wang
55 | ㄨㄥ ong weng
56 | ㄩㄝ ve yue
57 | ㄩㄢ van yuan
58 | ㄩㄣ vn yun
59 | ㄩㄥ iong yong
60 | ˊ 2
61 | ˇ 3
62 | ˋ 4
63 | ˙ _
64 |
--------------------------------------------------------------------------------
/res/raw/orthography_pu_pinyin.tsv:
--------------------------------------------------------------------------------
1 | #Combined, base, tone
2 | ā a 1
3 | á a 2
4 | ǎ a 3
5 | à a 4
6 | ō o 1
7 | ó o 2
8 | ǒ o 3
9 | ò o 4
10 | ē e 1
11 | é e 2
12 | ě e 3
13 | è e 4
14 | ī i 1
15 | í i 2
16 | ǐ i 3
17 | ì i 4
18 | ū u 1
19 | ú u 2
20 | ǔ u 3
21 | ù u 4
22 | ǖ v 1
23 | ǘ v 2
24 | ǚ v 3
25 | ǜ v 4
26 | ü v _
27 | ḿ m 2
28 | ń n 2
29 | ň n 3
30 | ǹ n 4
31 | ̄ _ 1
32 | ́ _ 2
33 | ̌ _ 3
34 | ̀ _ 4
35 |
--------------------------------------------------------------------------------
/res/raw/orthography_vn.tsv:
--------------------------------------------------------------------------------
1 | #Combined, base, tone
2 | à a f
3 | ả a r
4 | ã a x
5 | á a s
6 | ạ a j
7 | ă aw _
8 | ằ aw f
9 | ẳ aw r
10 | ẵ aw x
11 | ắ aw s
12 | ặ aw j
13 | â aa _
14 | ầ aa f
15 | ẩ aa r
16 | ẫ aa x
17 | ấ aa s
18 | ậ aa j
19 | è e f
20 | ẻ e r
21 | ẽ e x
22 | é e s
23 | ẹ e j
24 | ê ee _
25 | ề ee f
26 | ể ee r
27 | ễ ee x
28 | ế ee s
29 | ệ ee j
30 | ì i f
31 | ỉ i r
32 | ĩ i x
33 | í i s
34 | ị i j
35 | ò o f
36 | ỏ o r
37 | õ o x
38 | ó o s
39 | ọ o j
40 | ô oo _
41 | ồ oo f
42 | ổ oo r
43 | ỗ oo x
44 | ố oo s
45 | ộ oo j
46 | ơ ow _
47 | ờ ow f
48 | ở ow r
49 | ỡ ow x
50 | ớ ow s
51 | ợ ow j
52 | ù u f
53 | ủ u r
54 | ũ u x
55 | ú u s
56 | ụ u j
57 | ư uw _
58 | ừ uw f
59 | ử uw r
60 | ữ uw x
61 | ứ uw s
62 | ự uw j
63 | ỳ y f
64 | ỷ y r
65 | ỹ y x
66 | ý y s
67 | ỵ y j
68 | đ dd _
69 |
--------------------------------------------------------------------------------
/res/values-land/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 查詢:
5 | 模式:
6 | 選項:
7 |
8 |
9 |
--------------------------------------------------------------------------------
/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 漢字古今中外讀音查詢
5 | 設置
6 | 幫助
7 | 關於
8 | 漢字古今中外讀音查詢 V3.0\n2016.05.03\n\n作者:Maigo\nmaigoakisame@gmail.com
9 |
10 | 確定
11 | 取消
12 | 返回
13 | 下一步
14 | 保存
15 | 保留
16 | 刪除
17 | 管理
18 | 導出
19 | 導入
20 | 清空
21 | 覆蓋
22 |
23 | 字典
24 | 生字本
25 |
26 | 輸入漢字或讀音
27 | 查詢內容:
28 | 查詢模式:
29 | 查詢選項:
30 | 僅在《廣韻》內查找
31 | 簡繁、異體轉換
32 | 聲調不敏感
33 | 未找到符合條件的漢字。
34 |
35 |
36 | - 漢字
37 | - 中古拼音
38 | - 普通話
39 | - 粵語
40 | - 吳語(上海話)
41 | - 閩南語
42 | - 朝鮮語
43 | - 越南語
44 | - 日語吳音
45 | - 日語漢音
46 | - 日語(任意讀音)
47 |
48 |
49 | KUANGX_YONH_ONLY
50 | ALLOW_VARIANTS
51 | TONE_INSENSITIVE
52 |
53 | 複製“%c”字或其讀音
54 | 去在線詞典查詢“%c”字
55 |
56 | 複製“%c”字
57 | 複製“%c”字的Unicode編碼
58 | 複製“%c”字及其所有讀音
59 | 複製“%c”字的中古漢語讀音
60 | 複製“%c”字的普通話讀音
61 | 複製“%c”字的粵語讀音
62 | 複製“%c”字的吳語(上海話)讀音
63 | 複製“%c”字的閩南語讀音
64 | 複製“%c”字的朝鮮語讀音
65 | 複製“%c”字的越南語讀音
66 | 複製“%c”字的所有日語讀音
67 | 複製“%c”字的日語吳音
68 | 複製“%c”字的日語漢音
69 | 複製“%c”字的日語唐音
70 | 複製“%c”字的日語慣用音
71 | 複製“%c”字的日語其他讀音
72 |
73 | 已將 %s 複製到剪貼板
74 |
75 | 去 韻典網 查詢“%c”字
76 | 去 漢典網 查詢“%c”字
77 | 去 粵語審音配詞字庫 查詢“%c”字
78 | 去 吳音小字典 查詢“%c”字
79 | 去 臺灣閩南語常用詞辭典 查詢“%c”字
80 | 去 Naver漢字辭典 查詢“%c”字
81 | 去 漢越辭典摘引 查詢“%c”字
82 |
83 | 當前生字本中共有 %d 個生字
84 | 當前生字本中沒有生字。\n\n在“字典”頁面,點擊查詢結果右上角的五角星,可以把相應的漢字加入生字本中。\n\n若您備份過生字本,可以按此導入。
85 |
86 | 把“%c”字加入生字本
87 | 寫點筆記吧…
88 | 已將“%c”字加入生字本
89 |
90 | 查看“%c”字的筆記
91 | 編輯“%c”字的筆記
92 | 編輯“%c”字\n的筆記
93 | 查看或編輯“%c”字的筆記
94 | 已保存“%c”字的筆記
95 |
96 | 把“%c”字從生字本中刪除
97 | 從生字本中\n刪除“%c”字
98 | 確定把“%c”字從生字本中刪除嗎?刪除後無法恢復!
99 | 1小時內不再提示
100 | 已將“%c”字從生字本中刪除
101 |
102 | 把生字本導出到文件
103 | 已將生字本導出到備份文件 %s。\n\n今後,您可以將此文件複製到其它手機上的同一位置,並導入進那臺手機上的生字本。
104 | 生字本備份文件 %1$s 已存在,最後修改於 %2$s。\n\n要覆蓋它嗎?
105 |
106 | 把文件導入到生字本
107 | 未找到生字本備份文件 %s。\n\n請確認文件存在,再試一次。
108 | 讀取生字本備份文件 %s 失敗。文件可能已損壞。
109 | 找到了生字本備份文件 %s,但其中並不含有生字。
110 | 找到了生字本備份文件 %1$s,其中含有 %2$d 條生字。\n\n按“導入”按鈕可將這些生字導入進生字本。
111 | 找到了生字本備份文件 %1$s,其中含有 %2$d 條生字。\n\n在下一步,請仔細閱讀並選擇一種導入模式。
112 | 選擇導入模式
113 |
114 | - 捨棄生字本原有內容,僅保留導入的內容。
115 | - 保留生字本原有內容,將導入的內容按時間戳與原有內容混排。
116 | - 保留生字本原有內容,將導入內容的時間戳修改爲當前時間,導入的內容將排在原有內容之前。
117 |
118 | 已將備份文件 %s 的內容導入進生字本。\n\n是否刪除備份文件?
119 | 已刪除生字本備份文件。
120 | 刪除失敗,文件仍存在
121 |
122 | 清空生字本
123 | 確定清空生字本嗎?清空後無法恢復!
124 | 已清空生字本
125 |
126 | 操作失敗
127 | 已將錯誤信息保存至文件 %s。\n\n請與作者 Maigo (maigoakisame@gmail.com) 聯繫。
128 | 保存錯誤信息也失敗了 (T_T)\n\n有可能是您的手機沒有SD卡,SD卡已滿,或無法寫入。
129 |
130 | FAVORITE_DELETE_NO_CONFIRM_EXPIRY
131 |
132 | MANDARIN_DISPLAY
133 | 普通話顯示方式
134 |
135 | - 漢語拼音
136 | - 注音符號
137 |
138 |
139 | CANTONESE_ROMANIZATION
140 | 粵語拼音系統(用於輸入和顯示)
141 |
142 | - 香港語言學學會式(粵拼)
143 | - 教院式
144 | - 耶魯式
145 | - 劉錫祥式
146 |
147 |
148 | KOREAN_DISPLAY
149 | 朝鮮語顯示方式
150 |
151 | - 諺文
152 | - 文觀部2000年式羅馬字
153 |
154 |
155 | VIETNAMESE_TONE_POSITION
156 | 越南語聲調標記方式
157 |
158 | - 舊式(hòa, thủy)
159 | - 新式(hoà, thuỷ)
160 |
161 |
162 | JAPANESE_DISPLAY
163 | 日語顯示方式
164 |
165 | - 平假名
166 | - 片假名
167 | - 日本式羅馬字
168 | - 黑本式羅馬字
169 |
170 |
171 |
172 |
--------------------------------------------------------------------------------
/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
9 |
16 |
17 |
18 |
25 |
26 |
30 |
31 |
35 |
36 |
39 |
40 |
45 |
46 |
49 |
50 |
51 |
55 |
56 |
60 |
61 |
65 |
66 |
70 |
71 |
75 |
76 |
77 |
86 |
87 |
88 |
93 |
94 |
95 |
--------------------------------------------------------------------------------
/res/xml/preferences.xml:
--------------------------------------------------------------------------------
1 |
3 |
9 |
15 |
21 |
27 |
33 |
34 |
--------------------------------------------------------------------------------
/src/com/mobiRic/ui/widget/Boast.java:
--------------------------------------------------------------------------------
1 | // Cancel the last toast (if it still there) before displaying the next
2 | // Reference: http://stackoverflow.com/a/16103514
3 |
4 | package com.mobiRic.ui.widget;
5 |
6 | import android.annotation.SuppressLint;
7 | import android.content.Context;
8 | import android.content.res.Resources;
9 | import android.widget.Toast;
10 |
11 | /**
12 | * {@link Toast} decorator allowing for easy cancellation of notifications. Use
13 | * this class if you want subsequent Toast notifications to overwrite current
14 | * ones.
15 | *
16 | * By default, a current {@link Boast} notification will be cancelled by a
17 | * subsequent notification. This default behaviour can be changed by calling
18 | * certain methods like {@link #show(boolean)}.
19 | */
20 | public class Boast
21 | {
22 | /**
23 | * Keeps track of certain {@link Boast} notifications that may need to be cancelled.
24 | * This functionality is only offered by some of the methods in this class.
25 | */
26 | private volatile static Boast globalBoast = null;
27 |
28 | // ////////////////////////////////////////////////////////////////////////////////////////////////////////
29 |
30 | /**
31 | * Internal reference to the {@link Toast} object that will be displayed.
32 | */
33 | private Toast internalToast;
34 |
35 | // ////////////////////////////////////////////////////////////////////////////////////////////////////////
36 |
37 | /**
38 | * Private constructor creates a new {@link Boast} from a given
39 | * {@link Toast}.
40 | *
41 | * @throws NullPointerException
42 | * if the parameter is null
.
43 | */
44 | private Boast(Toast toast)
45 | {
46 | // null check
47 | if (toast == null)
48 | {
49 | throw new NullPointerException(
50 | "Boast.Boast(Toast) requires a non-null parameter.");
51 | }
52 |
53 | internalToast = toast;
54 | }
55 |
56 | // ////////////////////////////////////////////////////////////////////////////////////////////////////////
57 |
58 | /**
59 | * Make a standard {@link Boast} that just contains a text view.
60 | *
61 | * @param context
62 | * The context to use. Usually your {@link android.app.Application}
63 | * or {@link android.app.Activity} object.
64 | * @param text
65 | * The text to show. Can be formatted text.
66 | * @param duration
67 | * How long to display the message. Either {@link #LENGTH_SHORT} or
68 | * {@link #LENGTH_LONG}
69 | */
70 | @SuppressLint("ShowToast")
71 | public static Boast makeText(Context context, CharSequence text,
72 | int duration)
73 | {
74 | return new Boast(Toast.makeText(context, text, duration));
75 | }
76 |
77 | /**
78 | * Make a standard {@link Boast} that just contains a text view with the
79 | * text from a resource.
80 | *
81 | * @param context
82 | * The context to use. Usually your {@link android.app.Application}
83 | * or {@link android.app.Activity} object.
84 | * @param resId
85 | * The resource id of the string resource to use. Can be formatted
86 | * text.
87 | * @param duration
88 | * How long to display the message. Either {@link #LENGTH_SHORT} or
89 | * {@link #LENGTH_LONG}
90 | *
91 | * @throws Resources.NotFoundException
92 | * if the resource can't be found.
93 | */
94 | @SuppressLint("ShowToast")
95 | public static Boast makeText(Context context, int resId, int duration)
96 | throws Resources.NotFoundException
97 | {
98 | return new Boast(Toast.makeText(context, resId, duration));
99 | }
100 |
101 | /**
102 | * Make a standard {@link Boast} that just contains a text view. Duration
103 | * defaults to {@link #LENGTH_SHORT}.
104 | *
105 | * @param context
106 | * The context to use. Usually your {@link android.app.Application}
107 | * or {@link android.app.Activity} object.
108 | * @param text
109 | * The text to show. Can be formatted text.
110 | */
111 | @SuppressLint("ShowToast")
112 | public static Boast makeText(Context context, CharSequence text)
113 | {
114 | return new Boast(Toast.makeText(context, text, Toast.LENGTH_SHORT));
115 | }
116 |
117 | /**
118 | * Make a standard {@link Boast} that just contains a text view with the
119 | * text from a resource. Duration defaults to {@link #LENGTH_SHORT}.
120 | *
121 | * @param context
122 | * The context to use. Usually your {@link android.app.Application}
123 | * or {@link android.app.Activity} object.
124 | * @param resId
125 | * The resource id of the string resource to use. Can be formatted
126 | * text.
127 | *
128 | * @throws Resources.NotFoundException
129 | * if the resource can't be found.
130 | */
131 | @SuppressLint("ShowToast")
132 | public static Boast makeText(Context context, int resId)
133 | throws Resources.NotFoundException
134 | {
135 | return new Boast(Toast.makeText(context, resId, Toast.LENGTH_SHORT));
136 | }
137 |
138 | // ////////////////////////////////////////////////////////////////////////////////////////////////////////
139 |
140 | /**
141 | * Show a standard {@link Boast} that just contains a text view.
142 | *
143 | * @param context
144 | * The context to use. Usually your {@link android.app.Application}
145 | * or {@link android.app.Activity} object.
146 | * @param text
147 | * The text to show. Can be formatted text.
148 | * @param duration
149 | * How long to display the message. Either {@link #LENGTH_SHORT} or
150 | * {@link #LENGTH_LONG}
151 | */
152 | public static void showText(Context context, CharSequence text, int duration)
153 | {
154 | Boast.makeText(context, text, duration).show();
155 | }
156 |
157 | /**
158 | * Show a standard {@link Boast} that just contains a text view with the
159 | * text from a resource.
160 | *
161 | * @param context
162 | * The context to use. Usually your {@link android.app.Application}
163 | * or {@link android.app.Activity} object.
164 | * @param resId
165 | * The resource id of the string resource to use. Can be formatted
166 | * text.
167 | * @param duration
168 | * How long to display the message. Either {@link #LENGTH_SHORT} or
169 | * {@link #LENGTH_LONG}
170 | *
171 | * @throws Resources.NotFoundException
172 | * if the resource can't be found.
173 | */
174 | public static void showText(Context context, int resId, int duration)
175 | throws Resources.NotFoundException
176 | {
177 | Boast.makeText(context, resId, duration).show();
178 | }
179 |
180 | /**
181 | * Show a standard {@link Boast} that just contains a text view. Duration
182 | * defaults to {@link #LENGTH_SHORT}.
183 | *
184 | * @param context
185 | * The context to use. Usually your {@link android.app.Application}
186 | * or {@link android.app.Activity} object.
187 | * @param text
188 | * The text to show. Can be formatted text.
189 | */
190 | public static void showText(Context context, CharSequence text)
191 | {
192 | Boast.makeText(context, text, Toast.LENGTH_SHORT).show();
193 | }
194 |
195 | /**
196 | * Show a standard {@link Boast} that just contains a text view with the
197 | * text from a resource. Duration defaults to {@link #LENGTH_SHORT}.
198 | *
199 | * @param context
200 | * The context to use. Usually your {@link android.app.Application}
201 | * or {@link android.app.Activity} object.
202 | * @param resId
203 | * The resource id of the string resource to use. Can be formatted
204 | * text.
205 | *
206 | * @throws Resources.NotFoundException
207 | * if the resource can't be found.
208 | */
209 | public static void showText(Context context, int resId)
210 | throws Resources.NotFoundException
211 | {
212 | Boast.makeText(context, resId, Toast.LENGTH_SHORT).show();
213 | }
214 |
215 | // ////////////////////////////////////////////////////////////////////////////////////////////////////////
216 |
217 | /**
218 | * Close the view if it's showing, or don't show it if it isn't showing yet.
219 | * You do not normally have to call this. Normally view will disappear on
220 | * its own after the appropriate duration.
221 | */
222 | public void cancel()
223 | {
224 | internalToast.cancel();
225 | }
226 |
227 | /**
228 | * Show the view for the specified duration. By default, this method cancels
229 | * any current notification to immediately display the new one. For
230 | * conventional {@link Toast#show()} queueing behaviour, use method
231 | * {@link #show(boolean)}.
232 | *
233 | * @see #show(boolean)
234 | */
235 | public void show()
236 | {
237 | show(true);
238 | }
239 |
240 | /**
241 | * Show the view for the specified duration. This method can be used to
242 | * cancel the current notification, or to queue up notifications.
243 | *
244 | * @param cancelCurrent
245 | * true
to cancel any current notification and replace
246 | * it with this new one
247 | *
248 | * @see #show()
249 | */
250 | public void show(boolean cancelCurrent)
251 | {
252 | // cancel current
253 | if (cancelCurrent && (globalBoast != null))
254 | {
255 | globalBoast.cancel();
256 | }
257 |
258 | // save an instance of this current notification
259 | globalBoast = this;
260 |
261 | internalToast.show();
262 | }
263 |
264 | }
265 |
--------------------------------------------------------------------------------
/src/maigosoft/mcpdict/ActivityWithOptionsMenu.java:
--------------------------------------------------------------------------------
1 | package maigosoft.mcpdict;
2 |
3 | import android.app.AlertDialog;
4 | import android.app.Dialog;
5 | import android.content.Intent;
6 | import android.support.v4.app.FragmentActivity;
7 | import android.view.Gravity;
8 | import android.view.Menu;
9 | import android.view.MenuItem;
10 | import android.widget.TextView;
11 |
12 | public class ActivityWithOptionsMenu extends FragmentActivity {
13 |
14 | @Override
15 | public boolean onCreateOptionsMenu(Menu menu) {
16 | // Inflate the menu; this adds items to the action bar if it is present.
17 | getMenuInflater().inflate(R.menu.main_menu, menu);
18 | return true;
19 | }
20 |
21 | @Override
22 | public boolean onOptionsItemSelected(MenuItem item) {
23 | Intent intent;
24 | switch (item.getItemId()) {
25 | case R.id.menu_item_settings:
26 | intent = new Intent(this, SettingsActivity.class);
27 | startActivity(intent);
28 | return true;
29 | case R.id.menu_item_help:
30 | intent = new Intent(this, HelpActivity.class);
31 | startActivity(intent);
32 | return true;
33 | case R.id.menu_item_about:
34 | Dialog dialog = new AlertDialog.Builder(this)
35 | .setIcon(R.drawable.ic_info)
36 | .setTitle(R.string.about)
37 | .setMessage(R.string.about_message)
38 | .setPositiveButton(R.string.ok, null)
39 | .show();
40 | TextView messageText = (TextView) dialog.findViewById(android.R.id.message);
41 | messageText.setGravity(Gravity.CENTER);
42 | return true;
43 | default:
44 | return super.onOptionsItemSelected(item);
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/maigosoft/mcpdict/CustomListPreference.java:
--------------------------------------------------------------------------------
1 | package maigosoft.mcpdict;
2 |
3 | import android.app.AlertDialog.Builder;
4 | import android.content.Context;
5 | import android.content.DialogInterface;
6 | import android.content.res.Resources;
7 | import android.content.res.TypedArray;
8 | import android.os.Parcel;
9 | import android.os.Parcelable;
10 | import android.preference.DialogPreference;
11 | import android.preference.Preference;
12 | import android.util.AttributeSet;
13 |
14 | public class CustomListPreference extends DialogPreference {
15 |
16 | // Two differences from android.preference.ListPreference:
17 | // (1) Displays the currently selected entry as the summary
18 | // Reference: http://stackoverflow.com/a/8004498
19 | // (2) Stores the index of the selected entry (an integer) instead of a String into the SharedPreferences
20 | // Reference: http://stackoverflow.com/a/20295410
21 |
22 | private final static String ANDROID_NS = "http://schemas.android.com/apk/res/android";
23 | // This is specified by the xmlns:android attribute of the PreferenceScreen tag
24 | // in res/xml/preferences.xml
25 |
26 | private Context context;
27 | private CharSequence[] mEntries;
28 | private int mValue;
29 | private transient int mTempValue;
30 |
31 | public CustomListPreference(Context context, AttributeSet attrs) {
32 | super(context, attrs);
33 |
34 | this.context = context;
35 | Resources res = context.getResources();
36 |
37 | int entriesResId = attrs.getAttributeResourceValue(ANDROID_NS, "entries", 0);
38 | if (entriesResId != 0) {
39 | mEntries = res.getTextArray(entriesResId);
40 | }
41 |
42 | int defaultValueResId = attrs.getAttributeResourceValue(ANDROID_NS, "defaultValue", 0);
43 | if (defaultValueResId != 0) {
44 | mValue = res.getInteger(defaultValueResId);
45 | }
46 |
47 | setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
48 | @Override
49 | public boolean onPreferenceChange(Preference pref, Object value) {
50 | pref.setSummary(getEntry());
51 | return true;
52 | }
53 | });
54 | }
55 |
56 | public CustomListPreference(Context context) {
57 | this(context, null);
58 | }
59 |
60 | public void setEntries(CharSequence[] entries) {
61 | mEntries = entries;
62 | }
63 |
64 | public void setEntries(int entriesResId) {
65 | setEntries(getContext().getResources().getTextArray(entriesResId));
66 | }
67 |
68 | public CharSequence[] getEntries() {
69 | return mEntries;
70 | }
71 |
72 | public void setValue(int value) {
73 | mValue = value;
74 | persistInt(value);
75 | }
76 |
77 | public int getValue() {
78 | return mValue;
79 | }
80 |
81 | public CharSequence getEntry() {
82 | return (mEntries != null && mValue >= 0 && mValue < mEntries.length) ? mEntries[mValue] : null;
83 | }
84 |
85 | @Override
86 | public CharSequence getSummary() {
87 | return getEntry();
88 | }
89 |
90 | @Override
91 | protected void onPrepareDialogBuilder(Builder builder) {
92 | super.onPrepareDialogBuilder(builder);
93 |
94 | if (mEntries == null) {
95 | throw new IllegalStateException("CustomListPreference requires an entries array.");
96 | }
97 |
98 | builder.setSingleChoiceItems(mEntries, mValue, new DialogInterface.OnClickListener() {
99 | @Override
100 | public void onClick(DialogInterface dialog, int which) {
101 | mTempValue = which;
102 | /*
103 | * Clicking on an item simulates the positive button
104 | * click, and dismisses the dialog.
105 | */
106 | CustomListPreference.this.onClick(dialog, DialogInterface.BUTTON_POSITIVE);
107 | dialog.dismiss();
108 | }
109 | });
110 |
111 | /*
112 | * The typical interaction for list-based dialogs is to have
113 | * click-on-an-item dismiss the dialog instead of the user having to
114 | * press 'Ok'.
115 | */
116 | builder.setPositiveButton(null, null);
117 | builder.setNegativeButton(context.getString(R.string.cancel), null);
118 | }
119 |
120 | @Override
121 | protected void onDialogClosed(boolean positiveResult) {
122 | super.onDialogClosed(positiveResult);
123 |
124 | if (positiveResult) {
125 | if (callChangeListener(mTempValue)) {
126 | setValue(mTempValue);
127 | }
128 | }
129 | }
130 |
131 | @Override
132 | protected Object onGetDefaultValue(TypedArray a, int index) {
133 | return a.getInteger(index, -1);
134 | }
135 |
136 | @Override
137 | protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
138 | setValue(restoreValue ? getPersistedInt(mValue) : (Integer) defaultValue);
139 | }
140 |
141 | @Override
142 | protected Parcelable onSaveInstanceState() {
143 | final Parcelable superState = super.onSaveInstanceState();
144 | if (isPersistent()) {
145 | // No need to save instance state since it's persistent
146 | return superState;
147 | }
148 |
149 | final SavedState myState = new SavedState(superState);
150 | myState.value = getValue();
151 | return myState;
152 | }
153 |
154 | @Override
155 | protected void onRestoreInstanceState(Parcelable state) {
156 | if (state == null || !state.getClass().equals(SavedState.class)) {
157 | // Didn't save state for us in onSaveInstanceState
158 | super.onRestoreInstanceState(state);
159 | return;
160 | }
161 |
162 | SavedState myState = (SavedState) state;
163 | super.onRestoreInstanceState(myState.getSuperState());
164 | setValue(myState.value);
165 | }
166 |
167 | private static class SavedState extends BaseSavedState {
168 | int value;
169 |
170 | public SavedState(Parcel source) {
171 | super(source);
172 | value = source.readInt();
173 | }
174 |
175 | @Override
176 | public void writeToParcel(Parcel dest, int flags) {
177 | super.writeToParcel(dest, flags);
178 | dest.writeInt(value);
179 | }
180 |
181 | public SavedState(Parcelable superState) {
182 | super(superState);
183 | }
184 |
185 | @SuppressWarnings("unused")
186 | public static final Parcelable.Creator CREATOR =
187 | new Parcelable.Creator() {
188 | @Override
189 | public SavedState createFromParcel(Parcel in) {
190 | return new SavedState(in);
191 | }
192 |
193 | @Override
194 | public SavedState[] newArray(int size) {
195 | return new SavedState[size];
196 | }
197 | };
198 | }
199 | }
200 |
--------------------------------------------------------------------------------
/src/maigosoft/mcpdict/CustomSearchView.java:
--------------------------------------------------------------------------------
1 | package maigosoft.mcpdict;
2 |
3 | import android.content.Context;
4 | import android.text.Editable;
5 | import android.text.TextWatcher;
6 | import android.util.AttributeSet;
7 | import android.view.KeyEvent;
8 | import android.view.LayoutInflater;
9 | import android.view.View;
10 | import android.view.inputmethod.InputMethodManager;
11 | import android.widget.Button;
12 | import android.widget.EditText;
13 | import android.widget.RelativeLayout;
14 | import android.widget.TextView;
15 | import maigosoft.mcpdict.R;
16 |
17 | public class CustomSearchView extends RelativeLayout {
18 |
19 | private EditText editText;
20 | private Button clearButton;
21 | private Button searchButton;
22 |
23 | public CustomSearchView(Context context) {
24 | this(context, null);
25 | }
26 |
27 | public CustomSearchView(Context context, AttributeSet attrs) {
28 | super(context, attrs);
29 |
30 | LayoutInflater inflater = (LayoutInflater)
31 | context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
32 | inflater.inflate(R.layout.custom_search_view, this, true);
33 |
34 | editText = (EditText) findViewById(R.id.text_query);
35 | clearButton = (Button) findViewById(R.id.button_clear);
36 | searchButton = (Button) findViewById(R.id.button_search);
37 |
38 | // Toggle the clear button when user edits text
39 | editText.addTextChangedListener(new TextWatcher() {
40 | public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
41 | public void onTextChanged(CharSequence s, int start, int before, int count) {}
42 | public void afterTextChanged(Editable s) {
43 | clearButton.setVisibility((s.length() == 0) ? View.GONE : View.VISIBLE);
44 | }
45 | });
46 |
47 | // Invoke the search button when user hits Enter
48 | editText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
49 | public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
50 | searchButton.performClick();
51 | return true;
52 | }
53 | });
54 |
55 | clearButton.setVisibility(View.GONE);
56 | clearButton.setOnClickListener(new View.OnClickListener() {
57 | public void onClick(View v) {
58 | editText.setText("");
59 | }
60 | });
61 | }
62 |
63 | public void setHint(String hint) {
64 | editText.setHint(hint);
65 | }
66 |
67 | public void setSearchButtonOnClickListener(final View.OnClickListener listener) {
68 | searchButton.setOnClickListener(new View.OnClickListener() {
69 | public void onClick(View v) {
70 | // Hide the keyboard before performing the search
71 | editText.clearFocus();
72 | InputMethodManager imm = (InputMethodManager)v.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
73 | imm.hideSoftInputFromWindow(editText.getWindowToken(), 0);
74 | listener.onClick(v);
75 | }
76 | });
77 | }
78 |
79 | public void clickSearchButton() {
80 | searchButton.performClick();
81 | }
82 |
83 | public String getQuery() {
84 | return editText.getText().toString();
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/src/maigosoft/mcpdict/DictionaryFragment.java:
--------------------------------------------------------------------------------
1 | package maigosoft.mcpdict;
2 |
3 | import android.content.SharedPreferences;
4 | import android.content.res.Resources;
5 | import android.database.Cursor;
6 | import android.os.AsyncTask;
7 | import android.os.Bundle;
8 | import android.preference.PreferenceManager;
9 | import android.support.v4.app.Fragment;
10 | import android.view.LayoutInflater;
11 | import android.view.View;
12 | import android.view.ViewGroup;
13 | import android.widget.AdapterView;
14 | import android.widget.AdapterView.OnItemSelectedListener;
15 | import android.widget.ArrayAdapter;
16 | import android.widget.CheckBox;
17 | import android.widget.CompoundButton;
18 | import android.widget.Spinner;
19 | import android.widget.TextView;
20 |
21 | public class DictionaryFragment extends Fragment implements RefreshableFragment {
22 |
23 | private View selfView;
24 | private CustomSearchView searchView;
25 | private Spinner spinnerSearchAs;
26 | private CheckBox checkBoxKuangxYonhOnly;
27 | private CheckBox checkBoxAllowVariants;
28 | private CheckBox checkBoxToneInsensitive;
29 | private SearchResultFragment fragmentResult;
30 |
31 | @Override
32 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
33 | // A hack to avoid nested fragments from being inflated twice
34 | // Reference: http://stackoverflow.com/a/14695397
35 | if (selfView != null) {
36 | ViewGroup parent = (ViewGroup) selfView.getParent();
37 | if (parent != null) parent.removeView(selfView);
38 | return selfView;
39 | }
40 |
41 | // Inflate the fragment view
42 | selfView = inflater.inflate(R.layout.dictionary_fragment, container, false);
43 |
44 | // Set up the search view
45 | searchView = (CustomSearchView) selfView.findViewById(R.id.search_view);
46 | searchView.setHint(getResources().getString(R.string.search_hint));
47 | searchView.setSearchButtonOnClickListener(new View.OnClickListener() {
48 | public void onClick(View view) {
49 | refresh();
50 | fragmentResult.scrollToTop();
51 | }
52 | });
53 |
54 | // Set up the spinner
55 | spinnerSearchAs = (Spinner) selfView.findViewById(R.id.spinner_search_as);
56 | ArrayAdapter adapter = ArrayAdapter.createFromResource(
57 | getActivity(), R.array.search_as, android.R.layout.simple_spinner_item
58 | );
59 | adapter.setDropDownViewResource(R.layout.custom_spinner_dropdown_item);
60 | spinnerSearchAs.setAdapter(adapter);
61 | spinnerSearchAs.setOnItemSelectedListener(new OnItemSelectedListener() {
62 | @Override
63 | public void onItemSelected(AdapterView> parent, View view, int position, long id) {
64 | updateCheckBoxesEnabled();
65 | searchView.clickSearchButton();
66 | }
67 | @Override
68 | public void onNothingSelected(AdapterView> parent) {}
69 | });
70 |
71 | // Set up the checkboxes
72 | checkBoxKuangxYonhOnly = (CheckBox) selfView.findViewById(R.id.check_box_kuangx_yonh_only);
73 | checkBoxAllowVariants = (CheckBox) selfView.findViewById(R.id.check_box_allow_variants);
74 | checkBoxToneInsensitive = (CheckBox) selfView.findViewById(R.id.check_box_tone_insensitive);
75 | loadCheckBoxes();
76 | updateCheckBoxesEnabled();
77 | CompoundButton.OnCheckedChangeListener checkBoxListener = new CompoundButton.OnCheckedChangeListener() {
78 | @Override
79 | public void onCheckedChanged(CompoundButton view, boolean isChecked) {
80 | saveCheckBoxes();
81 | searchView.clickSearchButton();
82 | }
83 | };
84 | checkBoxKuangxYonhOnly.setOnCheckedChangeListener(checkBoxListener);
85 | checkBoxAllowVariants.setOnCheckedChangeListener(checkBoxListener);
86 | checkBoxToneInsensitive.setOnCheckedChangeListener(checkBoxListener);
87 |
88 | // Get a reference to the SearchResultFragment
89 | fragmentResult = (SearchResultFragment) getFragmentManager().findFragmentById(R.id.fragment_search_result);
90 |
91 | return selfView;
92 | }
93 |
94 | private void loadCheckBoxes() {
95 | SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getActivity());
96 | Resources r = getResources();
97 | checkBoxKuangxYonhOnly.setChecked(sp.getBoolean(r.getString(R.string.pref_key_kuangx_yonh_only), false));
98 | checkBoxAllowVariants.setChecked(sp.getBoolean(r.getString(R.string.pref_key_allow_variants), true));
99 | checkBoxToneInsensitive.setChecked(sp.getBoolean(r.getString(R.string.pref_key_tone_insensitive), false));
100 | }
101 |
102 | private void saveCheckBoxes() {
103 | SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getActivity());
104 | Resources r = getResources();
105 | sp.edit().putBoolean(r.getString(R.string.pref_key_kuangx_yonh_only), checkBoxKuangxYonhOnly.isChecked())
106 | .putBoolean(r.getString(R.string.pref_key_allow_variants), checkBoxAllowVariants.isChecked())
107 | .putBoolean(r.getString(R.string.pref_key_tone_insensitive), checkBoxToneInsensitive.isChecked())
108 | .apply();
109 | }
110 |
111 | private void updateCheckBoxesEnabled() {
112 | int mode = spinnerSearchAs.getSelectedItemPosition();
113 | checkBoxKuangxYonhOnly.setEnabled(mode != MCPDatabase.SEARCH_AS_MC);
114 | checkBoxAllowVariants.setEnabled(mode == MCPDatabase.SEARCH_AS_HZ);
115 | checkBoxToneInsensitive.setEnabled(mode == MCPDatabase.SEARCH_AS_MC ||
116 | mode == MCPDatabase.SEARCH_AS_PU ||
117 | mode == MCPDatabase.SEARCH_AS_CT ||
118 | mode == MCPDatabase.SEARCH_AS_SH ||
119 | mode == MCPDatabase.SEARCH_AS_MN ||
120 | mode == MCPDatabase.SEARCH_AS_VN);
121 | }
122 |
123 | @Override
124 | public void refresh() {
125 | // Search on a separate thread
126 | // Because AsyncTasks are put in a queue,
127 | // this will not run until the initialization of the orthography module finishes
128 | final String query = searchView.getQuery();
129 | final int mode = spinnerSearchAs.getSelectedItemPosition();
130 | new AsyncTask() {
131 | @Override
132 | protected Cursor doInBackground(Void... params) {
133 | return MCPDatabase.search(query, mode);
134 | }
135 | @Override
136 | protected void onPostExecute(Cursor data) {
137 | fragmentResult.setData(data);
138 | TextView textEmpty = (TextView) fragmentResult.getView().findViewById(android.R.id.empty);
139 | if (query.trim().equals("")) {
140 | textEmpty.setText("");
141 | }
142 | else {
143 | textEmpty.setText(R.string.no_matches);
144 | }
145 | }
146 | }.execute();
147 | }
148 | }
149 |
--------------------------------------------------------------------------------
/src/maigosoft/mcpdict/FavoriteCursorAdapter.java:
--------------------------------------------------------------------------------
1 | package maigosoft.mcpdict;
2 |
3 | import java.util.HashSet;
4 | import java.util.Set;
5 | import java.util.concurrent.atomic.AtomicInteger;
6 |
7 | import android.annotation.SuppressLint;
8 | import android.content.Context;
9 | import android.database.Cursor;
10 | import android.os.AsyncTask;
11 | import android.widget.CursorAdapter;
12 | import android.view.LayoutInflater;
13 | import android.view.View;
14 | import android.view.ViewGroup;
15 | import android.widget.Button;
16 | import android.widget.ListView;
17 | import android.widget.TextView;
18 |
19 | @SuppressLint("UseSparseArrays")
20 | public class FavoriteCursorAdapter extends CursorAdapter {
21 |
22 | private int layout;
23 | private LayoutInflater inflater;
24 | private FavoriteFragment fragment;
25 | private AtomicInteger nextId = new AtomicInteger(42);
26 | // Answer to life, the universe and everything
27 | private Set expandedItems;
28 |
29 | public FavoriteCursorAdapter(Context context, int layout, Cursor cursor, FavoriteFragment fragment) {
30 | super(context, cursor, FLAG_REGISTER_CONTENT_OBSERVER);
31 | this.layout = layout;
32 | this.inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
33 | this.fragment = fragment;
34 | this.expandedItems = new HashSet();
35 | }
36 |
37 | @Override
38 | public View newView(Context context, Cursor cursor, ViewGroup parent) {
39 | View view = inflater.inflate(layout, parent, false);
40 |
41 | // Give the container a unique ID,
42 | // so that a SearchResultFragment may be added to it
43 | int id = nextId.getAndIncrement();
44 | view.findViewWithTag("container").setId(id);
45 |
46 | // Add a SearchResultFragment to the container
47 | SearchResultFragment fragment = new SearchResultFragment(false);
48 | this.fragment.getFragmentManager().beginTransaction().add(id, fragment).commit();
49 | view.setTag(fragment);
50 | // Set the fragment as a tag of the view, so it can be retrieved in expandItem
51 |
52 | return view;
53 | }
54 |
55 | @Override
56 | public void bindView(final View view, final Context context, Cursor cursor) {
57 | final char unicode;
58 | String string;
59 | TextView textView;
60 |
61 | // Get the Chinese character from the cursor,
62 | // and make sure we're binding it to the view recorded in itemStatus
63 | string = cursor.getString(cursor.getColumnIndex("unicode"));
64 | unicode = (char) Integer.parseInt(string, 16);
65 |
66 | // Chinese character
67 | string = String.valueOf(unicode);
68 | textView = (TextView) view.findViewById(R.id.text_hz);
69 | textView.setText(string);
70 |
71 | // Timestamp
72 | string = cursor.getString(cursor.getColumnIndex("local_timestamp"));
73 | textView = (TextView) view.findViewById(R.id.text_timestamp);
74 | textView.setText(string);
75 |
76 | // Comment
77 | string = cursor.getString(cursor.getColumnIndex("comment"));
78 | textView = (TextView) view.findViewById(R.id.text_comment);
79 | textView.setText(string);
80 |
81 | // "Edit" button
82 | final Button buttonEdit = (Button) view.findViewById(R.id.button_edit);
83 | buttonEdit.setOnClickListener(new View.OnClickListener() {
84 | @Override
85 | public void onClick(View v) {
86 | FavoriteDialogs.view(unicode, view);
87 | }
88 | });
89 |
90 | // "Delete" button
91 | final Button buttonDelete = (Button) view.findViewById(R.id.button_delete);
92 | buttonDelete.setOnClickListener(new View.OnClickListener() {
93 | @Override
94 | public void onClick(View v) {
95 | FavoriteDialogs.delete(unicode, false);
96 | }
97 | });
98 |
99 | // Restore expanded status
100 | if (expandedItems.contains(unicode)) {
101 | expandItem(unicode, view);
102 | }
103 | else {
104 | collapseItem(unicode, view);
105 | }
106 | }
107 |
108 | public boolean isItemExpanded(char unicode) {
109 | return expandedItems.contains(unicode);
110 | }
111 |
112 | public void expandItem(char unicode, View view) {
113 | expandItem(unicode, view, null);
114 | }
115 |
116 | // Mark a Chinese character as expanded
117 | // If a view is provided, expand that view, too
118 | // If a list is provided, scroll the list so that the view is entirely visible
119 | public void expandItem(final char unicode, final View view, final ListView list) {
120 | expandedItems.add(unicode);
121 | if (view == null) return;
122 | final View container = view.findViewWithTag("container");
123 | final SearchResultFragment fragment = (SearchResultFragment) view.getTag();
124 | new AsyncTask() {
125 | @Override
126 | protected Cursor doInBackground(Void... params) {
127 | return MCPDatabase.directSearch(unicode);
128 | }
129 | @Override
130 | protected void onPostExecute(Cursor data) {
131 | fragment.setData(data);
132 | container.setVisibility(View.VISIBLE);
133 | if (list == null) return;
134 | scrollListToShowItem(list, view);
135 | }
136 | }.execute();
137 | }
138 |
139 | public void collapseItem(char unicode) {
140 | collapseItem(unicode, null, null);
141 | }
142 |
143 | public void collapseItem(char unicode, View view) {
144 | collapseItem(unicode, view, null);
145 | }
146 |
147 | // Mark a Chinese character as collapsed
148 | // If a view is provided, collapsed that view, too
149 | // If a list is provided, scroll the list so that the view is entirely visible
150 | public void collapseItem(char unicode, View view, ListView list) {
151 | expandedItems.remove(unicode);
152 | if (view == null) return;
153 | View container = view.findViewWithTag("container");
154 | container.setVisibility(View.GONE);
155 | if (list == null) return;
156 | scrollListToShowItem(list, view);
157 | }
158 |
159 | // Mark all Chinese characters as collapsed
160 | // Only called when clearing all favorite characters
161 | public void collapseAll() {
162 | expandedItems.clear();
163 | }
164 |
165 | // Scroll a list so that a view inside it becomes entirely visible
166 | // If the view is taller than the list, make sure the view's bottom is visible
167 | // This method had better reside in a utility class
168 | public static void scrollListToShowItem(final ListView list, final View view) {
169 | list.post(new Runnable() {
170 | @Override
171 | public void run() {
172 | int top = view.getTop();
173 | int bottom = view.getBottom();
174 | int height = bottom - top;
175 | int listTop = list.getPaddingTop();
176 | int listBottom = list.getHeight() - list.getPaddingBottom();
177 | int listHeight = listBottom - listTop;
178 | int y = (height > listHeight || bottom > listBottom) ? (listBottom - height) :
179 | (top < listTop) ? listTop : top;
180 | int position = list.getPositionForView(view);
181 | list.setSelectionFromTop(position, y);
182 | }
183 | });
184 | }
185 | }
186 |
--------------------------------------------------------------------------------
/src/maigosoft/mcpdict/FavoriteDialogs.java:
--------------------------------------------------------------------------------
1 | package maigosoft.mcpdict;
2 |
3 | import java.io.File;
4 | import java.io.IOException;
5 | import java.text.SimpleDateFormat;
6 | import java.util.Date;
7 |
8 | import android.annotation.SuppressLint;
9 | import android.app.AlertDialog;
10 | import android.content.DialogInterface;
11 | import android.content.SharedPreferences;
12 | import android.database.sqlite.SQLiteException;
13 | import android.preference.PreferenceManager;
14 | import android.view.View;
15 | import android.widget.CheckBox;
16 | import android.widget.EditText;
17 | import android.widget.TextView;
18 | import android.widget.Toast;
19 |
20 | import com.mobiRic.ui.widget.Boast;
21 |
22 | @SuppressLint("SimpleDateFormat")
23 | public class FavoriteDialogs {
24 |
25 | private static MainActivity activity;
26 |
27 | private static int importMode;
28 |
29 | public static void initialize(MainActivity activity) {
30 | FavoriteDialogs.activity = activity;
31 | }
32 |
33 | public static void add(final char unicode) {
34 | final EditText editText = new EditText(activity);
35 | editText.setHint(R.string.favorite_add_hint);
36 | editText.setSingleLine(false);
37 | new AlertDialog.Builder(activity)
38 | .setIcon(R.drawable.ic_star_yellow)
39 | .setTitle(String.format(activity.getString(R.string.favorite_add), unicode))
40 | .setView(editText)
41 | .setPositiveButton(R.string.save, new DialogInterface.OnClickListener() {
42 | @Override
43 | public void onClick(DialogInterface dialog, int which) {
44 | String comment = editText.getText().toString();
45 | UserDatabase.insertFavorite(unicode, comment);
46 | String message = String.format(activity.getString(R.string.favorite_add_done), unicode);
47 | Boast.showText(activity, message, Toast.LENGTH_SHORT);
48 | FavoriteFragment fragment = activity.getFavoriteFragment();
49 | if (fragment != null) {
50 | fragment.notifyAddItem();
51 | }
52 | activity.getCurrentFragment().refresh();
53 | }
54 | })
55 | .setNegativeButton(R.string.cancel, null)
56 | .show();
57 | }
58 |
59 | public static void view(final char unicode, final View view) {
60 | new AlertDialog.Builder(activity)
61 | .setIcon(R.drawable.ic_star_yellow)
62 | .setTitle(String.format(activity.getString(R.string.favorite_view), unicode))
63 | .setMessage(((TextView) view.findViewById(R.id.text_comment)).getText())
64 | .setPositiveButton(String.format(activity.getString(R.string.favorite_edit_2lines), unicode),
65 | new DialogInterface.OnClickListener() {
66 | @Override
67 | public void onClick(DialogInterface dialog, int which) {
68 | FavoriteDialogs.edit(unicode, view);
69 | }
70 | })
71 | .setNegativeButton(String.format(activity.getString(R.string.favorite_delete_2lines), unicode),
72 | new DialogInterface.OnClickListener() {
73 | @Override
74 | public void onClick(DialogInterface dialog, int which) {
75 | FavoriteDialogs.delete(unicode, false);
76 | }
77 | })
78 | .setNeutralButton(R.string.back, null)
79 | .show();
80 | }
81 |
82 | public static void edit(final char unicode, View view) {
83 | final EditText editText = new EditText(activity);
84 | editText.setText(((TextView) view.findViewById(R.id.text_comment)).getText());
85 | editText.setSingleLine(false);
86 | new AlertDialog.Builder(activity)
87 | .setIcon(R.drawable.ic_star_yellow)
88 | .setTitle(String.format(activity.getString(R.string.favorite_edit), unicode))
89 | .setView(editText)
90 | .setPositiveButton(R.string.save, new DialogInterface.OnClickListener() {
91 | @Override
92 | public void onClick(DialogInterface dialog, int which) {
93 | String comment = editText.getText().toString();
94 | UserDatabase.updateFavorite(unicode, comment);
95 | String message = String.format(activity.getString(R.string.favorite_edit_done), unicode);
96 | Boast.showText(activity, message, Toast.LENGTH_SHORT);
97 | activity.getCurrentFragment().refresh();
98 | }
99 | })
100 | .setNegativeButton(R.string.cancel, null)
101 | .show();
102 | }
103 |
104 | public static void delete(final char unicode, boolean force) {
105 | if (force) {
106 | UserDatabase.deleteFavorite(unicode);
107 | String message = String.format(activity.getString(R.string.favorite_delete_done), unicode);
108 | Boast.showText(activity, message, Toast.LENGTH_SHORT);
109 | FavoriteFragment fragment = activity.getFavoriteFragment();
110 | if (fragment != null) {
111 | FavoriteCursorAdapter adapter = (FavoriteCursorAdapter) fragment.getListAdapter();
112 | adapter.collapseItem(unicode);
113 | }
114 | activity.getCurrentFragment().refresh();
115 | return;
116 | }
117 |
118 | final SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(activity);
119 | final String prefKey = activity.getString(R.string.pref_key_favorite_delete_no_confirm_expiry);
120 | long expiry = sp.getLong(prefKey, 0);
121 | long now = System.currentTimeMillis();
122 | boolean expired = (expiry == 0 || now > expiry);
123 | if (!expired) {
124 | delete(unicode, true);
125 | return;
126 | }
127 |
128 | final CheckBox checkBox = new CheckBox(activity);
129 | checkBox.setText(R.string.favorite_delete_no_confirm);
130 | new AlertDialog.Builder(activity)
131 | .setIcon(R.drawable.ic_alert)
132 | .setTitle(String.format(activity.getString(R.string.favorite_delete), unicode))
133 | .setMessage(String.format(activity.getString(R.string.favorite_delete_confirm), unicode))
134 | .setView(checkBox)
135 | .setPositiveButton(R.string.delete, new DialogInterface.OnClickListener() {
136 | @Override
137 | public void onClick(DialogInterface dialog, int which) {
138 | delete(unicode, true);
139 | if (checkBox.isChecked()) {
140 | sp.edit().putLong(prefKey, System.currentTimeMillis() + 3600000).commit();
141 | // No confirmation for 1 hour
142 | }
143 | }
144 | })
145 | .setNegativeButton(R.string.cancel, null)
146 | .show();
147 | }
148 |
149 | public static void deleteAll() {
150 | new AlertDialog.Builder(activity)
151 | .setIcon(R.drawable.ic_alert)
152 | .setTitle(activity.getString(R.string.favorite_clear))
153 | .setMessage(activity.getString(R.string.favorite_clear_confirm))
154 | .setPositiveButton(R.string.clear, new DialogInterface.OnClickListener() {
155 | @Override
156 | public void onClick(DialogInterface dialog, int which) {
157 | UserDatabase.deleteAllFavorites();
158 | String message = activity.getString(R.string.favorite_clear_done);
159 | Boast.showText(activity, message, Toast.LENGTH_SHORT);
160 | FavoriteFragment fragment = activity.getFavoriteFragment();
161 | if (fragment != null) {
162 | FavoriteCursorAdapter adapter = (FavoriteCursorAdapter) fragment.getListAdapter();
163 | adapter.collapseAll();
164 | }
165 | activity.getCurrentFragment().refresh();
166 | }
167 | })
168 | .setNegativeButton(R.string.cancel, null)
169 | .show();
170 | }
171 |
172 | public static void export(boolean force) {
173 | File backupFile = new File(UserDatabase.getBackupPath());
174 | if (force || !backupFile.exists()) {
175 | try {
176 | UserDatabase.exportFavorites();
177 | new AlertDialog.Builder(activity)
178 | .setIcon(R.drawable.ic_info)
179 | .setTitle(activity.getString(R.string.favorite_export))
180 | .setMessage(String.format(activity.getString(R.string.favorite_export_done),
181 | UserDatabase.getBackupPath()))
182 | .setPositiveButton(R.string.ok, null)
183 | .show();
184 | }
185 | catch (IOException e) {
186 | crash(e);
187 | }
188 | }
189 | else {
190 | long timestamp = backupFile.lastModified();
191 | new AlertDialog.Builder(activity)
192 | .setIcon(R.drawable.ic_alert)
193 | .setTitle(activity.getString(R.string.favorite_export))
194 | .setMessage(String.format(activity.getString(R.string.favorite_export_overwrite),
195 | UserDatabase.getBackupPath(),
196 | new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(new Date(timestamp))))
197 | .setPositiveButton(R.string.overwrite, new DialogInterface.OnClickListener() {
198 | @Override
199 | public void onClick(DialogInterface dialog, int which) {
200 | export(true);
201 | }
202 | })
203 | .setNegativeButton(R.string.cancel, null)
204 | .show();
205 | }
206 | }
207 |
208 | public static void import_(int state) {
209 | // States:
210 | // 0: check if the backup file exists, is readable, and contains entries,
211 | // and display info about the backup file
212 | // 1: prompt for import mode
213 | // 2: do the importing, and (optionally) delete the backup file
214 |
215 | switch (state) {
216 | case 0:
217 | File backupFile = new File(UserDatabase.getBackupPath());
218 | if (!backupFile.exists()) {
219 | new AlertDialog.Builder(activity)
220 | .setIcon(R.drawable.ic_error)
221 | .setTitle(activity.getString(R.string.favorite_import))
222 | .setMessage(String.format(activity.getString(R.string.favorite_import_file_not_found),
223 | UserDatabase.getBackupPath()))
224 | .setPositiveButton(R.string.ok, null)
225 | .show();
226 | break;
227 | }
228 |
229 | int count = 0;
230 | try {
231 | count = UserDatabase.selectBackupFavoriteCount();
232 | }
233 | catch (SQLiteException e) {
234 | new AlertDialog.Builder(activity)
235 | .setIcon(R.drawable.ic_error)
236 | .setTitle(activity.getString(R.string.favorite_import))
237 | .setMessage(String.format(activity.getString(R.string.favorite_import_read_fail),
238 | UserDatabase.getBackupPath()))
239 | .setPositiveButton(R.string.ok, null)
240 | .show();
241 | break;
242 | }
243 |
244 | if (count == 0) {
245 | new AlertDialog.Builder(activity)
246 | .setIcon(R.drawable.ic_error)
247 | .setTitle(activity.getString(R.string.favorite_import))
248 | .setMessage(String.format(activity.getString(R.string.favorite_import_empty_file),
249 | UserDatabase.getBackupPath()))
250 | .setPositiveButton(R.string.ok, null)
251 | .show();
252 | break;
253 | }
254 |
255 | if (UserDatabase.selectAllFavorites().getCount() == 0) {
256 | new AlertDialog.Builder(activity)
257 | .setIcon(R.drawable.ic_info)
258 | .setTitle(activity.getString(R.string.favorite_import))
259 | .setMessage(String.format(activity.getString(R.string.favorite_import_detail),
260 | UserDatabase.getBackupPath(),
261 | count))
262 | .setPositiveButton(R.string.import_, new DialogInterface.OnClickListener() {
263 | @Override
264 | public void onClick(DialogInterface dialog, int which) {
265 | importMode = 0;
266 | import_(2);
267 | }
268 | })
269 | .setNegativeButton(R.string.cancel, null)
270 | .show();
271 | }
272 | else {
273 | new AlertDialog.Builder(activity)
274 | .setIcon(R.drawable.ic_info)
275 | .setTitle(activity.getString(R.string.favorite_import))
276 | .setMessage(String.format(activity.getString(R.string.favorite_import_detail_select_mode),
277 | UserDatabase.getBackupPath(),
278 | count))
279 | .setPositiveButton(R.string.next, new DialogInterface.OnClickListener() {
280 | @Override
281 | public void onClick(DialogInterface dialog, int which) {
282 | import_(1);
283 | }
284 | })
285 | .setNegativeButton(R.string.cancel, null)
286 | .show();
287 | }
288 | break;
289 |
290 | case 1:
291 | new AlertDialog.Builder(activity)
292 | .setIcon(R.drawable.ic_question)
293 | .setTitle(activity.getString(R.string.favorite_import_select_mode))
294 | .setSingleChoiceItems(R.array.favorite_import_modes, -1, null)
295 | .setPositiveButton(R.string.import_, new DialogInterface.OnClickListener() {
296 | @Override
297 | public void onClick(DialogInterface dialog, int which) {
298 | importMode = ((AlertDialog) dialog).getListView().getCheckedItemPosition();
299 | import_(2);
300 | }
301 | })
302 | .setNegativeButton(R.string.cancel, null)
303 | .show();
304 | break;
305 |
306 | case 2:
307 | try {
308 | switch (importMode) {
309 | case 0: UserDatabase.importFavoritesOverwrite(); break;
310 | case 1: UserDatabase.importFavoritesMix(); break;
311 | case 2: UserDatabase.importFavoritesAppend(); break;
312 | }
313 | }
314 | catch (IOException | SQLiteException e) {
315 | crash(e);
316 | break;
317 | }
318 |
319 | FavoriteFragment fragment = activity.getFavoriteFragment();
320 | if (fragment != null) {
321 | fragment.notifyAddItem();
322 | FavoriteCursorAdapter adapter = (FavoriteCursorAdapter) fragment.getListAdapter();
323 | adapter.collapseAll();
324 | }
325 | activity.getCurrentFragment().refresh();
326 |
327 | new AlertDialog.Builder(activity)
328 | .setIcon(R.drawable.ic_info)
329 | .setTitle(activity.getString(R.string.favorite_import))
330 | .setMessage(String.format(activity.getString(R.string.favorite_import_done),
331 | UserDatabase.getBackupPath()))
332 | .setPositiveButton(R.string.delete, new DialogInterface.OnClickListener() {
333 | @Override
334 | public void onClick(DialogInterface dialog, int which) {
335 | File backupFile = new File(UserDatabase.getBackupPath());
336 | backupFile.delete();
337 | String message = activity.getString(backupFile.exists() ?
338 | R.string.favorite_import_delete_backup_fail :
339 | R.string.favorite_import_delete_backup_done);
340 | Boast.showText(activity, message, Toast.LENGTH_SHORT);
341 | }
342 | })
343 | .setNegativeButton(R.string.keep, null)
344 | .show();
345 | break;
346 | }
347 | }
348 |
349 | public static void crash(Throwable e) {
350 | try {
351 | String logPath = activity.getExternalFilesDir(null) + "/crash.log";
352 | FileUtils.dumpException(logPath, e);
353 | new AlertDialog.Builder(activity)
354 | .setIcon(R.drawable.ic_error)
355 | .setTitle(activity.getString(R.string.crash))
356 | .setMessage(String.format(activity.getString(R.string.crash_saved), logPath))
357 | .setPositiveButton(R.string.ok, null)
358 | .show();
359 | }
360 | catch (IOException ex) {
361 | new AlertDialog.Builder(activity)
362 | .setIcon(R.drawable.ic_error)
363 | .setTitle(activity.getString(R.string.crash))
364 | .setMessage(activity.getString(R.string.crash_unsaved))
365 | .setPositiveButton(R.string.ok, null)
366 | .show();
367 | }
368 | }
369 | }
370 |
--------------------------------------------------------------------------------
/src/maigosoft/mcpdict/FavoriteFragment.java:
--------------------------------------------------------------------------------
1 | package maigosoft.mcpdict;
2 |
3 | import android.annotation.SuppressLint;
4 | import android.database.Cursor;
5 | import android.os.AsyncTask;
6 | import android.os.Bundle;
7 | import android.support.v4.app.ListFragment;
8 | import android.text.Spannable;
9 | import android.text.method.LinkMovementMethod;
10 | import android.text.style.ClickableSpan;
11 | import android.util.Log;
12 | import android.view.LayoutInflater;
13 | import android.view.MenuItem;
14 | import android.view.View;
15 | import android.view.ViewGroup;
16 | import android.widget.Button;
17 | import android.widget.ListView;
18 | import android.widget.PopupMenu;
19 | import android.widget.TextView;
20 | import android.widget.TextView.BufferType;
21 |
22 | @SuppressLint("UseSparseArrays")
23 | public class FavoriteFragment extends ListFragment implements RefreshableFragment {
24 |
25 | private View selfView;
26 | private View header;
27 | private TextView textTotal;
28 | private Button buttonManage;
29 | private ListView listView;
30 | private TextView textEmpty;
31 | private FavoriteCursorAdapter adapter;
32 | private boolean hasNewItem;
33 |
34 | @Override
35 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
36 | // A hack to avoid nested fragments from being inflated twice
37 | // Reference: http://stackoverflow.com/a/14695397
38 | if (selfView != null) {
39 | ViewGroup parent = (ViewGroup) selfView.getParent();
40 | if (parent != null) parent.removeView(selfView);
41 | return selfView;
42 | }
43 |
44 | // Inflate the fragment view
45 | selfView = inflater.inflate(R.layout.favorite_fragment, container, false);
46 |
47 | // Get references to some child views
48 | header = selfView.findViewById(R.id.favorite_header);
49 | textTotal = (TextView) selfView.findViewById(R.id.text_total);
50 | listView = (ListView) selfView.findViewById(android.R.id.list);
51 |
52 | // Set up the "management" button
53 | buttonManage = (Button) selfView.findViewById(R.id.button_favorite_manage);
54 | buttonManage.setOnClickListener(new View.OnClickListener() {
55 | @Override
56 | public void onClick(View v) {
57 | PopupMenu popup = new PopupMenu(getActivity(), v);
58 | popup.inflate(R.menu.favorite_manage_popup_menu);
59 | popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
60 | @Override
61 | public boolean onMenuItemClick(MenuItem item) {
62 | int id = item.getItemId();
63 | switch (id) {
64 | case R.id.menu_item_export: FavoriteDialogs.export(false); return true;
65 | case R.id.menu_item_import: FavoriteDialogs.import_(0); return true;
66 | case R.id.menu_item_clear: FavoriteDialogs.deleteAll(); return true;
67 | default: return false;
68 | }
69 | }
70 | });
71 | popup.show();
72 | }
73 | });
74 |
75 | // Set up the "import" link in the empty view
76 | textEmpty = (TextView) selfView.findViewById(android.R.id.empty);
77 | textEmpty.setText(textEmpty.getText(), BufferType.SPANNABLE);
78 | Spannable spannable = (Spannable) textEmpty.getText();
79 | int p = spannable.toString().length() - 5;
80 | spannable.setSpan(new ClickableSpan() {
81 | @Override
82 | public void onClick(View view) {
83 | Log.d("MCP", "A");
84 | FavoriteDialogs.import_(0);
85 | Log.d("MCP", "B");
86 | }
87 | }, p, p + 4, Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
88 | textEmpty.setMovementMethod(LinkMovementMethod.getInstance());
89 | // The last line is necessary to make the link clickable
90 | // Reference: http://stackoverflow.com/a/8662457
91 |
92 | return selfView;
93 | }
94 |
95 | @Override
96 | public void onActivityCreated(Bundle savedInstanceState) {
97 | super.onActivityCreated(savedInstanceState);
98 |
99 | // Set up the adapter
100 | if (adapter == null) {
101 | adapter = new FavoriteCursorAdapter(getActivity(), R.layout.favorite_item, null, this);
102 | setListAdapter(adapter);
103 | }
104 | }
105 |
106 | @Override
107 | public void onListItemClick(final ListView list, final View view, final int position, long id) {
108 | // When a favorite item is clicked, display a SearchResultFragment below it
109 | // to show the details about the character in the item, or hide the
110 | // SearchResultFragment if it is already displayed
111 |
112 | // Find the Chinese character in the view being clicked
113 | TextView text = (TextView) view.findViewById(R.id.text_hz);
114 | String hanzi = text.getText().toString();
115 | final char unicode = hanzi.charAt(0);
116 |
117 | if (adapter.isItemExpanded(unicode)) {
118 | adapter.collapseItem(unicode, view, list);
119 | }
120 | else {
121 | adapter.expandItem(unicode, view, list);
122 | }
123 | }
124 |
125 | @Override
126 | public void refresh() {
127 | if (adapter == null) return;
128 | new AsyncTask() {
129 | @Override
130 | protected Cursor doInBackground(Void... params) {
131 | return UserDatabase.selectAllFavorites();
132 | }
133 | @Override
134 | protected void onPostExecute(Cursor data) {
135 | adapter.changeCursor(data);
136 | if (data.getCount() == 0) {
137 | header.setVisibility(View.GONE);
138 | }
139 | else {
140 | header.setVisibility(View.VISIBLE);
141 | textTotal.setText(String.format(getString(R.string.favorite_total), data.getCount()));
142 | }
143 | }
144 | }.execute();
145 | if (hasNewItem) {
146 | listView.setSelectionAfterHeaderView();
147 | hasNewItem = false;
148 | }
149 | }
150 |
151 | public void notifyAddItem() {
152 | hasNewItem = true;
153 | }
154 | }
155 |
--------------------------------------------------------------------------------
/src/maigosoft/mcpdict/FileUtils.java:
--------------------------------------------------------------------------------
1 | package maigosoft.mcpdict;
2 |
3 | import java.io.File;
4 | import java.io.FileInputStream;
5 | import java.io.FileOutputStream;
6 | import java.io.IOException;
7 | import java.io.PrintWriter;
8 | import java.nio.channels.FileChannel;
9 |
10 | public class FileUtils {
11 |
12 | public static void copyFile(String srcPath, String dstPath) throws IOException {
13 | makeParentDirs(dstPath);
14 | File srcFile = new File(srcPath);
15 | File dstFile = new File(dstPath);
16 | FileInputStream srcStream = new FileInputStream(srcFile);
17 | FileOutputStream dstStream = new FileOutputStream(dstFile);
18 | FileChannel srcChannel = srcStream.getChannel();
19 | FileChannel dstChannel = dstStream.getChannel();
20 | srcChannel.transferTo(0, srcChannel.size(), dstChannel);
21 | srcStream.close();
22 | dstStream.close();
23 | }
24 |
25 | public static void makeParentDirs(String path) throws IOException {
26 | File parent = new File(path).getParentFile();
27 | if (!parent.exists()) {
28 | parent.mkdirs();
29 | }
30 | }
31 |
32 | public static void dumpException(String path, Throwable e) throws IOException {
33 | PrintWriter writer = new PrintWriter(path);
34 | e.printStackTrace(writer);
35 | writer.close();
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/maigosoft/mcpdict/HelpActivity.java:
--------------------------------------------------------------------------------
1 | package maigosoft.mcpdict;
2 |
3 | import android.annotation.SuppressLint;
4 | import android.os.Bundle;
5 | import android.webkit.WebView;
6 |
7 | public class HelpActivity extends ActivityWithOptionsMenu {
8 | @SuppressLint("SetJavaScriptEnabled")
9 | @Override
10 | protected void onCreate(Bundle savedInstanceState) {
11 | super.onCreate(savedInstanceState);
12 | setContentView(R.layout.help_activity);
13 |
14 | WebView webview = (WebView) findViewById(R.id.web_view_help);
15 | webview.getSettings().setJavaScriptEnabled(true);
16 | webview.loadUrl("file:///android_asset/help/index.htm");
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/maigosoft/mcpdict/MCPDatabase.java:
--------------------------------------------------------------------------------
1 | package maigosoft.mcpdict;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 | import java.util.Locale;
6 |
7 | import android.content.Context;
8 | import android.content.SharedPreferences;
9 | import android.content.res.Resources;
10 | import android.database.Cursor;
11 | import android.database.sqlite.SQLiteDatabase;
12 | import android.database.sqlite.SQLiteQueryBuilder;
13 | import android.preference.PreferenceManager;
14 |
15 | import com.readystatesoftware.sqliteasset.SQLiteAssetHelper;
16 |
17 | public class MCPDatabase extends SQLiteAssetHelper {
18 |
19 | private static final String DATABASE_NAME = "mcpdict";
20 | private static final int DATABASE_VERSION = 9;
21 |
22 | // Must be the same order as defined in the string array "search_as"
23 | public static final int SEARCH_AS_HZ = 0;
24 | public static final int SEARCH_AS_MC = 1;
25 | public static final int SEARCH_AS_PU = 2;
26 | public static final int SEARCH_AS_CT = 3;
27 | public static final int SEARCH_AS_SH = 4;
28 | public static final int SEARCH_AS_MN = 5;
29 | public static final int SEARCH_AS_KR = 6;
30 | public static final int SEARCH_AS_VN = 7;
31 | public static final int SEARCH_AS_JP_GO = 8;
32 | public static final int SEARCH_AS_JP_KAN = 9;
33 | public static final int SEARCH_AS_JP_ANY = 10;
34 |
35 | private static final String[] SEARCH_AS_TO_COLUMN_NAME = {
36 | "unicode", "mc", "pu", "ct", "sh", "mn", "kr", "vn", "jp_go", "jp_kan", null
37 | };
38 |
39 | private static Context context;
40 | private static SQLiteDatabase db = null;
41 |
42 | public static void initialize(Context c) {
43 | if (db != null) return;
44 | context = c;
45 | db = new MCPDatabase(context).getWritableDatabase();
46 | String userDbPath = UserDatabase.getDatabasePath();
47 | db.execSQL("ATTACH DATABASE '" + userDbPath + "' AS user");
48 | }
49 |
50 | public MCPDatabase(Context context) {
51 | super(context, DATABASE_NAME, null, DATABASE_VERSION);
52 | setForcedUpgradeVersion(DATABASE_VERSION);
53 | // Uncomment the following statements to force a database upgrade during development
54 | // SQLiteDatabase db = getWritableDatabase();
55 | // db.setVersion(-1);
56 | // db.close();
57 | // db = getWritableDatabase();
58 | }
59 |
60 | public static Cursor search(String input, int mode) {
61 | // Search for one or more keywords, considering mode and options
62 |
63 | // Get options and settings from SharedPreferences
64 | SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
65 | Resources r = context.getResources();
66 | boolean kuangxYonhOnly = sp.getBoolean(r.getString(R.string.pref_key_kuangx_yonh_only), false);
67 | boolean allowVariants = sp.getBoolean(r.getString(R.string.pref_key_allow_variants), true);
68 | boolean toneInsensitive = sp.getBoolean(r.getString(R.string.pref_key_tone_insensitive), false);
69 | int cantoneseSystem = sp.getInt(r.getString(R.string.pref_key_cantonese_romanization), 0);
70 |
71 | // Split the input string into keywords and canonicalize them
72 | List keywords = new ArrayList();
73 | List variants = new ArrayList();
74 | if (mode == SEARCH_AS_HZ) { // Each character is a query
75 | for (int i = 0; i < input.length(); i++) {
76 | char inputChar = input.charAt(i);
77 | if (!Orthography.Hanzi.isHanzi(inputChar)) continue;
78 | if (input.indexOf(inputChar) < i) continue; // Ignore a character if it has appeared earlier
79 | String inputHex = String.format("%04X", (int) inputChar);
80 | if (!allowVariants) {
81 | keywords.add(inputHex);
82 | }
83 | else {
84 | for (char variant : Orthography.Hanzi.getVariants(inputChar)) {
85 | String variantHex = String.format("%04X", (int) variant);
86 | int p = keywords.indexOf(variantHex);
87 | if (variant == inputChar) {
88 | if (p >= 0) { // The character itself must appear where it is
89 | keywords.remove(p);
90 | variants.remove(p);
91 | }
92 | keywords.add(inputHex);
93 | variants.add(null); // And no variant information is appended
94 | }
95 | else {
96 | if (p == -1) { // This variant character may have appeared before
97 | keywords.add(variantHex);
98 | variants.add(inputHex);
99 | }
100 | else {
101 | if (variants.get(p) != null) {
102 | variants.set(p, variants.get(p) + " " + inputHex);
103 | }
104 | }
105 | }
106 | }
107 | }
108 | }
109 | }
110 | else { // Each contiguous run of non-separator and non-comma characters is a query
111 | if (mode == SEARCH_AS_KR) { // For Korean, put separators around all hanguls
112 | StringBuilder sb = new StringBuilder();
113 | for (int i = 0; i < input.length(); i++) {
114 | char c = input.charAt(i);
115 | if (Orthography.Korean.isHangul(c)) {
116 | sb.append(" " + c + " ");
117 | }
118 | else {
119 | sb.append(c);
120 | }
121 | }
122 | input = sb.toString();
123 | }
124 | for (String token : input.split("[\\s,]+")) {
125 | if (token.equals("")) continue;
126 | token = token.toLowerCase(Locale.US);
127 | // Canonicalization
128 | switch (mode) {
129 | case SEARCH_AS_MC: token = Orthography.MiddleChinese.canonicalize(token); break;
130 | case SEARCH_AS_PU: token = Orthography.Mandarin.canonicalize(token); break;
131 | case SEARCH_AS_CT: token = Orthography.Cantonese.canonicalize(token, cantoneseSystem); break;
132 | case SEARCH_AS_SH: token = Orthography.Shanghai.canonicalize(token); break;
133 | case SEARCH_AS_MN: token = Orthography.Minnan.canonicalize(token); break;
134 | case SEARCH_AS_KR: token = Orthography.Korean.canonicalize(token); break;
135 | case SEARCH_AS_VN: token = Orthography.Vietnamese.canonicalize(token); break;
136 | case SEARCH_AS_JP_GO: case SEARCH_AS_JP_KAN: case SEARCH_AS_JP_ANY:
137 | token = Orthography.Japanese.canonicalize(token); break;
138 | }
139 | if (token == null) continue;
140 | List allTones = null;
141 | if (toneInsensitive) {
142 | switch (mode) {
143 | case SEARCH_AS_MC: allTones = Orthography.MiddleChinese.getAllTones(token); break;
144 | case SEARCH_AS_PU: allTones = Orthography.Mandarin.getAllTones(token); break;
145 | case SEARCH_AS_CT: allTones = Orthography.Cantonese.getAllTones(token); break;
146 | case SEARCH_AS_SH: allTones = Orthography.Shanghai.getAllTones(token); break;
147 | case SEARCH_AS_MN: allTones = Orthography.Minnan.getAllTones(token); break;
148 | case SEARCH_AS_VN: allTones = Orthography.Vietnamese.getAllTones(token); break;
149 | }
150 | }
151 | if (allTones != null) {
152 | keywords.addAll(allTones);
153 | }
154 | else {
155 | keywords.add(token);
156 | }
157 | }
158 | }
159 | if (keywords.isEmpty()) return null;
160 |
161 | // Columns to search
162 | String[] columns = (mode != SEARCH_AS_JP_ANY) ?
163 | new String[] {SEARCH_AS_TO_COLUMN_NAME[mode]} :
164 | new String[] {"jp_go", "jp_kan", "jp_tou", "jp_kwan", "jp_other"};
165 |
166 | // Build inner query statement (a union query returning the id's of matching Chinese characters)
167 | SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
168 | qb.setTables("mcpdict");
169 | List queries = new ArrayList();
170 | List args = new ArrayList();
171 | for (int i = 0; i < keywords.size(); i++) {
172 | String variant = (mode == SEARCH_AS_HZ && allowVariants && variants.get(i) != null) ?
173 | ("\"" + variants.get(i) + "\"") : "null";
174 | String[] projection = {"rowid AS _id", i + " AS rank", variant + " AS variants"};
175 | for (String column : columns) {
176 | queries.add(qb.buildQuery(projection, column + " MATCH ?", null, null, null, null));
177 | args.add(keywords.get(i));
178 | }
179 | }
180 | String query = qb.buildUnionQuery(queries.toArray(new String[0]), null, null);
181 |
182 | // Build outer query statement (returning all information about the matching Chinese characters)
183 | qb.setTables("(" + query + ") AS u, mcpdict AS v LEFT JOIN user.favorite AS w ON v.unicode = w.unicode");
184 | qb.setDistinct(true);
185 | String[] projection = {"_id",
186 | "v.unicode AS unicode", "variants",
187 | "mc", "pu", "ct", "sh", "mn", "kr", "vn",
188 | "jp_go", "jp_kan", "jp_tou", "jp_kwan", "jp_other",
189 | "timestamp IS NOT NULL AS is_favorite", "comment"};
190 | String selection = "u._id = v.rowid";
191 | if (kuangxYonhOnly) {
192 | selection += " AND mc IS NOT NULL";
193 | }
194 | query = qb.buildQuery(projection, selection, null, null, "rank", null);
195 |
196 | // Search
197 | return db.rawQuery(query, args.toArray(new String[0]));
198 | }
199 |
200 | @SuppressWarnings("deprecation")
201 | public static Cursor directSearch(char unicode) {
202 | // Search for a single Chinese character without any conversions
203 | SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
204 | qb.setTables("mcpdict AS v LEFT JOIN user.favorite AS w ON v.unicode = w.unicode");
205 | String[] projection = {"v.rowid AS _id",
206 | "v.unicode AS unicode", "NULL AS variants",
207 | "mc", "pu", "ct", "sh", "mn", "kr", "vn",
208 | "jp_go", "jp_kan", "jp_tou", "jp_kwan", "jp_other",
209 | "timestamp IS NOT NULL AS is_favorite", "comment"};
210 | String selection = "v.unicode = ?";
211 | String query = qb.buildQuery(projection, selection, null, null, null, null, null);
212 | String[] args = {String.format("%04X", (int) unicode)};
213 | return db.rawQuery(query, args);
214 | }
215 |
216 | }
217 |
--------------------------------------------------------------------------------
/src/maigosoft/mcpdict/MainActivity.java:
--------------------------------------------------------------------------------
1 | package maigosoft.mcpdict;
2 |
3 | import java.lang.reflect.Field;
4 |
5 | import android.os.AsyncTask;
6 | import android.os.Bundle;
7 | import android.support.v4.app.FragmentManager;
8 | import android.support.v4.app.FragmentTabHost;
9 | import android.view.View;
10 | import android.view.ViewConfiguration;
11 | import android.widget.TabHost;
12 | import android.widget.TextView;
13 |
14 | public class MainActivity extends ActivityWithOptionsMenu {
15 |
16 | private FragmentManager fm;
17 | private FragmentTabHost tabHost;
18 |
19 | @Override
20 | protected void onCreate(Bundle savedInstanceState) {
21 | // Initialize the some "static" classes on separate threads
22 | new AsyncTask() {
23 | @Override
24 | protected Void doInBackground(Void... params) {
25 | Orthography.initialize(getResources());
26 | return null;
27 | }
28 | }.execute();
29 |
30 | new AsyncTask() {
31 | @Override
32 | protected Void doInBackground(Void... params) {
33 | UserDatabase.initialize(MainActivity.this);
34 | MCPDatabase.initialize(MainActivity.this);
35 | return null;
36 | }
37 | }.execute();
38 |
39 | new AsyncTask() {
40 | @Override
41 | protected Void doInBackground(Void... params) {
42 | FavoriteDialogs.initialize(MainActivity.this);
43 | return null;
44 | }
45 | }.execute();
46 |
47 | // Force displaying the overflow menu in the action bar
48 | // Reference: http://stackoverflow.com/a/11438245
49 | // Only works for Android 4.x
50 | try {
51 | ViewConfiguration config = ViewConfiguration.get(this);
52 | Field menuKeyField = ViewConfiguration.class.getDeclaredField("sHasPermanentMenuKey");
53 | if (menuKeyField != null) {
54 | menuKeyField.setAccessible(true);
55 | menuKeyField.setBoolean(config, false);
56 | }
57 | }
58 | catch (Exception e) {
59 | // Ignore
60 | }
61 |
62 | // Set up activity layout
63 | super.onCreate(savedInstanceState);
64 | setContentView(R.layout.main_activity);
65 |
66 | // Set up the tabs
67 | tabHost = (FragmentTabHost) findViewById(android.R.id.tabhost);
68 | fm = getSupportFragmentManager();
69 | tabHost.setup(this, fm, android.R.id.tabcontent);
70 | @SuppressWarnings("rawtypes")
71 | Class[] fragmentClasses = {DictionaryFragment.class, FavoriteFragment.class};
72 | int[] titleIds = {R.string.tab_dictionary, R.string.tab_favorite};
73 | int nTabs = fragmentClasses.length;
74 | for (int i = 0; i < nTabs; i++) {
75 | String title = getString(titleIds[i]);
76 | tabHost.addTab(
77 | tabHost.newTabSpec(title).setIndicator(title),
78 | fragmentClasses[i],
79 | null
80 | );
81 | }
82 | tabHost.setOnTabChangedListener(new TabHost.OnTabChangeListener() {
83 | @Override
84 | public void onTabChanged(String tabId) {
85 | fm.executePendingTransactions();
86 | getCurrentFragment().refresh();
87 | }
88 | });
89 |
90 | // Styling of the tabs has to go here; XML doesn't work
91 | for (int i = 0; i < nTabs; i++) {
92 | View tab = tabHost.getTabWidget().getChildAt(i);
93 | TextView textView = (TextView) tab.findViewById(android.R.id.title);
94 | textView.setTextSize(17);
95 | }
96 | }
97 |
98 | @Override
99 | public void onRestart() {
100 | super.onRestart();
101 | // Make settings take effect immediately as the user navigates back to the dictionary
102 | DictionaryFragment fragment = getDictionaryFragment();
103 | if (fragment != null) {
104 | fragment.refresh();
105 | }
106 | }
107 |
108 | public RefreshableFragment getCurrentFragment() {
109 | return (RefreshableFragment) fm.findFragmentByTag(tabHost.getCurrentTabTag());
110 | }
111 |
112 | public DictionaryFragment getDictionaryFragment() {
113 | return (DictionaryFragment) fm.findFragmentByTag(getString(R.string.tab_dictionary));
114 | }
115 |
116 | public FavoriteFragment getFavoriteFragment() {
117 | return (FavoriteFragment) fm.findFragmentByTag(getString(R.string.tab_favorite));
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/src/maigosoft/mcpdict/Masks.java:
--------------------------------------------------------------------------------
1 | package maigosoft.mcpdict;
2 |
3 | public interface Masks {
4 | public static final int MASK_HZ = 0x1;
5 | public static final int MASK_UNICODE = 0x2;
6 |
7 | public static final int MASK_MC = 0x4;
8 | public static final int MASK_PU = 0x8;
9 | public static final int MASK_CT = 0x10;
10 | public static final int MASK_SH = 0x20;
11 | public static final int MASK_MN = 0x40;
12 | public static final int MASK_KR = 0x80;
13 | public static final int MASK_VN = 0x100;
14 | public static final int MASK_JP_GO = 0x200;
15 | public static final int MASK_JP_KAN = 0x400;
16 | public static final int MASK_JP_TOU = 0x800;
17 | public static final int MASK_JP_KWAN = 0x1000;
18 | public static final int MASK_JP_OTHER = 0x2000;
19 | public static final int MASK_JP_ALL = MASK_JP_GO | MASK_JP_KAN | MASK_JP_TOU | MASK_JP_KWAN | MASK_JP_OTHER;
20 | public static final int MASK_ALL_READINGS = MASK_MC | MASK_PU | MASK_CT | MASK_KR | MASK_VN | MASK_JP_ALL;
21 |
22 | public static final int MASK_FAVORITE = 0x4000;
23 | }
24 |
--------------------------------------------------------------------------------
/src/maigosoft/mcpdict/RefreshableFragment.java:
--------------------------------------------------------------------------------
1 | package maigosoft.mcpdict;
2 |
3 | public interface RefreshableFragment {
4 | public void refresh();
5 | }
6 |
--------------------------------------------------------------------------------
/src/maigosoft/mcpdict/SearchResultCursorAdapter.java:
--------------------------------------------------------------------------------
1 | package maigosoft.mcpdict;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 |
6 | import android.content.Context;
7 | import android.content.SharedPreferences;
8 | import android.content.res.Resources;
9 | import android.database.Cursor;
10 | import android.preference.PreferenceManager;
11 | import android.widget.CursorAdapter;
12 | import android.text.Spannable;
13 | import android.text.style.ForegroundColorSpan;
14 | import android.text.style.StyleSpan;
15 | import android.view.LayoutInflater;
16 | import android.view.View;
17 | import android.view.ViewGroup;
18 | import android.widget.Button;
19 | import android.widget.ImageView;
20 | import android.widget.TextView;
21 | import android.widget.TextView.BufferType;
22 |
23 | public class SearchResultCursorAdapter extends CursorAdapter implements Masks {
24 |
25 | private Context context;
26 | private int layout;
27 | private LayoutInflater inflater;
28 | private boolean showFavoriteButton;
29 |
30 | public SearchResultCursorAdapter(Context context, int layout, Cursor cursor, boolean showFavoriteButton) {
31 | super(context, cursor, FLAG_REGISTER_CONTENT_OBSERVER);
32 | this.context = context;
33 | this.layout = layout;
34 | this.inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
35 | this.showFavoriteButton = showFavoriteButton;
36 | }
37 |
38 | @Override
39 | public View newView(Context context, Cursor cursor, ViewGroup parent) {
40 | return inflater.inflate(layout, parent, false);
41 | }
42 |
43 | @Override
44 | public void bindView(final View view, final Context context, Cursor cursor) {
45 | final char unicode;
46 | String string;
47 | StringBuilder sb;
48 | TextView textView;
49 | int tag = 0;
50 |
51 | // Unicode
52 | string = cursor.getString(cursor.getColumnIndex("unicode"));
53 | textView = (TextView) view.findViewById(R.id.text_unicode);
54 | textView.setText("U+" + string);
55 | tag |= MASK_UNICODE;
56 |
57 | // Chinese character
58 | unicode = (char) Integer.parseInt(string, 16);
59 | string = String.valueOf(unicode);
60 | textView = (TextView) view.findViewById(R.id.text_hz);
61 | textView.setText(string);
62 | tag |= MASK_HZ;
63 |
64 | // Variants
65 | string = cursor.getString(cursor.getColumnIndex("variants"));
66 | textView = (TextView) view.findViewById(R.id.text_variants);
67 | if (string == null) {
68 | textView.setVisibility(View.GONE);
69 | }
70 | else {
71 | sb = new StringBuilder();
72 | for (String s : string.split(" ")) {
73 | sb.append((char) Integer.parseInt(s, 16));
74 | }
75 | textView.setText("(" + sb.toString() + ")");
76 | textView.setVisibility(View.VISIBLE);
77 | }
78 |
79 | // Middle Chinese
80 | string = cursor.getString(cursor.getColumnIndex("mc"));
81 | textView = (TextView) view.findViewById(R.id.text_mc);
82 | textView.setText(middleChineseDisplayer.display(string));
83 | textView = (TextView) view.findViewById(R.id.text_mc_detail);
84 | if (string != null) {
85 | textView.setText(middleChineseDetailDisplayer.display(string));
86 | tag |= MASK_MC;
87 | }
88 | else {
89 | textView.setText("");
90 | }
91 |
92 | // Mandarin
93 | string = cursor.getString(cursor.getColumnIndex("pu"));
94 | textView = (TextView) view.findViewById(R.id.text_pu);
95 | textView.setText(mandarinDisplayer.display(string));
96 | if (string != null) tag |= MASK_PU;
97 |
98 | // Cantonese
99 | string = cursor.getString(cursor.getColumnIndex("ct"));
100 | textView = (TextView) view.findViewById(R.id.text_ct);
101 | textView.setText(cantoneseDisplayer.display(string));
102 | if (string != null) tag |= MASK_CT;
103 |
104 | // Shanghai
105 | string = cursor.getString(cursor.getColumnIndex("sh"));
106 | textView = (TextView) view.findViewById(R.id.text_sh);
107 | setRichText(textView, shanghaiDisplayer.display(string));
108 | if (string != null) tag |= MASK_SH;
109 |
110 | // Minnan
111 | string = cursor.getString(cursor.getColumnIndex("mn"));
112 | textView = (TextView) view.findViewById(R.id.text_mn);
113 | setRichText(textView, minnanDisplayer.display(string));
114 | if (string != null) tag |= MASK_MN;
115 |
116 | // Korean
117 | string = cursor.getString(cursor.getColumnIndex("kr"));
118 | textView = (TextView) view.findViewById(R.id.text_kr);
119 | textView.setText(koreanDisplayer.display(string));
120 | if (string != null) tag |= MASK_KR;
121 |
122 | // Vietnamese
123 | string = cursor.getString(cursor.getColumnIndex("vn"));
124 | textView = (TextView) view.findViewById(R.id.text_vn);
125 | textView.setText(vietnameseDisplayer.display(string));
126 | if (string != null) tag |= MASK_VN;
127 |
128 | // Japanese go-on
129 | string = cursor.getString(cursor.getColumnIndex("jp_go"));
130 | textView = (TextView) view.findViewById(R.id.text_jp_go);
131 | setRichText(textView, japaneseDisplayer.display(string));
132 | if (string != null) tag |= MASK_JP_GO;
133 |
134 | // Japanese kan-on
135 | string = cursor.getString(cursor.getColumnIndex("jp_kan"));
136 | textView = (TextView) view.findViewById(R.id.text_jp_kan);
137 | setRichText(textView, japaneseDisplayer.display(string));
138 | if (string != null) tag |= MASK_JP_KAN;
139 |
140 | // Japanese extras
141 | ImageView[] imageViewJPExtras = {
142 | (ImageView) view.findViewById(R.id.image_jp_extra_1),
143 | (ImageView) view.findViewById(R.id.image_jp_extra_2),
144 | (ImageView) view.findViewById(R.id.image_jp_extra_3)
145 | };
146 | TextView[] textViewJPExtras = {
147 | (TextView) view.findViewById(R.id.text_jp_extra_1),
148 | (TextView) view.findViewById(R.id.text_jp_extra_2),
149 | (TextView) view.findViewById(R.id.text_jp_extra_3)
150 | };
151 | int i = 0;
152 |
153 | // Japanese tou-on
154 | string = cursor.getString(cursor.getColumnIndex("jp_tou"));
155 | if (string != null) {
156 | imageViewJPExtras[i].setImageResource(R.drawable.lang_jp_tou);
157 | setRichText(textViewJPExtras[i], japaneseDisplayer.display(string));
158 | i++;
159 | tag |= MASK_JP_TOU;
160 | }
161 |
162 | // Japanese kwan'you-on
163 | string = cursor.getString(cursor.getColumnIndex("jp_kwan"));
164 | if (string != null) {
165 | imageViewJPExtras[i].setImageResource(R.drawable.lang_jp_kwan);
166 | setRichText(textViewJPExtras[i], japaneseDisplayer.display(string));
167 | i++;
168 | tag |= MASK_JP_KWAN;
169 | }
170 |
171 | // Japanese other pronunciations
172 | string = cursor.getString(cursor.getColumnIndex("jp_other"));
173 | if (string != null) {
174 | imageViewJPExtras[i].setImageResource(R.drawable.lang_jp_other);
175 | setRichText(textViewJPExtras[i], japaneseDisplayer.display(string));
176 | i++;
177 | tag |= MASK_JP_OTHER;
178 | }
179 |
180 | for (int j = 0; j < i; j++) {
181 | imageViewJPExtras[j].setVisibility(View.VISIBLE);
182 | textViewJPExtras[j].setVisibility(View.VISIBLE);
183 | }
184 | for (int j = i; j < 3; j++) {
185 | imageViewJPExtras[j].setVisibility(View.GONE);
186 | textViewJPExtras[j].setVisibility(View.GONE);
187 | }
188 |
189 | // "Favorite" button
190 | boolean favorite = cursor.getInt(cursor.getColumnIndex("is_favorite")) == 1;
191 | Button button = (Button) view.findViewById(R.id.button_favorite);
192 | button.setOnClickListener(new View.OnClickListener() {
193 | @Override
194 | public void onClick(View v) {
195 | int tag = (Integer) view.getTag();
196 | if ((tag & MASK_FAVORITE) == 0) {
197 | FavoriteDialogs.add(unicode);
198 | }
199 | else {
200 | FavoriteDialogs.view(unicode, view);
201 | }
202 | }
203 | });
204 | if (showFavoriteButton) {
205 | button.setBackgroundResource(favorite ? R.drawable.ic_star_yellow : R.drawable.ic_star_white);
206 | }
207 | else {
208 | button.setVisibility(View.GONE);
209 | }
210 | if (favorite) {
211 | tag |= MASK_FAVORITE;
212 | }
213 |
214 | // Favorite comment
215 | string = cursor.getString(cursor.getColumnIndex("comment"));
216 | textView = (TextView) view.findViewById(R.id.text_comment);
217 | textView.setText(string);
218 |
219 | // Set the view's tag to indicate which readings exist
220 | view.setTag(tag);
221 | }
222 |
223 | public void setRichText(TextView view, String richTextString) {
224 | StringBuilder sb = new StringBuilder();
225 | List bolds = new ArrayList();
226 | List dims = new ArrayList();
227 |
228 | for (int i = 0; i < richTextString.length(); i++) {
229 | char c = richTextString.charAt(i);
230 | switch (c) {
231 | case '*': bolds.add(sb.length()); break;
232 | case '|': dims.add(sb.length()); break;
233 | default : sb.append(c); break;
234 | }
235 | }
236 |
237 | view.setText(sb.toString(), BufferType.SPANNABLE);
238 | Spannable spannable = (Spannable) view.getText();
239 | for (int i = 1; i < bolds.size(); i += 2) {
240 | spannable.setSpan(new StyleSpan(android.graphics.Typeface.BOLD), bolds.get(i-1), bolds.get(i), Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
241 | }
242 | for (int i = 1; i < dims.size(); i += 2) {
243 | spannable.setSpan(new ForegroundColorSpan(0xFF808080), dims.get(i-1), dims.get(i), Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
244 | }
245 | }
246 |
247 | private abstract static class Displayer {
248 | protected static final String NULL_STRING = "-";
249 |
250 | public String display(String s) {
251 | if (s == null) return NULL_STRING;
252 | s = lineBreak(s);
253 | // Find all regions of [a-z0-9]+ in s, and apply displayer to each of them
254 | StringBuilder sb = new StringBuilder();
255 | int L = s.length(), p = 0;
256 | while (p < L) {
257 | int q = p;
258 | while (q < L && Character.isLetterOrDigit(s.charAt(q))) q++;
259 | if (q > p) {
260 | String t1 = s.substring(p, q);
261 | String t2 = displayOne(t1);
262 | sb.append(t2 == null ? t1 : t2);
263 | p = q;
264 | }
265 | while (p < L && !Character.isLetterOrDigit(s.charAt(p))) p++;
266 | sb.append(s.substring(q, p));
267 | }
268 | // Add spaces as hints for line wrapping
269 | s = sb.toString().replace(",", ", ")
270 | .replace("(", " (")
271 | .replace("]", "] ")
272 | .replaceAll(" +", " ")
273 | .replace(" ,", ",")
274 | .trim();
275 | return s;
276 | }
277 |
278 | public String lineBreak(String s) {return s;}
279 | public abstract String displayOne(String s);
280 | }
281 |
282 | private final Displayer middleChineseDisplayer = new Displayer() {
283 | public String lineBreak(String s) {return s.replace(",", "\n");}
284 | public String displayOne(String s) {return Orthography.MiddleChinese.display(s);}
285 | };
286 |
287 | private final Displayer middleChineseDetailDisplayer = new Displayer() {
288 | public String lineBreak(String s) {return s.replace(",", "\n");}
289 | public String displayOne(String s) {return "(" + Orthography.MiddleChinese.detail(s) + ")";}
290 | public String display(String s) {return " " + super.display(s);}
291 | };
292 |
293 | private final Displayer mandarinDisplayer = new Displayer() {
294 | public String displayOne(String s) {
295 | SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
296 | Resources r = context.getResources();
297 | int style = sp.getInt(r.getString(R.string.pref_key_mandarin_display), 0);
298 | return Orthography.Mandarin.display(s, style);
299 | }
300 | };
301 |
302 | private final Displayer cantoneseDisplayer = new Displayer() {
303 | public String displayOne(String s) {
304 | SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
305 | Resources r = context.getResources();
306 | int system = sp.getInt(r.getString(R.string.pref_key_cantonese_romanization), 0);
307 | return Orthography.Cantonese.display(s, system);
308 | }
309 | };
310 |
311 | private final Displayer shanghaiDisplayer = new Displayer() {
312 | public String displayOne(String s) {
313 | return Orthography.Shanghai.display(s);
314 | }
315 | };
316 |
317 | private final Displayer minnanDisplayer = new Displayer() {
318 | public String displayOne(String s) {
319 | return Orthography.Minnan.display(s);
320 | }
321 | };
322 |
323 | private final Displayer koreanDisplayer = new Displayer() {
324 | public String displayOne(String s) {
325 | SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
326 | Resources r = context.getResources();
327 | int style = sp.getInt(r.getString(R.string.pref_key_korean_display), 0);
328 | return Orthography.Korean.display(s, style);
329 | }
330 | };
331 |
332 | private final Displayer vietnameseDisplayer = new Displayer() {
333 | public String displayOne(String s) {
334 | SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
335 | Resources r = context.getResources();
336 | int style = sp.getInt(r.getString(R.string.pref_key_vietnamese_tone_position), 0);
337 | return Orthography.Vietnamese.display(s, style);
338 | }
339 | };
340 |
341 | private final Displayer japaneseDisplayer = new Displayer() {
342 | public String lineBreak(String s) {
343 | if (s.charAt(0) == '[') {
344 | s = '[' + s.substring(1).replace("[", "\n[");
345 | }
346 | return s;
347 | }
348 |
349 | public String displayOne(String s) {
350 | SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
351 | Resources r = context.getResources();
352 | int style = sp.getInt(r.getString(R.string.pref_key_japanese_display), 0);
353 | return Orthography.Japanese.display(s, style);
354 | }
355 | };
356 | }
357 |
--------------------------------------------------------------------------------
/src/maigosoft/mcpdict/SearchResultFragment.java:
--------------------------------------------------------------------------------
1 | package maigosoft.mcpdict;
2 |
3 | import java.io.UnsupportedEncodingException;
4 | import java.net.URLEncoder;
5 | import java.util.HashMap;
6 | import java.util.Map;
7 |
8 | import android.annotation.SuppressLint;
9 | import android.content.Context;
10 | import android.content.Intent;
11 | import android.database.Cursor;
12 | import android.net.Uri;
13 | import android.os.Bundle;
14 | import android.support.v4.app.ListFragment;
15 | import android.text.ClipboardManager;
16 | import android.view.ContextMenu;
17 | import android.view.ContextMenu.ContextMenuInfo;
18 | import android.view.LayoutInflater;
19 | import android.view.Menu;
20 | import android.view.MenuItem;
21 | import android.view.SubMenu;
22 | import android.view.View;
23 | import android.view.ViewGroup;
24 | import android.widget.AdapterView.AdapterContextMenuInfo;
25 | import android.widget.ListView;
26 | import android.widget.TextView;
27 | import android.widget.Toast;
28 |
29 | import com.mobiRic.ui.widget.Boast;
30 |
31 | @SuppressWarnings("deprecation")
32 | public class SearchResultFragment extends ListFragment implements Masks {
33 |
34 | @SuppressLint("UseSparseArrays")
35 | private static final Map COPY_MENU_ITEM_TO_MASK = new HashMap();
36 | static {
37 | COPY_MENU_ITEM_TO_MASK.put(R.id.menu_item_copy_hz, MASK_HZ);
38 | COPY_MENU_ITEM_TO_MASK.put(R.id.menu_item_copy_unicode, MASK_UNICODE);
39 | COPY_MENU_ITEM_TO_MASK.put(R.id.menu_item_copy_all, MASK_ALL_READINGS);
40 | COPY_MENU_ITEM_TO_MASK.put(R.id.menu_item_copy_mc, MASK_MC);
41 | COPY_MENU_ITEM_TO_MASK.put(R.id.menu_item_copy_pu, MASK_PU);
42 | COPY_MENU_ITEM_TO_MASK.put(R.id.menu_item_copy_ct, MASK_CT);
43 | COPY_MENU_ITEM_TO_MASK.put(R.id.menu_item_copy_sh, MASK_SH);
44 | COPY_MENU_ITEM_TO_MASK.put(R.id.menu_item_copy_mn, MASK_MN);
45 | COPY_MENU_ITEM_TO_MASK.put(R.id.menu_item_copy_kr, MASK_KR);
46 | COPY_MENU_ITEM_TO_MASK.put(R.id.menu_item_copy_vn, MASK_VN);
47 | COPY_MENU_ITEM_TO_MASK.put(R.id.menu_item_copy_jp_all, MASK_JP_ALL);
48 | COPY_MENU_ITEM_TO_MASK.put(R.id.menu_item_copy_jp_go, MASK_JP_GO);
49 | COPY_MENU_ITEM_TO_MASK.put(R.id.menu_item_copy_jp_kan, MASK_JP_KAN);
50 | COPY_MENU_ITEM_TO_MASK.put(R.id.menu_item_copy_jp_tou, MASK_JP_TOU);
51 | COPY_MENU_ITEM_TO_MASK.put(R.id.menu_item_copy_jp_kwan, MASK_JP_KWAN);
52 | COPY_MENU_ITEM_TO_MASK.put(R.id.menu_item_copy_jp_other, MASK_JP_OTHER);
53 | }
54 |
55 | private static final int[] DICT_LINK_MASKS = {
56 | MASK_MC, MASK_PU, MASK_CT, MASK_SH, MASK_MN, MASK_KR, MASK_VN
57 | };
58 |
59 | private static final String[] DICT_LINK_BASES = {
60 | "http://ytenx.org/zim?kyonh=1&dzih=", // plus UTF-8 encoded string
61 | "http://www.zdic.net/sousuo/?q=", // plus UTF-8 encoded string
62 | "http://humanum.arts.cuhk.edu.hk/Lexis/lexi-can/search.php?q=",
63 | // plus Big5 encoded string
64 | "http://www.wu-chinese.com/minidict/search.php?searchlang=zaonhe&searchkey=",
65 | // plus UTF-8 encoded string
66 | "http://twblg.dict.edu.tw/holodict_new/result.jsp?querytarget=1&radiobutton=0&limit=20&sample=",
67 | // plus UTF-8 encoded string
68 | "http://hanja.naver.com/hanja?q=", // plus UTF-8 encoded string
69 | "http://hanviet.org/hv_timchu.php?unichar=", // plus UTF-8 encoded string
70 | }; // Bases of links to external dictionaries
71 |
72 | private View selfView;
73 | private ListView listView;
74 | private SearchResultCursorAdapter adapter;
75 | private boolean showFavoriteButton;
76 | private View selectedEntry;
77 |
78 | private static SearchResultFragment selectedFragment;
79 |
80 | public SearchResultFragment() {
81 | this(true);
82 | }
83 |
84 | public SearchResultFragment(boolean showFavoriteButton) {
85 | super();
86 | this.showFavoriteButton = showFavoriteButton;
87 | }
88 |
89 | @Override
90 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
91 | // A hack to avoid nested fragments from being inflated twice
92 | // Reference: http://stackoverflow.com/a/14695397
93 | if (selfView != null) {
94 | ViewGroup parent = (ViewGroup) selfView.getParent();
95 | if (parent != null) parent.removeView(selfView);
96 | return selfView;
97 | }
98 |
99 | // Inflate the fragment view
100 | selfView = inflater.inflate(R.layout.search_result_fragment, container, false);
101 |
102 | // Get a reference to the ListView
103 | listView = (ListView) selfView.findViewById(android.R.id.list);
104 |
105 | // Set up a context menu for each item of the search result
106 | registerForContextMenu(listView);
107 |
108 | return selfView;
109 | }
110 |
111 | @Override
112 | public void onActivityCreated(Bundle savedInstanceState) {
113 | super.onActivityCreated(savedInstanceState);
114 |
115 | // Set up the adapter
116 | if (adapter == null) {
117 | adapter = new SearchResultCursorAdapter(
118 | getActivity(),
119 | R.layout.search_result_item,
120 | null,
121 | showFavoriteButton
122 | );
123 | setListAdapter(adapter);
124 | }
125 | }
126 |
127 | @Override
128 | public void onListItemClick(ListView list, View view, int position, long id) {
129 | // Show context menu on short clicks, too
130 | list.showContextMenuForChild(view);
131 | }
132 |
133 | @Override
134 | public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfo) {
135 | selectedFragment = this;
136 | // This is a bug with Android: when a context menu item is clicked,
137 | // all fragments of this class receive a call to onContextItemSelected.
138 | // Therefore we need to remember which fragment created the context menu.
139 |
140 | // Find the Chinese character in the view being clicked
141 | ListView list = (ListView) view;
142 | AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo;
143 | int position = info.position - list.getFirstVisiblePosition();
144 | // info.position is the position of the item in the entire list
145 | // but list.getChildAt() on the next line requires the position of the item in currently visible items
146 | selectedEntry = list.getChildAt(position);
147 | TextView text = (TextView) selectedEntry.findViewById(R.id.text_hz);
148 | String hanzi = text.getText().toString();
149 | char unicode = hanzi.charAt(0);
150 | int tag = (Integer) selectedEntry.getTag();
151 |
152 | // Inflate the context menu
153 | getActivity().getMenuInflater().inflate(R.menu.search_result_context_menu, menu);
154 | SubMenu menuCopy = menu.getItem(0).getSubMenu();
155 | SubMenu menuDictLinks = menu.getItem(1).getSubMenu();
156 | MenuItem item;
157 |
158 | // Determine whether to retain each item in the "copy" sub-menu
159 | for (int i = menuCopy.size() - 1; i >= 0; i--) {
160 | int id = menuCopy.getItem(i).getItemId();
161 | int mask = COPY_MENU_ITEM_TO_MASK.get(id);
162 | if ((tag & mask) == 0) menuCopy.removeItem(id);
163 | }
164 |
165 | // Determine whether to enable each item in the sub-menu of external dictionaries,
166 | // and generate links for enabled items
167 | String utf8 = null;
168 | String big5 = null;
169 | try {utf8 = URLEncoder.encode(hanzi, "utf-8");} catch (UnsupportedEncodingException e) {}
170 | try {big5 = URLEncoder.encode(hanzi, "big5");} catch (UnsupportedEncodingException e) {}
171 | if (big5.equals("%3F")) big5 = null; // Unsupported character
172 | String[] linkArgs = {utf8, utf8, big5, utf8, utf8, utf8, utf8};
173 | for (int i = 0; i < menuDictLinks.size(); i++) {
174 | item = menuDictLinks.getItem(i);
175 | if ((tag & DICT_LINK_MASKS[i]) != 0) {
176 | item.setEnabled(true);
177 | item.setIntent(new Intent(Intent.ACTION_VIEW, Uri.parse(DICT_LINK_BASES[i] + linkArgs[i])));
178 | }
179 | else {
180 | item.setEnabled(false);
181 | }
182 | }
183 |
184 | // Determine the functionality of the "favorite" item
185 | item = menu.getItem(2);
186 | item.setTitle((tag & MASK_FAVORITE) == 0 ? R.string.favorite_add : R.string.favorite_view_or_edit);
187 |
188 | // Replace the placeholders in the menu items with the character selected
189 | for (Menu m : new Menu[] {menu, menuCopy, menuDictLinks}) {
190 | for (int i = 0; i < m.size(); i++) {
191 | item = m.getItem(i);
192 | item.setTitle(String.format(item.getTitle().toString(), unicode));
193 | }
194 | }
195 | }
196 |
197 | @Override
198 | public boolean onContextItemSelected(MenuItem item) {
199 | if (selectedFragment != this) return false;
200 | if (COPY_MENU_ITEM_TO_MASK.containsKey(item.getItemId())) {
201 | // Generate the text to copy to the clipboard
202 | String text = getCopyText(selectedEntry, COPY_MENU_ITEM_TO_MASK.get(item.getItemId()));
203 | ClipboardManager clipboard = (ClipboardManager) getActivity().getSystemService(Context.CLIPBOARD_SERVICE);
204 | clipboard.setText(text);
205 | String label = item.getTitle().toString().substring(2); // this is ugly
206 | String message = String.format(getString(R.string.copy_done), label);
207 | Boast.showText(getActivity(), message, Toast.LENGTH_SHORT);
208 | return true;
209 | }
210 | else if (item.getItemId() == R.id.menu_item_favorite) {
211 | selectedEntry.findViewById(R.id.button_favorite).performClick();
212 | return true;
213 | }
214 | else {
215 | // Fall back to default behavior
216 | return false;
217 | }
218 | }
219 |
220 | private String getCopyText(View entry, int mask) {
221 | int tag = (Integer) entry.getTag();
222 | if ((tag & mask) == 0) return null;
223 |
224 | TextView[] textViewJPExtras = {
225 | (TextView) entry.findViewById(R.id.text_jp_extra_1),
226 | (TextView) entry.findViewById(R.id.text_jp_extra_2),
227 | (TextView) entry.findViewById(R.id.text_jp_extra_3)
228 | };
229 | StringBuilder sb;
230 |
231 | switch (mask) {
232 | case MASK_HZ:
233 | return ((TextView) entry.findViewById(R.id.text_hz)).getText().toString();
234 | case MASK_UNICODE:
235 | return ((TextView) entry.findViewById(R.id.text_unicode)).getText().toString();
236 | case MASK_MC:
237 | String[] readings = ((TextView) entry.findViewById(R.id.text_mc)).getText().toString().split("\n");
238 | String[] details = ((TextView) entry.findViewById(R.id.text_mc_detail)).getText().toString().split("\n");
239 | String text = "";
240 | for (int i = 0; i < readings.length; i++) {
241 | if (i > 0) text += "\n";
242 | text += readings[i] + details[i];
243 | }
244 | return text;
245 | case MASK_PU:
246 | return ((TextView) entry.findViewById(R.id.text_pu)).getText().toString();
247 | case MASK_CT:
248 | return ((TextView) entry.findViewById(R.id.text_ct)).getText().toString();
249 | case MASK_SH:
250 | return ((TextView) entry.findViewById(R.id.text_sh)).getText().toString();
251 | case MASK_MN:
252 | return ((TextView) entry.findViewById(R.id.text_mn)).getText().toString();
253 | case MASK_KR:
254 | return ((TextView) entry.findViewById(R.id.text_kr)).getText().toString();
255 | case MASK_VN:
256 | return ((TextView) entry.findViewById(R.id.text_vn)).getText().toString();
257 | case MASK_JP_GO:
258 | return ((TextView) entry.findViewById(R.id.text_jp_go)).getText().toString();
259 | case MASK_JP_KAN:
260 | return ((TextView) entry.findViewById(R.id.text_jp_kan)).getText().toString();
261 | case MASK_JP_TOU:
262 | return textViewJPExtras[0].getText().toString();
263 | case MASK_JP_KWAN:
264 | return textViewJPExtras[(tag & MASK_JP_TOU) > 0 ? 1 : 0].getText().toString();
265 | case MASK_JP_OTHER:
266 | return textViewJPExtras[((tag & MASK_JP_TOU) > 0 ? 1 : 0) +
267 | ((tag & MASK_JP_KWAN) > 0 ? 1 : 0)].getText().toString();
268 | case MASK_JP_ALL:
269 | sb = new StringBuilder();
270 | if ((tag & MASK_JP_GO) > 0) sb.append(formatReading("吳", getCopyText(entry, MASK_JP_GO)));
271 | if ((tag & MASK_JP_KAN) > 0) sb.append(formatReading("漢", getCopyText(entry, MASK_JP_KAN)));
272 | if ((tag & MASK_JP_TOU) > 0) sb.append(formatReading("唐", getCopyText(entry, MASK_JP_TOU)));
273 | if ((tag & MASK_JP_KWAN) > 0) sb.append(formatReading("慣", getCopyText(entry, MASK_JP_KWAN)));
274 | if ((tag & MASK_JP_OTHER) > 0) sb.append(formatReading("他", getCopyText(entry, MASK_JP_OTHER)));
275 | return sb.toString();
276 | case MASK_ALL_READINGS:
277 | sb = new StringBuilder();
278 | String hanzi = ((TextView) entry.findViewById(R.id.text_hz)).getText().toString();
279 | String unicode = ((TextView) entry.findViewById(R.id.text_unicode)).getText().toString();
280 | sb.append(hanzi + " " + unicode + "\n");
281 | if ((tag & MASK_MC) > 0) sb.append(formatReading("中古", getCopyText(entry, MASK_MC)));
282 | if ((tag & MASK_PU) > 0) sb.append(formatReading("普", getCopyText(entry, MASK_PU)));
283 | if ((tag & MASK_CT) > 0) sb.append(formatReading("粵", getCopyText(entry, MASK_CT)));
284 | if ((tag & MASK_SH) > 0) sb.append(formatReading("吳", getCopyText(entry, MASK_SH)));
285 | if ((tag & MASK_MN) > 0) sb.append(formatReading("閩", getCopyText(entry, MASK_MN)));
286 | if ((tag & MASK_KR) > 0) sb.append(formatReading("朝", getCopyText(entry, MASK_KR)));
287 | if ((tag & MASK_VN) > 0) sb.append(formatReading("越", getCopyText(entry, MASK_VN)));
288 | if ((tag & MASK_JP_GO) > 0) sb.append(formatReading("日·吳", getCopyText(entry, MASK_JP_GO)));
289 | if ((tag & MASK_JP_KAN) > 0) sb.append(formatReading("日·漢", getCopyText(entry, MASK_JP_KAN)));
290 | if ((tag & MASK_JP_TOU) > 0) sb.append(formatReading("日·唐", getCopyText(entry, MASK_JP_TOU)));
291 | if ((tag & MASK_JP_KWAN) > 0) sb.append(formatReading("日·慣", getCopyText(entry, MASK_JP_KWAN)));
292 | if ((tag & MASK_JP_OTHER) > 0) sb.append(formatReading("日·他", getCopyText(entry, MASK_JP_OTHER)));
293 | return sb.toString();
294 | }
295 | return null;
296 | }
297 |
298 | private String formatReading(String prefix, String reading) {
299 | String separator = reading.contains("\n") ? "\n" : " ";
300 | return "[" + prefix + "]" + separator + reading + "\n";
301 | }
302 |
303 | public void setData(Cursor data) {
304 | if (adapter == null) return;
305 | adapter.changeCursor(data);
306 | }
307 |
308 | public void scrollToTop() {
309 | listView.setSelectionAfterHeaderView();
310 | }
311 | }
312 |
--------------------------------------------------------------------------------
/src/maigosoft/mcpdict/SettingsActivity.java:
--------------------------------------------------------------------------------
1 | package maigosoft.mcpdict;
2 |
3 | import android.os.Bundle;
4 |
5 | public class SettingsActivity extends ActivityWithOptionsMenu {
6 | @Override
7 | protected void onCreate(Bundle savedInstanceState) {
8 | super.onCreate(savedInstanceState);
9 | setContentView(R.layout.settings_activity);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/maigosoft/mcpdict/SettingsFragment.java:
--------------------------------------------------------------------------------
1 | package maigosoft.mcpdict;
2 |
3 | import android.os.Bundle;
4 | import android.preference.PreferenceFragment;
5 |
6 | public class SettingsFragment extends PreferenceFragment {
7 | @Override
8 | public void onCreate(Bundle savedInstanceState) {
9 | super.onCreate(savedInstanceState);
10 |
11 | // Load the preferences from an XML resource
12 | addPreferencesFromResource(R.xml.preferences);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/maigosoft/mcpdict/UserDatabase.java:
--------------------------------------------------------------------------------
1 | package maigosoft.mcpdict;
2 |
3 | import java.io.IOException;
4 |
5 | import android.content.ContentValues;
6 | import android.content.Context;
7 | import android.database.Cursor;
8 | import android.database.sqlite.SQLiteDatabase;
9 | import android.database.sqlite.SQLiteOpenHelper;
10 |
11 | class UserDatabase extends SQLiteOpenHelper {
12 |
13 | // STATIC VARIABLES AND METHODS
14 |
15 | private static final String DATABASE_NAME = "user";
16 | private static final int DATABASE_VERSION = 1;
17 |
18 | private static Context context;
19 | private static SQLiteDatabase db = null;
20 |
21 | public static void initialize(Context c) {
22 | if (db != null) return;
23 | context = c;
24 | db = new UserDatabase(context).getWritableDatabase();
25 | }
26 |
27 | public static String getDatabasePath() {
28 | return context.getDatabasePath(DATABASE_NAME).getAbsolutePath();
29 | }
30 |
31 | public static String getBackupPath() {
32 | return context.getExternalFilesDir(null) + "/" + DATABASE_NAME + ".db";
33 | }
34 |
35 | // "READ" OPERATIONS
36 |
37 | public static Cursor selectAllFavorites() {
38 | String query = "SELECT rowid AS _id, unicode, comment, " +
39 | "STRFTIME('%Y/%m/%d', timestamp, 'localtime') AS local_timestamp " +
40 | "FROM favorite ORDER BY timestamp DESC";
41 | Cursor data = db.rawQuery(query, null);
42 | return data;
43 | }
44 |
45 | // "WRITE" OPERATIONS
46 |
47 | public static void insertFavorite(char unicode, String comment) {
48 | ContentValues values = new ContentValues();
49 | values.put("unicode", String.format("%04X", (int) unicode));
50 | values.put("comment", comment);
51 | db.insert("favorite", null, values);
52 | }
53 |
54 | public static void updateFavorite(char unicode, String comment) {
55 | ContentValues values = new ContentValues();
56 | values.put("comment", comment);
57 | String[] args = {String.format("%04X", (int) unicode)};
58 | db.update("favorite", values, "unicode = ?", args);
59 | }
60 |
61 | public static void deleteFavorite(char unicode) {
62 | String[] args = {String.format("%04X", (int) unicode)};
63 | db.delete("favorite", "unicode = ?", args);
64 | }
65 |
66 | public static void deleteAllFavorites() {
67 | db.delete("favorite", null, null);
68 | }
69 |
70 | // EXPORTING AND IMPORTING
71 |
72 | public static void exportFavorites() throws IOException {
73 | FileUtils.copyFile(getDatabasePath(), getBackupPath());
74 | }
75 |
76 | public static int selectBackupFavoriteCount() {
77 | db.execSQL("ATTACH DATABASE '" + getBackupPath() + "' AS backup");
78 | String query = "SELECT rowid, unicode, comment, timestamp FROM backup.favorite";
79 | int count = db.rawQuery(query, null).getCount();
80 | db.execSQL("DETACH DATABASE backup");
81 | return count;
82 | }
83 |
84 | public static void importFavoritesOverwrite() throws IOException {
85 | FileUtils.copyFile(getBackupPath(), getDatabasePath());
86 | }
87 |
88 | public static void importFavoritesMix() {
89 | db.execSQL("ATTACH DATABASE '" + getBackupPath() + "' AS backup");
90 | db.execSQL("DELETE FROM favorite WHERE unicode IN (SELECT unicode FROM backup.favorite)");
91 | db.execSQL("INSERT INTO favorite(unicode, comment, timestamp) SELECT unicode, comment, timestamp FROM backup.favorite");
92 | db.execSQL("DETACH DATABASE backup");
93 | }
94 |
95 | public static void importFavoritesAppend() {
96 | db.execSQL("ATTACH DATABASE '" + getBackupPath() + "' AS backup");
97 | db.execSQL("DELETE FROM favorite WHERE unicode IN (SELECT unicode FROM backup.favorite)");
98 | db.execSQL("INSERT INTO favorite(unicode, comment) SELECT unicode, comment FROM backup.favorite");
99 | db.execSQL("DETACH DATABASE backup");
100 | }
101 |
102 | // NON-STATIC METHODS IMPLEMENTING THOSE OF THE ABSTRACT SUPER-CLASS
103 |
104 | public UserDatabase(Context context) {
105 | super(context, DATABASE_NAME, null, DATABASE_VERSION);
106 | }
107 |
108 | @Override
109 | public void onCreate(SQLiteDatabase db) {
110 | db.execSQL("CREATE TABLE favorite (" +
111 | " unicode TEXT UNIQUE NOT NULL," +
112 | " comment STRING," +
113 | " timestamp REAL DEFAULT (JULIANDAY('now')) NOT NULL" +
114 | ")");
115 | }
116 |
117 | @Override
118 | public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {}
119 | }
120 |
--------------------------------------------------------------------------------