diff --git a/.github/workflows/build-debug-apk.yml b/.github/workflows/build-debug-apk.yml index 6f3fb94a..831f7042 100644 --- a/.github/workflows/build-debug-apk.yml +++ b/.github/workflows/build-debug-apk.yml @@ -20,7 +20,7 @@ jobs: distribution: 'temurin' java-version: 17 - run: ./gradlew clean && ./gradlew assembleDebug - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: name: app-debug.apk path: app/build/outputs/apk/debug/app-debug.apk diff --git a/CHANGELOG.md b/CHANGELOG.md index 9024e36d..9abff3a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,39 @@ ## [Unreleased] +## [v1.4.4] (70) Cursed be Android 16 again (2025-08-24) + +- Fixed keyboard height not immediately updating in Android 16 + + +## [v1.4.3] (69) Cursed be Android 16 (2025-06-29) + +### User + +- Fixed keyboard failing to spawn in Android 16 + +### Developer + +- Upgraded Android Gradle Plugin to 8.10.1 +- Fixed various Gradle-related deprecations +- Replaced `printStackTrace()` with logging +- Replaced `size()` and `length()` comparisons with `isEmpty()` check + + +## [v1.4.2] (68) 煮者 (2025-01-12) + +- Fixed candidate duplication when exact match is also a prefix match (者, 煮) +- Rewrote `addCodePointsToSet` as `toCodePointSet` +- Eliminated suffix Hungarian notation outside method names +- Improved Help > Miscellaneous table + - Put Function column before Action column in table + - Moved 'Retract keyboard' explanation into table +- Made minor edits to Privacy Policy + - English: added parenthetical "(English version)" + - Chinese: added parenthetical "(中文版)", reduced "不會" to "不", expanded "英文" to "英文版" +- Updated privacy policy button URL anchor for said minor edits + + ## [v1.4.1] (67) 不鏽鋼 (2024-12-29) - Updated stroke input data to [Conway Stroke Data v1.34.0] @@ -663,7 +696,13 @@ the actual functionality has not been implemented yet. [Unreleased]: - https://github.com/stroke-input/stroke-input-android/compare/v1.4.1...HEAD + https://github.com/stroke-input/stroke-input-android/compare/v1.4.4...HEAD +[v1.4.4]: + https://github.com/stroke-input/stroke-input-android/compare/v1.4.3...v1.4.4 +[v1.4.3]: + https://github.com/stroke-input/stroke-input-android/compare/v1.4.2...v1.4.3 +[v1.4.2]: + https://github.com/stroke-input/stroke-input-android/compare/v1.4.1...v1.4.2 [v1.4.1]: https://github.com/stroke-input/stroke-input-android/compare/v1.4.0...v1.4.1 [v1.4.0]: diff --git a/PRIVACY-zh.md b/PRIVACY-zh.md index 64dc2152..429735e1 100644 --- a/PRIVACY-zh.md +++ b/PRIVACY-zh.md @@ -1,11 +1,11 @@ -# 私隱政策 +# 私隱政策(中文版) -**此應用程式(筆畫輸入法)不會收集任何個人資料。** +**此應用程式(筆畫輸入法)不收集任何個人資料。** 歡迎並鼓勵考察原碼,<>。 倘發現漏洞或私隱問題,請告諸 <>。 --- -[English version of Privacy Policy](PRIVACY.md#privacy-policy)
-(凡中英文有出入者,則以英文為主。) +[Privacy Policy (English version)](PRIVACY.md#privacy-policy-english-version)
+(凡中英文有出入者,則以英文版為主。) diff --git a/PRIVACY.md b/PRIVACY.md index 839f0138..927a341d 100644 --- a/PRIVACY.md +++ b/PRIVACY.md @@ -1,4 +1,4 @@ -# Privacy Policy +# Privacy Policy (English version) **This app (Stroke Input Method) does not collect any personal data.** @@ -9,6 +9,6 @@ please report to <>. --- -[私隱政策中文版](PRIVACY-zh.md#私隱政策)
+[私隱政策(中文版)](PRIVACY-zh.md#私隱政策中文版)
(If there be any discrepancy between the English and the Chinese, the English version shall prevail.) diff --git a/README.md b/README.md index 7100701d..a33bf001 100644 --- a/README.md +++ b/README.md @@ -36,13 +36,13 @@ Uses [Conway Stroke Data]. [f-droid]: https://f-droid.org/en/packages/io.github.yawnoc.strokeinput/ [google-play]: https://play.google.com/store/apps/details?id=io.github.yawnoc.strokeinput -- [English version of Privacy Policy](PRIVACY.md#privacy-policy) -- [私隱政策中文版](PRIVACY-zh.md#私隱政策) +- [Privacy Policy (English version)](PRIVACY.md#privacy-policy-english-version) +- [私隱政策(中文版)](PRIVACY-zh.md#私隱政策中文版) ## License -**Copyright 2021–2024 Conway**
+**Copyright 2021–2025 Conway**
Licensed under the GNU General Public License v3.0 (GPL-3.0-only).
This is free software with NO WARRANTY etc. etc., see [LICENSE].
@@ -73,14 +73,11 @@ see [app/src/main/assets/about.html]. ### Miscellaneous -| Action | Function | +| Function | Action | | - | - | -| Horizontally swipe space-bar | Change language | -| Long press space-bar | Change to other keyboard | - -### Retract keyboard - -Use the system Back Button (or Back Gesture) to retract the keyboard. +| Retract keyboard | Use system Back Button (or Back Gesture) | +| Change language | Horizontally swipe space-bar | +| Change to other keyboard | Long press space-bar | ## Assets (for devs) diff --git a/app/build.gradle b/app/build.gradle index db93691a..9903324f 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -11,10 +11,14 @@ android minSdkVersion 24 compileSdk 35 targetSdkVersion 35 - versionCode 67 - versionName "1.4.1" + versionCode 70 + versionName "1.4.4" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } + buildFeatures + { + buildConfig true + } buildTypes { release diff --git a/app/src/main/assets/about-zh-Hans-CN.html b/app/src/main/assets/about-zh-Hans-CN.html index 41b03cbd..26f3e7f3 100644 --- a/app/src/main/assets/about-zh-Hans-CN.html +++ b/app/src/main/assets/about-zh-Hans-CN.html @@ -7,12 +7,12 @@ 关于 -

笔画输入法 (v1.4.1)

+

笔画输入法 (v1.4.4)

笔画输入法是自由开源软件,而恕无担保。

-© 2021–2024 Conway
+© 2021–2025 Conway
GPL-3.0-only 授权,见<https://www.gnu.org/licenses/>。

diff --git a/app/src/main/assets/about-zh-Hant-HK.html b/app/src/main/assets/about-zh-Hant-HK.html index 858945c5..b2c89766 100644 --- a/app/src/main/assets/about-zh-Hant-HK.html +++ b/app/src/main/assets/about-zh-Hant-HK.html @@ -7,12 +7,12 @@ 關於 -

筆畫輸入法 (v1.4.1)

+

筆畫輸入法 (v1.4.4)

筆畫輸入法為自由開源軟件,而恕無擔保。

-© 2021–2024 Conway
+© 2021–2025 Conway
GPL-3.0-only 授權,見<https://www.gnu.org/licenses/>。

diff --git a/app/src/main/assets/about-zh-Hant-TW.html b/app/src/main/assets/about-zh-Hant-TW.html index 7a4e4b8b..644f5ab5 100644 --- a/app/src/main/assets/about-zh-Hant-TW.html +++ b/app/src/main/assets/about-zh-Hant-TW.html @@ -7,12 +7,12 @@ 關於 -

筆畫輸入法 (v1.4.1)

+

筆畫輸入法 (v1.4.4)

筆畫輸入法為自由開源軟體,而恕無擔保。

-© 2021–2024 Conway
+© 2021–2025 Conway
GPL-3.0-only 授權,見<https://www.gnu.org/licenses/>。

diff --git a/app/src/main/assets/about.html b/app/src/main/assets/about.html index fe490be6..e8c2a631 100644 --- a/app/src/main/assets/about.html +++ b/app/src/main/assets/about.html @@ -7,13 +7,13 @@ About -

Stroke Input Method (v1.4.1)

+

Stroke Input Method (v1.4.4)

Stroke Input Method is free and open-source software with ABSOLUTELY NO WARRANTY.

-© 2021–2024 Conway
+© 2021–2025 Conway
Licensed under GPL-3.0-only, see <https://www.gnu.org/licenses/>.

diff --git a/app/src/main/assets/help-zh-Hans-CN.cmd b/app/src/main/assets/help-zh-Hans-CN.cmd index e5f4bc51..cb116d4a 100644 --- a/app/src/main/assets/help-zh-Hans-CN.cmd +++ b/app/src/main/assets/help-zh-Hans-CN.cmd @@ -73,28 +73,21 @@ OrdinaryDictionaryReplacement: #.boilerplate-properties-override '''' |^ // - ; 动作 ; 功能 + ; 动作 |: // - , 横扫 \space-bar-zh + , 收回键盘 + , 用系统返回按钮(或返回手势) +// , 转语言 + , 横扫 \space-bar-zh // - , 长按 \space-bar-zh , 转至其他键盘 + , 长按 \space-bar-zh '''' -## 收回键盘 - ----- -用系统__返回按钮__(或__返回手势__)来收回键盘。 ----- ----- -(若 Android~13+ __手势导航__不便,可考虑复还__“三按钮”导航__,并使用__返回按钮__。) ----- - -

此页的 [CMD] 源码: [cmd-source]
diff --git a/app/src/main/assets/help-zh-Hans-CN.html b/app/src/main/assets/help-zh-Hans-CN.html index ff4474db..c87bcfbc 100644 --- a/app/src/main/assets/help-zh-Hans-CN.html +++ b/app/src/main/assets/help-zh-Hans-CN.html @@ -70,28 +70,25 @@

杂项

- + - + + + + + - +
动作 功能动作
横扫 ◀ 中文 ▶收回键盘用系统返回按钮(或返回手势)
转语言横扫 ◀ 中文 ▶
长按 ◀ 中文 ▶ 转至其他键盘长按 ◀ 中文 ▶
-

收回键盘

-

-用系统返回按钮(或返回手势)来收回键盘。 -

-

-(若 Android 13+ 手势导航不便,可考虑复还“三按钮”导航,并使用返回按钮。) -

diff --git a/app/src/main/assets/help-zh-Hant-HK.cmd b/app/src/main/assets/help-zh-Hant-HK.cmd index b67fcca8..2d7ceed3 100644 --- a/app/src/main/assets/help-zh-Hant-HK.cmd +++ b/app/src/main/assets/help-zh-Hant-HK.cmd @@ -74,28 +74,21 @@ OrdinaryDictionaryReplacement: #.boilerplate-properties-override '''' |^ // - ; 動作 ; 功能 + ; 動作 |: // - , 橫掃 \space-bar-zh + , 收回鍵盤 + , 以系統返回掣(或返回手勢) +// , 轉語言 + , 橫掃 \space-bar-zh // - , 長撳 \space-bar-zh , 轉至其他鍵盤 + , 長撳 \space-bar-zh '''' -## 收回鍵盤 - ----- -以系統__返回掣__(或__返回手勢__)而收回鍵盤。 ----- ----- -(若 Android~13+ __手勢導覽__不便,可考慮復還__三按鈕導覽__,並使用__反回掣__。) ----- - -
此頁之 [CMD] 源碼: [cmd-source]
diff --git a/app/src/main/assets/help-zh-Hant-HK.html b/app/src/main/assets/help-zh-Hant-HK.html index cc2c3e1d..c6ea0f29 100644 --- a/app/src/main/assets/help-zh-Hant-HK.html +++ b/app/src/main/assets/help-zh-Hant-HK.html @@ -70,28 +70,25 @@

雜項

- + - + + + + + - +
動作 功能動作
橫掃 ◀ 中文 ▶收回鍵盤以系統返回掣(或返回手勢)
轉語言橫掃 ◀ 中文 ▶
長撳 ◀ 中文 ▶ 轉至其他鍵盤長撳 ◀ 中文 ▶
-

收回鍵盤

-

-以系統返回掣(或返回手勢)而收回鍵盤。 -

-

-(若 Android 13+ 手勢導覽不便,可考慮復還三按鈕導覽,並使用反回掣。) -

diff --git a/app/src/main/assets/help-zh-Hant-TW.cmd b/app/src/main/assets/help-zh-Hant-TW.cmd index 8d6e373c..4fc63856 100644 --- a/app/src/main/assets/help-zh-Hant-TW.cmd +++ b/app/src/main/assets/help-zh-Hant-TW.cmd @@ -74,28 +74,21 @@ OrdinaryDictionaryReplacement: #.boilerplate-properties-override '''' |^ // - ; 動作 ; 功能 + ; 動作 |: // - , 橫掃 \space-bar-zh + , 收回鍵盤 + , 以系統返回按鈕(或返回手勢) +// , 轉語言 + , 橫掃 \space-bar-zh // - , 長按 \space-bar-zh , 轉至其他鍵盤 + , 長按 \space-bar-zh '''' -## 收回鍵盤 - ----- -以系統__返回按鈕__(或__返回手勢__)而收回鍵盤。 ----- ----- -(若 Android~13+ __手勢操作__不便,可考慮復還__三按鈕操作__,並使用__返回按鈕__。) ----- - -
此頁之 [CMD] 原始程式: [cmd-source]
diff --git a/app/src/main/assets/help-zh-Hant-TW.html b/app/src/main/assets/help-zh-Hant-TW.html index 07aa70eb..adb4df97 100644 --- a/app/src/main/assets/help-zh-Hant-TW.html +++ b/app/src/main/assets/help-zh-Hant-TW.html @@ -70,28 +70,25 @@

雜項

- + - + + + + + - +
動作 功能動作
橫掃 ◀ 中文 ▶收回鍵盤以系統返回按鈕(或返回手勢)
轉語言橫掃 ◀ 中文 ▶
長按 ◀ 中文 ▶ 轉至其他鍵盤長按 ◀ 中文 ▶
-

收回鍵盤

-

-以系統返回按鈕(或返回手勢)而收回鍵盤。 -

-

-(若 Android 13+ 手勢操作不便,可考慮復還三按鈕操作,並使用返回按鈕。) -

diff --git a/app/src/main/assets/help.cmd b/app/src/main/assets/help.cmd index 0b9c511b..a8c8b951 100644 --- a/app/src/main/assets/help.cmd +++ b/app/src/main/assets/help.cmd @@ -73,29 +73,21 @@ OrdinaryDictionaryReplacement: #.boilerplate-properties-override '''' |^ // - ; Action ; Function + ; Action |: // - , Horizontally swipe \space-bar-en + , Retract keyboard + , Use system Back Button (or Back Gesture) +// , Change language + , Horizontally swipe \space-bar-en // - , Long press \space-bar-en , Change to other keyboard + , Long press \space-bar-en '''' -## Retract keyboard - ----- -Use the system __Back Button__ (or __Back Gesture__) to retract the keyboard. ----- ----- -(If Android~13+ __Gesture Navigation__ is inconvenient, -consider reverting to __3-Button Navigation__ and using the __Back Button__.) ----- - -
This page's [CMD] source: [cmd-source]
diff --git a/app/src/main/assets/help.html b/app/src/main/assets/help.html index 2df9b98a..e757c8d2 100644 --- a/app/src/main/assets/help.html +++ b/app/src/main/assets/help.html @@ -70,29 +70,25 @@

Miscellaneous

- + - + + + + + - +
Action FunctionAction
Horizontally swipe ◀ English ▶Retract keyboardUse system Back Button (or Back Gesture)
Change languageHorizontally swipe ◀ English ▶
Long press ◀ English ▶ Change to other keyboardLong press ◀ English ▶
-

Retract keyboard

-

-Use the system Back Button (or Back Gesture) to retract the keyboard. -

-

-(If Android 13+ Gesture Navigation is inconvenient, -consider reverting to 3-Button Navigation and using the Back Button.) -

diff --git a/app/src/main/assets/links.cmdr b/app/src/main/assets/links.cmdr index b8f52916..0ed8df0f 100644 --- a/app/src/main/assets/links.cmdr +++ b/app/src/main/assets/links.cmdr @@ -15,8 +15,8 @@ OrdinaryDictionaryReplacement: #.links.license-links OrdinaryDictionaryReplacement: #.links.software-properties - queue_position: BEFORE #explicit-links - apply_mode: SIMULTANEOUS -* \stroke-input-version --> v1.4.1 -* \stroke-input-years --> 2021--2024 +* \stroke-input-version --> v1.4.4 +* \stroke-input-years --> 2021--2025 * \material-version --> v1.12.0 * \material-years --> 2024 * \stroke-data-version --> v1.34.0 diff --git a/app/src/main/java/io/github/yawnoc/strokeinput/CandidatesViewAdapter.java b/app/src/main/java/io/github/yawnoc/strokeinput/CandidatesViewAdapter.java index f6d49d65..11f3b138 100644 --- a/app/src/main/java/io/github/yawnoc/strokeinput/CandidatesViewAdapter.java +++ b/app/src/main/java/io/github/yawnoc/strokeinput/CandidatesViewAdapter.java @@ -27,12 +27,12 @@ public class CandidatesViewAdapter { private CandidateListener candidateListener; private final LayoutInflater layoutInflater; - private final List candidateList; + private final List candidates; - CandidatesViewAdapter(final Context context, final List candidateList) + CandidatesViewAdapter(final Context context, final List candidates) { this.layoutInflater = LayoutInflater.from(context); - this.candidateList = candidateList; + this.candidates = candidates; } public interface CandidateListener @@ -46,10 +46,10 @@ public void setCandidateListener(final CandidateListener candidateListener) } @SuppressLint("NotifyDataSetChanged") - public void updateCandidateList(final List candidateList) + public void updateCandidates(final List candidates) { - this.candidateList.clear(); - this.candidateList.addAll(candidateList); + this.candidates.clear(); + this.candidates.addAll(candidates); notifyDataSetChanged(); } @@ -64,14 +64,14 @@ public ButtonHolder onCreateViewHolder(@NonNull final ViewGroup viewGroup, final @Override public void onBindViewHolder(@NonNull final ButtonHolder buttonHolder, final int candidateIndex) { - final String candidate = candidateList.get(candidateIndex); + final String candidate = candidates.get(candidateIndex); buttonHolder.candidateButton.setText(candidate); } @Override public int getItemCount() { - return candidateList.size(); + return candidates.size(); } public class ButtonHolder diff --git a/app/src/main/java/io/github/yawnoc/strokeinput/InputContainer.java b/app/src/main/java/io/github/yawnoc/strokeinput/InputContainer.java index b1b698cf..0600203c 100644 --- a/app/src/main/java/io/github/yawnoc/strokeinput/InputContainer.java +++ b/app/src/main/java/io/github/yawnoc/strokeinput/InputContainer.java @@ -1,5 +1,5 @@ /* - Copyright 2021--2024 Conway + Copyright 2021--2025 Conway Licensed under the GNU General Public License v3.0 (GPL-3.0-only). This is free software with NO WARRANTY etc. etc., see LICENSE or . @@ -143,9 +143,9 @@ public void setStrokeDigitSequence(final String strokeDigitSequence) strokeSequenceBar.setStrokeDigitSequence(strokeDigitSequence); } - public void setCandidateList(final List candidateList) + public void setCandidates(final List candidates) { - candidatesViewAdapter.updateCandidateList(candidateList); + candidatesViewAdapter.updateCandidates(candidates); candidatesView.scrollToPosition(0); } diff --git a/app/src/main/java/io/github/yawnoc/strokeinput/Key.java b/app/src/main/java/io/github/yawnoc/strokeinput/Key.java index edd34cee..29b7fc0f 100644 --- a/app/src/main/java/io/github/yawnoc/strokeinput/Key.java +++ b/app/src/main/java/io/github/yawnoc/strokeinput/Key.java @@ -1,5 +1,5 @@ /* - Copyright 2021, 2023--2024 Conway + Copyright 2021, 2023--2025 Conway Licensed under the GNU General Public License v3.0 (GPL-3.0-only). This is free software with NO WARRANTY etc. etc., see LICENSE or . @@ -167,7 +167,9 @@ public boolean containsPoint(final int x, final int y) && (this.isExtendedRight || x <= this.x + this.width) && - this.y <= y && y <= this.y + this.height + this.y <= y + && + y <= this.y + this.height ); } diff --git a/app/src/main/java/io/github/yawnoc/strokeinput/KeyPreviewPlane.java b/app/src/main/java/io/github/yawnoc/strokeinput/KeyPreviewPlane.java index aa6c905d..33f373f5 100644 --- a/app/src/main/java/io/github/yawnoc/strokeinput/KeyPreviewPlane.java +++ b/app/src/main/java/io/github/yawnoc/strokeinput/KeyPreviewPlane.java @@ -39,7 +39,7 @@ public class KeyPreviewPlane private int width; private int height; private int keyboardHeight; - private final List keyList = new ArrayList<>(); + private final List keys = new ArrayList<>(); private Key latestKey; private int shiftMode = KeyboardView.SHIFT_DISABLED; @@ -77,7 +77,7 @@ private void initialiseDismissalHandler() public void handleMessage(@NonNull Message message) { Key key = (Key) message.obj; - keyList.remove(key); + keys.remove(key); invalidate(); } }; @@ -117,9 +117,9 @@ public void updateShiftMode(final int shiftMode) public void showPreviewAt(final Key key) { - if (key != null && !keyList.contains(key) && key.isPreviewable) + if (key != null && !keys.contains(key) && key.isPreviewable) { - keyList.add(key); + keys.add(key); } latestKey = key; invalidate(); @@ -127,7 +127,7 @@ public void showPreviewAt(final Key key) public void movePreviewTo(final Key key) { - keyList.remove(latestKey); + keys.remove(latestKey); showPreviewAt(key); } @@ -141,7 +141,7 @@ public void dismissLatest() public void dismissAllImmediately() { - keyList.clear(); + keys.clear(); latestKey = null; invalidate(); } @@ -149,7 +149,7 @@ public void dismissAllImmediately() @Override public void onDraw(@NonNull final Canvas canvas) { - for (final Key key : keyList) + for (final Key key : keys) { final int keyPreviewWidth = (int) (key.previewMagnification * key.width); final int keyPreviewHeight = (int) (key.previewMagnification * key.height); diff --git a/app/src/main/java/io/github/yawnoc/strokeinput/Keyboard.java b/app/src/main/java/io/github/yawnoc/strokeinput/Keyboard.java index d52ca143..beb9e42e 100644 --- a/app/src/main/java/io/github/yawnoc/strokeinput/Keyboard.java +++ b/app/src/main/java/io/github/yawnoc/strokeinput/Keyboard.java @@ -1,5 +1,5 @@ /* - Copyright 2021--2024 Conway + Copyright 2021--2025 Conway Licensed under the GNU General Public License v3.0 (GPL-3.0-only). This is free software with NO WARRANTY etc. etc., see LICENSE or . @@ -26,6 +26,7 @@ import android.content.res.XmlResourceParser; import android.graphics.Color; import android.util.DisplayMetrics; +import android.util.Log; import android.util.Xml; import java.util.ArrayList; @@ -38,6 +39,8 @@ */ public class Keyboard { + private static final String LOG_TAG = "Keyboard"; + private static final String KEYBOARD_TAG = "Keyboard"; private static final String ROW_TAG = "Row"; private static final String KEY_TAG = "Key"; @@ -69,7 +72,7 @@ public class Keyboard private final Context applicationContext; private int width; private int height, naturalHeight; - private final List keyList; + private final List keys; public int fillColour; // Key properties @@ -107,15 +110,15 @@ public Keyboard(final Context context, final int layoutResourceId) defaultKeyTextSizePx = (int) Valuey.pxFromSp(DEFAULT_KEY_TEXT_SIZE_SP, displayMetrics); defaultKeyPreviewMarginYPx = (int) Valuey.pxFromDp(DEFAULT_KEY_PREVIEW_MARGIN_Y_DP, displayMetrics); - keyList = new ArrayList<>(); + keys = new ArrayList<>(); makeKeyboard(context, resources.getXml(layoutResourceId)); adjustKeyboardHeight(); } - public List getKeyList() + public List getKeys() { - return keyList; + return keys; } public int getWidth() @@ -176,7 +179,7 @@ private void makeKeyboard(final Context context, final XmlResourceParser xmlReso case KEY_TAG: inKey = true; key = new Key(row, x, y, resources, xmlResourceParser); - keyList.add(key); + keys.add(key); break; } break; @@ -203,14 +206,14 @@ else if (inRow) } catch (Exception exception) { - exception.printStackTrace(); + Log.e(LOG_TAG, "makeKeyboard failed", exception); } } public void correctKeyboardWidth(int inputContainerWidth) { final float correctionFactor = ((float) inputContainerWidth) / screenWidth; - for (final Key key : keyList) + for (final Key key : keys) { key.x = (int) (key.naturalX * correctionFactor); key.width = (int) (key.naturalWidth * correctionFactor); @@ -224,7 +227,7 @@ public void adjustKeyboardHeight() final float userAdjustmentFactor = MainActivity.keyboardHeightAdjustmentProgressToFactor(userAdjustmentProgress); final float actualAdjustmentFactor = Math.min(userAdjustmentFactor, KEYBOARD_HEIGHT_MAX_FRACTION * screenHeight / naturalHeight); - for (final Key key : keyList) + for (final Key key : keys) { key.y = (int) (key.naturalY * actualAdjustmentFactor); key.height = (int) (key.naturalHeight * actualAdjustmentFactor); diff --git a/app/src/main/java/io/github/yawnoc/strokeinput/KeyboardView.java b/app/src/main/java/io/github/yawnoc/strokeinput/KeyboardView.java index 87c50fb6..12d3ccb0 100644 --- a/app/src/main/java/io/github/yawnoc/strokeinput/KeyboardView.java +++ b/app/src/main/java/io/github/yawnoc/strokeinput/KeyboardView.java @@ -72,7 +72,7 @@ public class KeyboardView private LinearLayout mainInputPlane; private KeyPreviewPlane keyPreviewPlane; private Keyboard keyboard; - private List keyList; + private List keys; // Active key private Key activeKey; @@ -196,7 +196,7 @@ public void setKeyboard(final Keyboard keyboard) { keyboardListener.saveKeyboard(keyboard); this.keyboard = keyboard; - keyList = keyboard.getKeyList(); + keys = keyboard.getKeys(); keyboardFillPaint.setColor(keyboard.fillColour); if (shiftMode != SHIFT_PERSISTENT) { @@ -256,7 +256,7 @@ public void onDraw(@NonNull final Canvas canvas) canvas.drawRect(keyboardRectangle, keyboardFillPaint); - for (final Key key : keyList) + for (final Key key : keys) { keyRectangle.set(0, 0, key.width, key.height); @@ -623,7 +623,7 @@ private void sendShiftUpEvent(boolean shouldRedrawKeyboard) private Key getKeyAtPoint(final int x, final int y) { - for (final Key key : keyList) + for (final Key key : keys) { if (key.containsPoint(x, y)) { diff --git a/app/src/main/java/io/github/yawnoc/strokeinput/MainActivity.java b/app/src/main/java/io/github/yawnoc/strokeinput/MainActivity.java index 665d69ff..540a01c0 100644 --- a/app/src/main/java/io/github/yawnoc/strokeinput/MainActivity.java +++ b/app/src/main/java/io/github/yawnoc/strokeinput/MainActivity.java @@ -1,5 +1,5 @@ /* - Copyright 2021--2024 Conway + Copyright 2021--2025 Conway Licensed under the GNU General Public License v3.0 (GPL-3.0-only). This is free software with NO WARRANTY etc. etc., see LICENSE or . @@ -50,7 +50,7 @@ public class MainActivity private static final String ASSETS_DIRECTORY = "file:///android_asset/"; private static final String SOURCE_CODE_URI = "https://github.com/stroke-input/stroke-input-android"; private static final String PRIVACY_POLICY_URI = - "https://github.com/stroke-input/stroke-input-android/blob/master/PRIVACY.md#privacy-policy"; + "https://github.com/stroke-input/stroke-input-android/blob/master/PRIVACY.md#privacy-policy-english-version"; AlertDialog.Builder candidateOrderDialogBuilder; Dialog candidateOrderDialog; @@ -93,13 +93,13 @@ public void onStartTrackingTouch(SeekBar seekBar) @Override public void onStopTrackingTouch(SeekBar seekBar) { - // Hide and reshow keyboard to trigger height readjustment + // Trigger height readjustment and ensure keyboard shown final View focusView = getCurrentFocus(); if (focusView != null) { final InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); - inputMethodManager.hideSoftInputFromWindow(focusView.getWindowToken(), 0); + inputMethodManager.restartInput(focusView); inputMethodManager.showSoftInput(findViewById(R.id.test_input), InputMethodManager.SHOW_IMPLICIT); } } diff --git a/app/src/main/java/io/github/yawnoc/strokeinput/StrokeInputService.java b/app/src/main/java/io/github/yawnoc/strokeinput/StrokeInputService.java index b9df2eb1..44ee824b 100644 --- a/app/src/main/java/io/github/yawnoc/strokeinput/StrokeInputService.java +++ b/app/src/main/java/io/github/yawnoc/strokeinput/StrokeInputService.java @@ -1,5 +1,5 @@ /* - Copyright 2021--2024 Conway + Copyright 2021--2025 Conway Licensed under the GNU General Public License v3.0 (GPL-3.0-only). This is free software with NO WARRANTY etc. etc., see LICENSE or . @@ -127,28 +127,28 @@ public class StrokeInputService private Map nameFromKeyboard; private Map keyboardFromName; - private Set keyboardSet; + private Set keyboards; private InputContainer inputContainer; private final NavigableMap charactersFromStrokeDigitSequence = new TreeMap<>(); - private final Set codePointSetTraditional = new HashSet<>(); - private final Set codePointSetSimplified = new HashSet<>(); + private final Set codePointsTraditional = new HashSet<>(); + private final Set codePointsSimplified = new HashSet<>(); private final Map sortingRankFromCodePointTraditional = new HashMap<>(); private final Map sortingRankFromCodePointSimplified = new HashMap<>(); - private final Set commonCodePointSetTraditional = new HashSet<>(); - private final Set commonCodePointSetSimplified = new HashSet<>(); - private final NavigableSet phraseSetTraditional = new TreeSet<>(); - private final NavigableSet phraseSetSimplified = new TreeSet<>(); + private final Set commonCodePointsTraditional = new HashSet<>(); + private final Set commonCodePointsSimplified = new HashSet<>(); + private final NavigableSet phrasesTraditional = new TreeSet<>(); + private final NavigableSet phrasesSimplified = new TreeSet<>(); - private Set unpreferredCodePointSet; + private Set unpreferredCodePoints; private Map sortingRankFromCodePoint; - private Set commonCodePointSet; - private NavigableSet phraseSet; + private Set commonCodePoints; + private NavigableSet phrases; private String strokeDigitSequence = ""; - private List candidateList = new ArrayList<>(); - private final List phraseCompletionFirstCodePointList = new ArrayList<>(); + private List candidates = new ArrayList<>(); + private final List phraseCompletionFirstCodePoints = new ArrayList<>(); private int inputActionsBits; private boolean enterKeyHasAction; @@ -159,13 +159,13 @@ public void onCreate() { super.onCreate(); - loadSequenceCharactersDataIntoMap(SEQUENCE_CHARACTERS_FILE_NAME, charactersFromStrokeDigitSequence); - loadCharactersIntoCodePointSet(CHARACTERS_FILE_NAME_TRADITIONAL, codePointSetTraditional); - loadCharactersIntoCodePointSet(CHARACTERS_FILE_NAME_SIMPLIFIED, codePointSetSimplified); - loadRankingData(RANKING_FILE_NAME_TRADITIONAL, sortingRankFromCodePointTraditional, commonCodePointSetTraditional); - loadRankingData(RANKING_FILE_NAME_SIMPLIFIED, sortingRankFromCodePointSimplified, commonCodePointSetSimplified); - loadPhrasesIntoSet(PHRASES_FILE_NAME_TRADITIONAL, phraseSetTraditional); - loadPhrasesIntoSet(PHRASES_FILE_NAME_SIMPLIFIED, phraseSetSimplified); + loadSequenceCharactersData(SEQUENCE_CHARACTERS_FILE_NAME, charactersFromStrokeDigitSequence); + loadCharactersData(CHARACTERS_FILE_NAME_TRADITIONAL, codePointsTraditional); + loadCharactersData(CHARACTERS_FILE_NAME_SIMPLIFIED, codePointsSimplified); + loadRankingData(RANKING_FILE_NAME_TRADITIONAL, sortingRankFromCodePointTraditional, commonCodePointsTraditional); + loadRankingData(RANKING_FILE_NAME_SIMPLIFIED, sortingRankFromCodePointSimplified, commonCodePointsSimplified); + loadPhrasesData(PHRASES_FILE_NAME_TRADITIONAL, phrasesTraditional); + loadPhrasesData(PHRASES_FILE_NAME_SIMPLIFIED, phrasesSimplified); updateCandidateOrderPreference(); } @@ -189,7 +189,7 @@ public View onCreateInputView() nameFromKeyboard.put(qwertyKeyboard, QWERTY_KEYBOARD_NAME); nameFromKeyboard.put(qwertySymbolsKeyboard, QWERTY_SYMBOLS_KEYBOARD_NAME); keyboardFromName = Mappy.invertMap(nameFromKeyboard); - keyboardSet = nameFromKeyboard.keySet(); + keyboards = nameFromKeyboard.keySet(); inputContainer = (InputContainer) getLayoutInflater().inflate(R.layout.input_container, null); inputContainer.initialisePopupRecess(); @@ -219,11 +219,11 @@ private Keyboard loadSavedKeyboard() @SuppressWarnings("BooleanMethodIsAlwaysInverted") private static boolean isCommentLine(final String line) { - return line.startsWith("#") || line.length() == 0; + return line.startsWith("#") || line.isEmpty(); } @SuppressWarnings("SameParameterValue") - private void loadSequenceCharactersDataIntoMap( + private void loadSequenceCharactersData( final String sequenceCharactersFileName, final Map charactersFromStrokeDigitSequence ) @@ -249,14 +249,14 @@ private void loadSequenceCharactersDataIntoMap( } catch (IOException exception) { - exception.printStackTrace(); + Log.e(LOG_TAG, "loadSequenceCharactersData failed", exception); } final long endMilliseconds = System.currentTimeMillis(); sendLoadingTimeLog(sequenceCharactersFileName, startMilliseconds, endMilliseconds); } - private void loadCharactersIntoCodePointSet(final String charactersFileName, final Set codePointSet) + private void loadCharactersData(final String charactersFileName, final Set codePoints) { final long startMilliseconds = System.currentTimeMillis(); @@ -270,13 +270,13 @@ private void loadCharactersIntoCodePointSet(final String charactersFileName, fin { if (!isCommentLine(line)) { - codePointSet.add(Stringy.getFirstCodePoint(line)); + codePoints.add(Stringy.getFirstCodePoint(line)); } } } catch (IOException exception) { - exception.printStackTrace(); + Log.e(LOG_TAG, "loadCharactersData failed", exception); } final long endMilliseconds = System.currentTimeMillis(); @@ -286,7 +286,7 @@ private void loadCharactersIntoCodePointSet(final String charactersFileName, fin private void loadRankingData( final String rankingFileName, final Map sortingRankFromCodePoint, - final Set commonCodePointSet + final Set commonCodePoints ) { final long startMilliseconds = System.currentTimeMillis(); @@ -311,7 +311,7 @@ private void loadRankingData( } if (currentRank < LAG_PREVENTION_CODE_POINT_COUNT) { - commonCodePointSet.add(codePoint); + commonCodePoints.add(codePoint); } } } @@ -319,14 +319,14 @@ private void loadRankingData( } catch (IOException exception) { - exception.printStackTrace(); + Log.e(LOG_TAG, "loadRankingData failed", exception); } final long endMilliseconds = System.currentTimeMillis(); sendLoadingTimeLog(rankingFileName, startMilliseconds, endMilliseconds); } - private void loadPhrasesIntoSet(final String phrasesFileName, final Set phraseSet) + private void loadPhrasesData(final String phrasesFileName, final Set phrases) { final long startMilliseconds = System.currentTimeMillis(); @@ -340,13 +340,13 @@ private void loadPhrasesIntoSet(final String phrasesFileName, final Set { if (!isCommentLine(line)) { - phraseSet.add(line); + phrases.add(line); } } } catch (IOException exception) { - exception.printStackTrace(); + Log.e(LOG_TAG, "loadPhrasesData failed", exception); } final long endMilliseconds = System.currentTimeMillis(); @@ -409,7 +409,7 @@ public void onStartInputView(final EditorInfo editorInfo, final boolean isRestar () -> { final int inputContainerWidth = inputContainer.getWidth(); - for (final Keyboard keyboard : keyboardSet) + for (final Keyboard keyboard : keyboards) { keyboard.correctKeyboardWidth(inputContainerWidth); // needed in API level 35+ due to edge-to-edge breakage } @@ -417,7 +417,7 @@ public void onStartInputView(final EditorInfo editorInfo, final boolean isRestar } ); - for (final Keyboard keyboard : keyboardSet) + for (final Keyboard keyboard : keyboards) { keyboard.adjustKeyboardHeight(); } @@ -429,7 +429,7 @@ public void onStartInputView(final EditorInfo editorInfo, final boolean isRestar inputContainer.setBackground(isFullscreen); inputContainer.setStrokeDigitSequence(strokeDigitSequence); - inputContainer.setCandidateList(candidateList); + inputContainer.setCandidates(candidates); setEnterKeyDisplayText(); } @@ -468,9 +468,9 @@ private void setEnterKeyDisplayText() enterKeyDisplayText = getString(R.string.display_text__return); } - for (final Keyboard keyboard : keyboardSet) + for (final Keyboard keyboard : keyboards) { - for (final Key key : keyboard.getKeyList()) + for (final Key key : keyboard.getKeys()) { if (key.valueText.equals(ENTER_KEY_VALUE_TEXT)) { @@ -493,6 +493,13 @@ public void onComputeInsets(final Insets insets) } } + @Override + public boolean onEvaluateInputViewShown() + { + super.onEvaluateInputViewShown(); + return true; // override needed in API level 36 + } + @Override public void onCandidate(final String candidate) { @@ -504,7 +511,7 @@ public void onCandidate(final String candidate) inputConnection.commitText(candidate, 1); setStrokeDigitSequence(""); - setPhraseCompletionCandidateList(inputConnection); + setPhraseCompletionCandidates(inputConnection); } @Override @@ -557,27 +564,27 @@ public void onKey(final String valueText) private void effectStrokeAppend(final String strokeDigit) { final String newStrokeDigitSequence = strokeDigitSequence + strokeDigit; - final List newCandidateList = computeCandidateList(newStrokeDigitSequence); - if (newCandidateList.size() > 0) + final List newCandidates = computeCandidates(newStrokeDigitSequence); + if (!newCandidates.isEmpty()) { setStrokeDigitSequence(newStrokeDigitSequence); - setCandidateList(newCandidateList); + setCandidates(newCandidates); } } private void effectBackspace(final InputConnection inputConnection) { - if (strokeDigitSequence.length() > 0) + if (!strokeDigitSequence.isEmpty()) { final String newStrokeDigitSequence = Stringy.removeSuffixRegex("(?s).", strokeDigitSequence); - final List newCandidateList = computeCandidateList(newStrokeDigitSequence); + final List newCandidates = computeCandidates(newStrokeDigitSequence); setStrokeDigitSequence(newStrokeDigitSequence); - setCandidateList(newCandidateList); + setCandidates(newCandidates); - if (newStrokeDigitSequence.length() == 0) + if (newStrokeDigitSequence.isEmpty()) { - setPhraseCompletionCandidateList(inputConnection); + setPhraseCompletionCandidates(inputConnection); } inputContainer.setKeyRepeatIntervalMilliseconds(BACKSPACE_REPEAT_INTERVAL_MILLISECONDS_UTF_8); @@ -586,7 +593,7 @@ private void effectBackspace(final InputConnection inputConnection) { final String upToOneCharacterBeforeCursor = getTextBeforeCursor(inputConnection, 1); - if (upToOneCharacterBeforeCursor.length() > 0) + if (!upToOneCharacterBeforeCursor.isEmpty()) { final CharSequence selection = inputConnection.getSelectedText(0); if (TextUtils.isEmpty(selection)) @@ -604,7 +611,7 @@ private void effectBackspace(final InputConnection inputConnection) inputConnection.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DEL)); } - setPhraseCompletionCandidateList(inputConnection); + setPhraseCompletionCandidates(inputConnection); final int nextBackspaceIntervalMilliseconds = (Stringy.isAscii(upToOneCharacterBeforeCursor)) @@ -622,7 +629,7 @@ private void effectKeyboardSwitch(final String keyboardName) private void effectSpaceKey(final InputConnection inputConnection) { - if (strokeDigitSequence.length() > 0) + if (!strokeDigitSequence.isEmpty()) { onCandidate(getFirstCandidate()); } @@ -634,7 +641,7 @@ private void effectSpaceKey(final InputConnection inputConnection) private void effectEnterKey(final InputConnection inputConnection) { - if (strokeDigitSequence.length() > 0) + if (!strokeDigitSequence.isEmpty()) { onCandidate(getFirstCandidate()); } @@ -650,7 +657,7 @@ else if (enterKeyHasAction) private void effectOrdinaryKey(final InputConnection inputConnection, final String valueText) { - if (strokeDigitSequence.length() > 0) + if (!strokeDigitSequence.isEmpty()) { return; } @@ -713,32 +720,32 @@ private void setStrokeDigitSequence(final String strokeDigitSequence) inputContainer.setStrokeDigitSequence(strokeDigitSequence); } - private void setCandidateList(final List candidateList) + private void setCandidates(final List candidates) { - this.candidateList = candidateList; - inputContainer.setCandidateList(candidateList); + this.candidates = candidates; + inputContainer.setCandidates(candidates); } - private void setPhraseCompletionCandidateList(final InputConnection inputConnection) + private void setPhraseCompletionCandidates(final InputConnection inputConnection) { - List phraseCompletionCandidateList = computePhraseCompletionCandidateList(inputConnection); + List phraseCompletionCandidates = computePhraseCompletionCandidates(inputConnection); - phraseCompletionFirstCodePointList.clear(); - for (final String phraseCompletionCandidate : phraseCompletionCandidateList) + phraseCompletionFirstCodePoints.clear(); + for (final String phraseCompletionCandidate : phraseCompletionCandidates) { - phraseCompletionFirstCodePointList.add(Stringy.getFirstCodePoint(phraseCompletionCandidate)); + phraseCompletionFirstCodePoints.add(Stringy.getFirstCodePoint(phraseCompletionCandidate)); } - setCandidateList(phraseCompletionCandidateList); + setCandidates(phraseCompletionCandidates); } /* Candidate comparator for a string. */ private Comparator candidateComparator( - final Set unpreferredCodePointSet, + final Set unpreferredCodePoints, final Map sortingRankFromCodePoint, - final List phraseCompletionFirstCodePointList + final List phraseCompletionFirstCodePoints ) { return @@ -746,9 +753,9 @@ private Comparator candidateComparator( string -> computeCandidateRank( string, - unpreferredCodePointSet, + unpreferredCodePoints, sortingRankFromCodePoint, - phraseCompletionFirstCodePointList + phraseCompletionFirstCodePoints ) ); } @@ -757,9 +764,9 @@ private Comparator candidateComparator( Candidate comparator for a code point. */ private Comparator candidateCodePointComparator( - final Set unpreferredCodePointSet, + final Set unpreferredCodePoints, final Map sortingRankFromCodePoint, - final List phraseCompletionFirstCodePointList + final List phraseCompletionFirstCodePoints ) { return @@ -768,9 +775,9 @@ private Comparator candidateCodePointComparator( computeCandidateRank( codePoint, 1, - unpreferredCodePointSet, + unpreferredCodePoints, sortingRankFromCodePoint, - phraseCompletionFirstCodePointList + phraseCompletionFirstCodePoints ) ); } @@ -780,9 +787,9 @@ private Comparator candidateCodePointComparator( */ private int computeCandidateRank( final String string, - final Set unpreferredCodePointSet, + final Set unpreferredCodePoints, final Map sortingRankFromCodePoint, - final List phraseCompletionFirstCodePointList + final List phraseCompletionFirstCodePoints ) { final int firstCodePoint = Stringy.getFirstCodePoint(string); @@ -792,9 +799,9 @@ private int computeCandidateRank( computeCandidateRank( firstCodePoint, stringLength, - unpreferredCodePointSet, + unpreferredCodePoints, sortingRankFromCodePoint, - phraseCompletionFirstCodePointList + phraseCompletionFirstCodePoints ); } @@ -804,17 +811,17 @@ private int computeCandidateRank( private int computeCandidateRank( final int firstCodePoint, final int stringLength, - final Set unpreferredCodePointSet, + final Set unpreferredCodePoints, final Map sortingRankFromCodePoint, - final List phraseCompletionFirstCodePointList + final List phraseCompletionFirstCodePoints ) { final int coarseRank; final int fineRank; final int penalty; - final boolean phraseCompletionListIsEmpty = phraseCompletionFirstCodePointList.size() == 0; - final int phraseCompletionIndex = phraseCompletionFirstCodePointList.indexOf(firstCodePoint); + final boolean phraseCompletionsIsEmpty = phraseCompletionFirstCodePoints.isEmpty(); + final int phraseCompletionIndex = phraseCompletionFirstCodePoints.indexOf(firstCodePoint); final boolean firstCodePointMatchesPhraseCompletionCandidate = phraseCompletionIndex > 0; final Integer sortingRank = sortingRankFromCodePoint.get(firstCodePoint); @@ -829,11 +836,11 @@ private int computeCandidateRank( final int lengthPenalty = (stringLength - 1) * RANKING_PENALTY_PER_CHAR; final int unpreferredPenalty = - (unpreferredCodePointSet.contains(firstCodePoint)) + (unpreferredCodePoints.contains(firstCodePoint)) ? RANKING_PENALTY_UNPREFERRED : 0; - if (phraseCompletionListIsEmpty) + if (phraseCompletionsIsEmpty) { coarseRank = Integer.MIN_VALUE; fineRank = sortingRankNonNull; @@ -855,30 +862,32 @@ else if (firstCodePointMatchesPhraseCompletionCandidate) return coarseRank + fineRank + penalty; } - private List computeCandidateList(final String strokeDigitSequence) + private List computeCandidates(final String strokeDigitSequence) { - if (strokeDigitSequence.length() == 0) + if (strokeDigitSequence.isEmpty()) { return Collections.emptyList(); } updateCandidateOrderPreference(); - final List exactMatchCandidateList; + final Set exactMatchCodePoints; + final List exactMatchCandidates; final String exactMatchCharacters = charactersFromStrokeDigitSequence.get(strokeDigitSequence); if (exactMatchCharacters != null) { - exactMatchCandidateList = Stringy.toCharacterList(exactMatchCharacters); - exactMatchCandidateList.sort( - candidateComparator(unpreferredCodePointSet, sortingRankFromCodePoint, phraseCompletionFirstCodePointList) + exactMatchCodePoints = Stringy.toCodePointSet(exactMatchCharacters); + exactMatchCandidates = Stringy.toCharacterList(exactMatchCharacters); + exactMatchCandidates.sort( + candidateComparator(unpreferredCodePoints, sortingRankFromCodePoint, phraseCompletionFirstCodePoints) ); } else { - exactMatchCandidateList = Collections.emptyList(); + exactMatchCodePoints = Collections.emptySet(); + exactMatchCandidates = Collections.emptyList(); } - final Set prefixMatchCodePointSet = new HashSet<>(); final Collection prefixMatchCharactersCollection = charactersFromStrokeDigitSequence .subMap( @@ -887,58 +896,38 @@ private List computeCandidateList(final String strokeDigitSequence) ) .values(); - final long addCodePointsStartMilliseconds = System.currentTimeMillis(); - for (final String characters : prefixMatchCharactersCollection) - { - Stringy.addCodePointsToSet(characters, prefixMatchCodePointSet); - } - final long addCodePointsEndMilliseconds = System.currentTimeMillis(); - if (BuildConfig.DEBUG) - { - final long durationMilliseconds = addCodePointsEndMilliseconds - addCodePointsStartMilliseconds; - Log.d(LOG_TAG, String.format("Added code points to set in %d ms", durationMilliseconds)); - } + final Set prefixMatchCodePoints = Stringy.toCodePointSet(prefixMatchCharactersCollection); - if (prefixMatchCodePointSet.size() > LAG_PREVENTION_CODE_POINT_COUNT) + prefixMatchCodePoints.removeAll(exactMatchCodePoints); + if (prefixMatchCodePoints.size() > LAG_PREVENTION_CODE_POINT_COUNT) { - prefixMatchCodePointSet.retainAll(commonCodePointSet); + prefixMatchCodePoints.retainAll(commonCodePoints); } - final List prefixMatchCandidateCodePointList = new ArrayList<>(prefixMatchCodePointSet); - final long sortPrefixMatchesStartMilliseconds = System.currentTimeMillis(); - prefixMatchCandidateCodePointList.sort( - candidateCodePointComparator( - unpreferredCodePointSet, - sortingRankFromCodePoint, - phraseCompletionFirstCodePointList - ) + final List prefixMatchCandidateCodePoints = new ArrayList<>(prefixMatchCodePoints); + prefixMatchCandidateCodePoints.sort( + candidateCodePointComparator(unpreferredCodePoints, sortingRankFromCodePoint, phraseCompletionFirstCodePoints) ); - final long sortPrefixMatchesEndMilliseconds = System.currentTimeMillis(); - if (BuildConfig.DEBUG) - { - final long durationMilliseconds = sortPrefixMatchesEndMilliseconds - sortPrefixMatchesStartMilliseconds; - Log.d(LOG_TAG, String.format("Sorted prefix matches in %d ms", durationMilliseconds)); - } - final int prefixMatchCount = Math.min(prefixMatchCandidateCodePointList.size(), MAX_PREFIX_MATCH_COUNT); - final List prefixMatchCandidateList = new ArrayList<>(); - for (final int prefixMatchCodePoint : prefixMatchCandidateCodePointList.subList(0, prefixMatchCount)) + final int prefixMatchCount = Math.min(prefixMatchCandidateCodePoints.size(), MAX_PREFIX_MATCH_COUNT); + final List prefixMatchCandidates = new ArrayList<>(); + for (final int prefixMatchCodePoint : prefixMatchCandidateCodePoints.subList(0, prefixMatchCount)) { - prefixMatchCandidateList.add(Stringy.toString(prefixMatchCodePoint)); + prefixMatchCandidates.add(Stringy.toString(prefixMatchCodePoint)); } - final List candidateList = new ArrayList<>(); - candidateList.addAll(exactMatchCandidateList); - candidateList.addAll(prefixMatchCandidateList); + final List candidates = new ArrayList<>(); + candidates.addAll(exactMatchCandidates); + candidates.addAll(prefixMatchCandidates); - return candidateList; + return candidates; } private String getFirstCandidate() { try { - return candidateList.get(0); + return candidates.get(0); } catch (IndexOutOfBoundsException exception) { @@ -950,40 +939,40 @@ private String getFirstCandidate() Compute the phrase completion candidate list. Longer matches with the text before the cursor are ranked earlier. */ - private List computePhraseCompletionCandidateList(final InputConnection inputConnection) + private List computePhraseCompletionCandidates(final InputConnection inputConnection) { updateCandidateOrderPreference(); - final List phraseCompletionCandidateList = new ArrayList<>(); + final List phraseCompletionCandidates = new ArrayList<>(); for ( String phrasePrefix = getTextBeforeCursor(inputConnection, MAX_PHRASE_LENGTH - 1); - phrasePrefix.length() > 0; + !phrasePrefix.isEmpty(); phrasePrefix = Stringy.removePrefixRegex("(?s).", phrasePrefix) ) { - final Set prefixMatchPhraseCandidateSet = - phraseSet.subSet( + final Set prefixMatchPhraseCandidates = + phrases.subSet( phrasePrefix, false, phrasePrefix + Character.MAX_VALUE, false ); - final List prefixMatchPhraseCompletionList = new ArrayList<>(); + final List prefixMatchPhraseCompletions = new ArrayList<>(); - for (final String phraseCandidate : prefixMatchPhraseCandidateSet) + for (final String phraseCandidate : prefixMatchPhraseCandidates) { final String phraseCompletion = Stringy.removePrefix(phrasePrefix, phraseCandidate); - if (!phraseCompletionCandidateList.contains(phraseCompletion)) + if (!phraseCompletionCandidates.contains(phraseCompletion)) { - prefixMatchPhraseCompletionList.add(phraseCompletion); + prefixMatchPhraseCompletions.add(phraseCompletion); } } - prefixMatchPhraseCompletionList.sort( - candidateComparator(unpreferredCodePointSet, sortingRankFromCodePoint, Collections.emptyList()) + prefixMatchPhraseCompletions.sort( + candidateComparator(unpreferredCodePoints, sortingRankFromCodePoint, Collections.emptyList()) ); - phraseCompletionCandidateList.addAll(prefixMatchPhraseCompletionList); + phraseCompletionCandidates.addAll(prefixMatchPhraseCompletions); } - return phraseCompletionCandidateList; + return phraseCompletionCandidates; } private String getTextBeforeCursor(final InputConnection inputConnection, final int characterCount) @@ -1009,17 +998,17 @@ private void updateCandidateOrderPreference() { if (shouldPreferTraditional()) { - unpreferredCodePointSet = codePointSetSimplified; + unpreferredCodePoints = codePointsSimplified; sortingRankFromCodePoint = sortingRankFromCodePointTraditional; - commonCodePointSet = commonCodePointSetTraditional; - phraseSet = phraseSetTraditional; + commonCodePoints = commonCodePointsTraditional; + phrases = phrasesTraditional; } else { - unpreferredCodePointSet = codePointSetTraditional; + unpreferredCodePoints = codePointsTraditional; sortingRankFromCodePoint = sortingRankFromCodePointSimplified; - commonCodePointSet = commonCodePointSetSimplified; - phraseSet = phraseSetSimplified; + commonCodePoints = commonCodePointsSimplified; + phrases = phrasesSimplified; } } diff --git a/app/src/main/java/io/github/yawnoc/strokeinput/StrokeSequenceBar.java b/app/src/main/java/io/github/yawnoc/strokeinput/StrokeSequenceBar.java index d0d09cb3..65fedeaa 100644 --- a/app/src/main/java/io/github/yawnoc/strokeinput/StrokeSequenceBar.java +++ b/app/src/main/java/io/github/yawnoc/strokeinput/StrokeSequenceBar.java @@ -1,5 +1,5 @@ /* - Copyright 2021, 2023 Conway + Copyright 2021, 2023, 2025 Conway Licensed under the GNU General Public License v3.0 (GPL-3.0-only). This is free software with NO WARRANTY etc. etc., see LICENSE or . @@ -25,7 +25,7 @@ public StrokeSequenceBar(Context context, AttributeSet attributes) public void setStrokeDigitSequence(final String strokeDigitSequence) { - if (strokeDigitSequence.length() > 0) + if (!strokeDigitSequence.isEmpty()) { final String strokeSequence = strokeDigitSequence diff --git a/app/src/main/java/io/github/yawnoc/utilities/Stringy.java b/app/src/main/java/io/github/yawnoc/utilities/Stringy.java index 8e5659b0..164449b6 100644 --- a/app/src/main/java/io/github/yawnoc/utilities/Stringy.java +++ b/app/src/main/java/io/github/yawnoc/utilities/Stringy.java @@ -8,6 +8,8 @@ package io.github.yawnoc.utilities; import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.regex.Pattern; @@ -52,31 +54,50 @@ Convert a string to a list of (unicode) code points. */ public static List toCodePointList(final String string) { - final List codePointList = new ArrayList<>(); + final List codePoints = new ArrayList<>(); final int charCount = string.length(); for (int charIndex = 0; charIndex < charCount;) { final int codePoint = string.codePointAt(charIndex); - codePointList.add(codePoint); + codePoints.add(codePoint); charIndex += Character.charCount(codePoint); } - return codePointList; + return codePoints; } /* - Add the (unicode) code points of a string to a set + Convert a string to a set of (unicode) code points. */ - public static void addCodePointsToSet(final String string, final Set set) + public static Set toCodePointSet(final String string) { + final Set codePoints = new HashSet<>(); + final int charCount = string.length(); for (int charIndex = 0; charIndex < charCount;) { final int codePoint = string.codePointAt(charIndex); - set.add(codePoint); + codePoints.add(codePoint); charIndex += Character.charCount(codePoint); } + + return codePoints; + } + + /* + Convert a collection of strings to a set of (unicode) code points. + */ + public static Set toCodePointSet(final Collection stringCollection) + { + final Set codePoints = new HashSet<>(); + + for (final String string : stringCollection) + { + codePoints.addAll(toCodePointSet(string)); + } + + return codePoints; } /* @@ -92,20 +113,18 @@ Convert a string to a list of (unicode) characters. */ public static List toCharacterList(final String string) { - final List characterList = new ArrayList<>(); + final List characters = new ArrayList<>(); final int codePointCount = string.codePointCount(0, string.length()); for (int codePointIndex = 0; codePointIndex < codePointCount; codePointIndex++) { - characterList.add( - string.substring( - string.offsetByCodePoints(0, codePointIndex), - string.offsetByCodePoints(0, codePointIndex + 1) - ) - ); + final int startIndex = string.offsetByCodePoints(0, codePointIndex); + final int endIndex = string.offsetByCodePoints(0, codePointIndex + 1); + final String character = string.substring(startIndex, endIndex); + characters.add(character); } - return characterList; + return characters; } /* diff --git a/app/src/test/java/io/github/yawnoc/utilities/MappyTest.java b/app/src/test/java/io/github/yawnoc/utilities/MappyTest.java index 5282fba9..1d35f099 100644 --- a/app/src/test/java/io/github/yawnoc/utilities/MappyTest.java +++ b/app/src/test/java/io/github/yawnoc/utilities/MappyTest.java @@ -7,7 +7,7 @@ package io.github.yawnoc.utilities; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; import org.junit.Test; @@ -23,12 +23,12 @@ public void invertMap_isCorrect() forwardMap.put(1, "one"); forwardMap.put(2, "two"); forwardMap.put(3, "three"); - + final Map inverseMap = new HashMap<>(); inverseMap.put("one", 1); inverseMap.put("two", 2); inverseMap.put("three", 3); - + assertEquals(Mappy.invertMap(forwardMap), inverseMap); assertEquals(Mappy.invertMap(inverseMap), forwardMap); } diff --git a/app/src/test/java/io/github/yawnoc/utilities/StringyTest.java b/app/src/test/java/io/github/yawnoc/utilities/StringyTest.java index 0a290547..0f51c06d 100644 --- a/app/src/test/java/io/github/yawnoc/utilities/StringyTest.java +++ b/app/src/test/java/io/github/yawnoc/utilities/StringyTest.java @@ -7,15 +7,16 @@ package io.github.yawnoc.utilities; -import static org.junit.Assert.*; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import org.junit.Test; import java.util.Arrays; -import java.util.Collections; import java.util.HashSet; import java.util.List; -import java.util.Set; import java.util.stream.Collectors; import java.util.stream.IntStream; @@ -39,13 +40,13 @@ public void isAscii_isCorrect() assertTrue(Stringy.isAscii("abc 123 #@% +-*/ ,:;.?!")); assertTrue(Stringy.isAscii("\"\\")); assertTrue(Stringy.isAscii(ASCII_FULL_STRING)); - + assertFalse(Stringy.isAscii(Stringy.toString(ASCII_CODE_POINT_END + 1))); assertFalse(Stringy.isAscii(ASCII_FULL_STRING + "文")); assertFalse(Stringy.isAscii("一")); assertFalse(Stringy.isAscii("U+FF0C FULLWIDTH COMMA ,")); } - + @Test public void removePrefixRegex_isCorrect() { @@ -54,7 +55,7 @@ public void removePrefixRegex_isCorrect() assertEquals(Stringy.removePrefixRegex("[a-z]+", "abc xyz"), " xyz"); assertEquals(Stringy.removePrefixRegex("[a-z]+", "123 456"), "123 456"); } - + @Test public void removeSuffixRegex_isCorrect() { @@ -62,7 +63,7 @@ public void removeSuffixRegex_isCorrect() assertEquals(Stringy.removeSuffixRegex("[a-z]+", "abc xyz"), "abc "); assertEquals(Stringy.removeSuffixRegex("[a-z]+", "123 456"), "123 456"); } - + @Test public void removePrefix_isCorrect() { @@ -71,49 +72,48 @@ public void removePrefix_isCorrect() assertEquals(Stringy.removePrefix("2", "234"), "34"); assertEquals(Stringy.removePrefix("WELL_", "WELL_SCREW_YOU"), "SCREW_YOU"); } - + @Test public void getFirstCodePoint_isCorrect() { assertEquals(Stringy.getFirstCodePoint("ABC"), 0x0041); assertEquals(Stringy.getFirstCodePoint("天下為公"), 0x5929); } - + @Test public void toCodePointList_isCorrect() { assertEquals(Stringy.toCodePointList(ASCII_FULL_STRING), ASCII_CODE_POINT_RANGE); assertEquals(Stringy.toCodePointList("天下為公"), Arrays.asList(0x5929, 0x4E0B, 0x70BA, 0x516C)); } - + @Test - public void addCodePointsToSet_isCorrect() + public void toCodePointSet_isCorrect() { - final int SOME_CODE_POINT = 0x1234; - final Set commonsCodePointSet = new HashSet<>(Collections.singletonList(SOME_CODE_POINT)); - Stringy.addCodePointsToSet("天下為公", commonsCodePointSet); - assertEquals(commonsCodePointSet, new HashSet<>(Arrays.asList(SOME_CODE_POINT, 0x5929, 0x4E0B, 0x70BA, 0x516C))); - - final Set asciiFullCodePointSet = new HashSet<>(); - Stringy.addCodePointsToSet(ASCII_FULL_STRING, asciiFullCodePointSet); - assertEquals(asciiFullCodePointSet, new HashSet<>(ASCII_CODE_POINT_RANGE)); + assertEquals(Stringy.toCodePointSet(ASCII_FULL_STRING), new HashSet<>(ASCII_CODE_POINT_RANGE)); + assertEquals(Stringy.toCodePointSet("天下為公"), new HashSet<>(Arrays.asList(0x5929, 0x4E0B, 0x70BA, 0x516C))); + + assertEquals( + Stringy.toCodePointSet(Arrays.asList("ABC", "天地玄黃", "BCD")), + new HashSet<>(Arrays.asList(0x41, 0x42, 0x43, 0x5929, 0x5730, 0x7384, 0x9EC3, 0x44)) + ); } - + @Test public void toString_isCorrect() { assertEquals(Stringy.toString(0x0000), "\0"); - + assertEquals(Stringy.toString(0x0030), "0"); assertEquals("!", Stringy.toString(0x0021)); assertEquals("A", Stringy.toString(0x0041)); - + assertEquals(Stringy.toString(0x3007), "〇"); assertEquals(Stringy.toString(0x3400), "㐀"); assertEquals(Stringy.toString(0x4DB5), "䶵"); assertEquals(Stringy.toString(0x4E00), "一"); assertEquals(Stringy.toString(0x9FD0), "鿐"); - + assertEquals(Stringy.toString(0x2000B), "\uD840\uDC0B"); // 𠀋 assertEquals(Stringy.toString(0x2A6B2), "\uD869\uDEB2"); // 𪚲 assertEquals(Stringy.toString(0x2A7DD), "\uD869\uDFDD"); // 𪟝 @@ -123,21 +123,21 @@ public void toString_isCorrect() assertEquals(Stringy.toString(0x2B8B8), "\uD86E\uDCB8"); // 𫢸 assertEquals(Stringy.toString(0x2CE93), "\uD873\uDE93"); // 𬺓 } - + @Test public void toCharacterList_isCorrect() { assertEquals(Stringy.toCharacterList("ABC"), Arrays.asList("A", "B", "C")); assertEquals(Stringy.toCharacterList("天下為公"), Arrays.asList("天", "下", "為", "公")); } - + @Test public void sunder_isCorrect() { assertArrayEquals(Stringy.sunder("abc 123", " "), new String[]{"abc", "123"}); assertArrayEquals(Stringy.sunder("abc\t123", "\t"), new String[]{"abc", "123"}); assertArrayEquals(Stringy.sunder("abc:::123", ":::"), new String[]{"abc", "123"}); - + assertArrayEquals(Stringy.sunder("abc123", " "), new String[]{"abc123", ""}); assertArrayEquals(Stringy.sunder(" abc123", " "), new String[]{"", "abc123"}); } diff --git a/app/src/test/java/io/github/yawnoc/utilities/ValueyTest.java b/app/src/test/java/io/github/yawnoc/utilities/ValueyTest.java index 708e8dda..3621bf89 100644 --- a/app/src/test/java/io/github/yawnoc/utilities/ValueyTest.java +++ b/app/src/test/java/io/github/yawnoc/utilities/ValueyTest.java @@ -7,7 +7,7 @@ package io.github.yawnoc.utilities; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; import org.junit.Test; @@ -17,14 +17,14 @@ public class ValueyTest { private static final float FLOAT_ASSERTION_DELTA = 0f; - + @Test public void clipValueToRange_isCorrect() { final List LESS_THAN_ZERO_VALUES = Arrays.asList(-Float.MAX_VALUE, -10f, -1f, -0.5f, -Float.MIN_VALUE, -0f); final List ZERO_TO_ONE_VALUES = Arrays.asList(0f, +0f, Float.MIN_VALUE, 0.7f, 1f - Float.MIN_VALUE, 1f); final List GREATER_THAN_ONE_VALUES = Arrays.asList(1 + Float.MIN_VALUE, 2f, 10000f, Float.MAX_VALUE); - + for (final float value : LESS_THAN_ZERO_VALUES) { assertEquals(Valuey.clipValueToRange(value, 0f, 1f), 0f, FLOAT_ASSERTION_DELTA); @@ -37,7 +37,7 @@ public void clipValueToRange_isCorrect() { assertEquals(Valuey.clipValueToRange(value, 0f, 1f), 1f, FLOAT_ASSERTION_DELTA); } - + assertEquals(Valuey.clipValueToRange(3f, 4f, 6f), 4f, FLOAT_ASSERTION_DELTA); assertEquals(Valuey.clipValueToRange(5f, 4f, 6f), 5f, FLOAT_ASSERTION_DELTA); assertEquals(Valuey.clipValueToRange(7f, 4f, 6f), 6f, FLOAT_ASSERTION_DELTA); diff --git a/build.gradle b/build.gradle index d0d97f4d..b28e6679 100644 --- a/build.gradle +++ b/build.gradle @@ -7,7 +7,7 @@ buildscript } dependencies { - classpath 'com.android.tools.build:gradle:8.1.4' + classpath 'com.android.tools.build:gradle:8.10.1' } } @@ -30,5 +30,5 @@ allprojects tasks.register('clean', Delete) { - delete rootProject.buildDir + delete rootProject.layout.buildDirectory } diff --git a/fastlane/metadata/android/en-US/changelogs/67.txt b/fastlane/metadata/android/en-US/changelogs/67.txt index ad6cca44..7320162f 100644 --- a/fastlane/metadata/android/en-US/changelogs/67.txt +++ b/fastlane/metadata/android/en-US/changelogs/67.txt @@ -1,3 +1,3 @@ -* Adds phrases 喼汁, 矮瓜, 蜜棗 -* Adds phrases 縮骨, 縮骨遮 -* Adds phrases 生鏽, 不鏽鋼 +* Added phrases 喼汁, 矮瓜, 蜜棗 +* Added phrases 縮骨, 縮骨遮 +* Added phrases 生鏽, 不鏽鋼 diff --git a/fastlane/metadata/android/en-US/changelogs/68.txt b/fastlane/metadata/android/en-US/changelogs/68.txt new file mode 100644 index 00000000..360e2d20 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/68.txt @@ -0,0 +1,5 @@ +* Fixed duplicate candidates when exact match is also a prefix match (者, 煮) +* Improved Help > Miscellaneous table +* Made minor edits to Privacy Policy + * English: added parenthetical "(English version)" + * Chinese: added parenthetical "(中文版)", reduced "不會" to "不", expanded "英文" to "英文版" diff --git a/fastlane/metadata/android/en-US/changelogs/69.txt b/fastlane/metadata/android/en-US/changelogs/69.txt new file mode 100644 index 00000000..63b454b5 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/69.txt @@ -0,0 +1 @@ +* Fixed keyboard failing to spawn in Android 16 diff --git a/fastlane/metadata/android/en-US/changelogs/70.txt b/fastlane/metadata/android/en-US/changelogs/70.txt new file mode 100644 index 00000000..367a686c --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/70.txt @@ -0,0 +1 @@ +* Fixed keyboard height not immediately updating in Android 16 diff --git a/gradle.properties b/gradle.properties index 683c3de0..fed1c82a 100644 --- a/gradle.properties +++ b/gradle.properties @@ -15,6 +15,5 @@ org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 # Android operating system, and which are packaged with your app"s APK # https://developer.android.com/topic/libraries/support-library/androidx-rn android.useAndroidX=true -android.defaults.buildfeatures.buildconfig=true android.nonTransitiveRClass=false android.nonFinalResIds=false diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index f97ab1f0..e76e90e4 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=4159b938ec734a8388ce03f52aa8f3c7ed0d31f5438622545de4f83a89b79788 -distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip +distributionSha256Sum=7197a12f450794931532469d4ff21a59ea2c1cd59a3ec3f89c035c3c420a6999 +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists