From 58f5123bdb0618c77cd43c714f4d323108795263 Mon Sep 17 00:00:00 2001 From: sspanak Date: Tue, 19 Mar 2024 15:18:18 +0200 Subject: [PATCH] separated the Language class into smaller files and reorganized the languages package --- .../sspanak/tt9/db/DictionaryLoader.java | 10 +- .../io/github/sspanak/tt9/db/LegacyDb.java | 2 +- .../github/sspanak/tt9/db/SlowQueryStats.java | 4 +- .../io/github/sspanak/tt9/db/WordStore.java | 21 +- .../github/sspanak/tt9/db/WordStoreAsync.java | 2 +- .../sspanak/tt9/db/entities/WordBatch.java | 2 +- .../sspanak/tt9/db/entities/WordFile.java | 2 +- .../tt9/db/exporter/AbstractExporter.java | 2 +- .../tt9/db/exporter/DictionaryExporter.java | 4 +- .../github/sspanak/tt9/db/sqlite/ReadOps.java | 2 +- .../sspanak/tt9/db/sqlite/SQLiteOpener.java | 2 +- .../sspanak/tt9/db/sqlite/UpdateOps.java | 4 +- .../github/sspanak/tt9/ime/KeyPadHandler.java | 4 +- .../github/sspanak/tt9/ime/TraditionalT9.java | 7 +- .../tt9/ime/helpers/InputModeValidator.java | 2 +- .../sspanak/tt9/ime/helpers/TextField.java | 9 +- .../sspanak/tt9/ime/modes/InputMode.java | 5 +- .../github/sspanak/tt9/ime/modes/Mode123.java | 5 +- .../github/sspanak/tt9/ime/modes/ModeABC.java | 8 +- .../sspanak/tt9/ime/modes/ModePredictive.java | 24 +- .../tt9/ime/modes/helpers/AutoSpace.java | 2 +- .../tt9/ime/modes/helpers/AutoTextCase.java | 2 +- .../sspanak/tt9/languages/EmojiLanguage.java | 16 +- .../sspanak/tt9/languages/Language.java | 304 +++--------------- .../tt9/languages/LanguageCollection.java | 26 +- .../tt9/languages/LanguageDefinition.java | 2 +- .../sspanak/tt9/languages/LanguageKind.java | 11 + .../tt9/languages/NaturalLanguage.java | 247 ++++++++++++++ .../sspanak/tt9/languages/NullLanguage.java | 23 +- .../InvalidLanguageCharactersException.java | 4 +- .../InvalidLanguageException.java | 2 +- .../tt9/preferences/PreferencesActivity.java | 2 +- .../tt9/preferences/SettingsStore.java | 2 +- .../tt9/preferences/items/ItemClickable.java | 2 +- .../tt9/preferences/items/ItemDropDown.java | 2 +- .../screens/BaseScreenFragment.java | 2 +- .../screens/MainSettingsScreen.java | 2 +- .../screens/debug/DebugScreen.java | 2 +- .../screens/debug/ItemLogLevel.java | 2 +- .../deleteWords/PreferenceSearchWords.java | 4 +- .../deleteWords/TextChangeListener.java | 2 +- .../screens/hotkeys/ItemResetKeys.java | 2 - .../screens/hotkeys/SectionKeymap.java | 2 +- .../keypad/ItemSelectZeroKeyCharacter.java | 2 +- .../languages/ItemExportDictionary.java | 2 +- .../sspanak/tt9/ui/DictionaryLoadingBar.java | 4 +- .../sspanak/tt9/ui/PopupDialogActivity.java | 2 +- .../sspanak/tt9/ui/dialogs/AddWordDialog.java | 2 +- .../ConfirmDictionaryUpdateDialog.java | 2 +- .../sspanak/tt9/ui/dialogs/PopupDialog.java | 2 +- .../tt9/ui/main/keys/SoftBackspaceKey.java | 7 +- .../sspanak/tt9/ui/main/keys/SoftKey.java | 2 +- .../tt9/ui/main/keys/SoftNumberKey.java | 9 +- .../tt9/ui/main/keys/SoftPunctuationKey.java | 5 +- .../github/sspanak/tt9/ui/tray/StatusBar.java | 2 +- .../tt9/{languages => util}/Characters.java | 2 +- .../tt9/{ => util}/ConsumerCompat.java | 2 +- .../github/sspanak/tt9/{ => util}/Logger.java | 4 +- .../sspanak/tt9/{languages => util}/Text.java | 4 +- .../tt9/{languages => util}/TextTools.java | 2 +- .../github/sspanak/tt9/{ => util}/Timer.java | 2 +- 61 files changed, 456 insertions(+), 387 deletions(-) create mode 100644 app/src/main/java/io/github/sspanak/tt9/languages/LanguageKind.java create mode 100644 app/src/main/java/io/github/sspanak/tt9/languages/NaturalLanguage.java rename app/src/main/java/io/github/sspanak/tt9/languages/{ => exceptions}/InvalidLanguageCharactersException.java (72%) rename app/src/main/java/io/github/sspanak/tt9/languages/{ => exceptions}/InvalidLanguageException.java (70%) rename app/src/main/java/io/github/sspanak/tt9/{languages => util}/Characters.java (98%) rename app/src/main/java/io/github/sspanak/tt9/{ => util}/ConsumerCompat.java (85%) rename app/src/main/java/io/github/sspanak/tt9/{ => util}/Logger.java (92%) rename app/src/main/java/io/github/sspanak/tt9/{languages => util}/Text.java (97%) rename app/src/main/java/io/github/sspanak/tt9/{languages => util}/TextTools.java (97%) rename app/src/main/java/io/github/sspanak/tt9/{ => util}/Timer.java (95%) diff --git a/app/src/main/java/io/github/sspanak/tt9/db/DictionaryLoader.java b/app/src/main/java/io/github/sspanak/tt9/db/DictionaryLoader.java index 1a09c193..eefec04a 100644 --- a/app/src/main/java/io/github/sspanak/tt9/db/DictionaryLoader.java +++ b/app/src/main/java/io/github/sspanak/tt9/db/DictionaryLoader.java @@ -10,9 +10,9 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.Locale; -import io.github.sspanak.tt9.ConsumerCompat; -import io.github.sspanak.tt9.Logger; -import io.github.sspanak.tt9.Timer; +import io.github.sspanak.tt9.util.ConsumerCompat; +import io.github.sspanak.tt9.util.Logger; +import io.github.sspanak.tt9.util.Timer; import io.github.sspanak.tt9.db.entities.WordBatch; import io.github.sspanak.tt9.db.entities.WordFile; import io.github.sspanak.tt9.db.exceptions.DictionaryImportAbortedException; @@ -23,8 +23,8 @@ import io.github.sspanak.tt9.db.sqlite.SQLiteOpener; import io.github.sspanak.tt9.db.sqlite.Tables; import io.github.sspanak.tt9.ime.TraditionalT9; import io.github.sspanak.tt9.languages.EmojiLanguage; -import io.github.sspanak.tt9.languages.InvalidLanguageCharactersException; -import io.github.sspanak.tt9.languages.InvalidLanguageException; +import io.github.sspanak.tt9.languages.exceptions.InvalidLanguageCharactersException; +import io.github.sspanak.tt9.languages.exceptions.InvalidLanguageException; import io.github.sspanak.tt9.languages.Language; import io.github.sspanak.tt9.preferences.SettingsStore; import io.github.sspanak.tt9.ui.DictionaryLoadingBar; diff --git a/app/src/main/java/io/github/sspanak/tt9/db/LegacyDb.java b/app/src/main/java/io/github/sspanak/tt9/db/LegacyDb.java index bb0ddd6a..9dbb82ba 100644 --- a/app/src/main/java/io/github/sspanak/tt9/db/LegacyDb.java +++ b/app/src/main/java/io/github/sspanak/tt9/db/LegacyDb.java @@ -4,7 +4,7 @@ import android.app.Activity; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; -import io.github.sspanak.tt9.Logger; +import io.github.sspanak.tt9.util.Logger; public class LegacyDb extends SQLiteOpenHelper { private final String LOG_TAG = getClass().getSimpleName(); diff --git a/app/src/main/java/io/github/sspanak/tt9/db/SlowQueryStats.java b/app/src/main/java/io/github/sspanak/tt9/db/SlowQueryStats.java index 972afa39..b9e3d5d4 100644 --- a/app/src/main/java/io/github/sspanak/tt9/db/SlowQueryStats.java +++ b/app/src/main/java/io/github/sspanak/tt9/db/SlowQueryStats.java @@ -2,9 +2,9 @@ package io.github.sspanak.tt9.db; import java.util.HashMap; -import io.github.sspanak.tt9.Logger; +import io.github.sspanak.tt9.util.Logger; import io.github.sspanak.tt9.languages.Language; -import io.github.sspanak.tt9.languages.TextTools; +import io.github.sspanak.tt9.util.TextTools; import io.github.sspanak.tt9.preferences.SettingsStore; public class SlowQueryStats { diff --git a/app/src/main/java/io/github/sspanak/tt9/db/WordStore.java b/app/src/main/java/io/github/sspanak/tt9/db/WordStore.java index 09104a19..1254b104 100644 --- a/app/src/main/java/io/github/sspanak/tt9/db/WordStore.java +++ b/app/src/main/java/io/github/sspanak/tt9/db/WordStore.java @@ -6,8 +6,8 @@ import androidx.annotation.NonNull; import java.util.ArrayList; -import io.github.sspanak.tt9.Logger; -import io.github.sspanak.tt9.Timer; +import io.github.sspanak.tt9.util.Logger; +import io.github.sspanak.tt9.util.Timer; import io.github.sspanak.tt9.db.entities.Word; import io.github.sspanak.tt9.db.entities.WordList; import io.github.sspanak.tt9.db.sqlite.DeleteOps; @@ -18,7 +18,8 @@ import io.github.sspanak.tt9.db.sqlite.UpdateOps; import io.github.sspanak.tt9.ime.TraditionalT9; import io.github.sspanak.tt9.languages.EmojiLanguage; import io.github.sspanak.tt9.languages.Language; -import io.github.sspanak.tt9.languages.Text; +import io.github.sspanak.tt9.languages.NullLanguage; +import io.github.sspanak.tt9.util.Text; import io.github.sspanak.tt9.preferences.SettingsStore; import io.github.sspanak.tt9.ui.dialogs.AddWordDialog; @@ -68,7 +69,7 @@ public class WordStore { return new ArrayList<>(); } - if (language == null) { + if (language == null || language instanceof NullLanguage) { Logger.w(LOG_TAG, "Attempting to get words for NULL language."); return new ArrayList<>(); } @@ -93,12 +94,12 @@ public class WordStore { @NonNull public ArrayList getSimilarCustom(Language language, String wordFilter) { - return language != null && checkOrNotify() ? readOps.getCustomWords(sqlite.getDb(), language, wordFilter) : new ArrayList<>(); + return language != null && !(language instanceof NullLanguage) && checkOrNotify() ? readOps.getCustomWords(sqlite.getDb(), language, wordFilter) : new ArrayList<>(); } @NonNull public String getLanguageFileHash(Language language) { - return language != null && checkOrNotify() ? readOps.getLanguageFileHash(sqlite.getDb(), language.getId()) : ""; + return language != null && !(language instanceof NullLanguage) && checkOrNotify() ? readOps.getLanguageFileHash(sqlite.getDb(), language.getId()) : ""; } @@ -126,7 +127,7 @@ public class WordStore { public void removeCustomWord(Language language, String word) { - if (language == null || !checkOrNotify()) { + if (language == null || language instanceof NullLanguage || !checkOrNotify()) { return; } @@ -148,7 +149,7 @@ public class WordStore { return AddWordDialog.CODE_BLANK_WORD; } - if (language == null) { + if (language == null || language instanceof NullLanguage) { return AddWordDialog.CODE_INVALID_LANGUAGE; } @@ -191,7 +192,7 @@ public class WordStore { public void makeTopWord(@NonNull Language language, @NonNull String word, @NonNull String sequence) { - if (!checkOrNotify() || word.isEmpty() || sequence.isEmpty()) { + if (!checkOrNotify() || word.isEmpty() || sequence.isEmpty() || language instanceof NullLanguage) { return; } @@ -258,7 +259,7 @@ public class WordStore { public void scheduleNormalization(Language language) { - if (language != null && checkOrNotify()) { + if (language != null && !(language instanceof NullLanguage) && checkOrNotify()) { UpdateOps.scheduleNormalization(sqlite.getDb(), language); } } diff --git a/app/src/main/java/io/github/sspanak/tt9/db/WordStoreAsync.java b/app/src/main/java/io/github/sspanak/tt9/db/WordStoreAsync.java index 46fc61ed..37b372df 100644 --- a/app/src/main/java/io/github/sspanak/tt9/db/WordStoreAsync.java +++ b/app/src/main/java/io/github/sspanak/tt9/db/WordStoreAsync.java @@ -7,7 +7,7 @@ import androidx.annotation.NonNull; import java.util.ArrayList; -import io.github.sspanak.tt9.ConsumerCompat; +import io.github.sspanak.tt9.util.ConsumerCompat; import io.github.sspanak.tt9.languages.Language; public class WordStoreAsync { diff --git a/app/src/main/java/io/github/sspanak/tt9/db/entities/WordBatch.java b/app/src/main/java/io/github/sspanak/tt9/db/entities/WordBatch.java index 657634c6..ab363450 100644 --- a/app/src/main/java/io/github/sspanak/tt9/db/entities/WordBatch.java +++ b/app/src/main/java/io/github/sspanak/tt9/db/entities/WordBatch.java @@ -4,7 +4,7 @@ import androidx.annotation.NonNull; import java.util.ArrayList; -import io.github.sspanak.tt9.languages.InvalidLanguageCharactersException; +import io.github.sspanak.tt9.languages.exceptions.InvalidLanguageCharactersException; import io.github.sspanak.tt9.languages.Language; public class WordBatch { diff --git a/app/src/main/java/io/github/sspanak/tt9/db/entities/WordFile.java b/app/src/main/java/io/github/sspanak/tt9/db/entities/WordFile.java index 5ac732bc..b2ae39cc 100644 --- a/app/src/main/java/io/github/sspanak/tt9/db/entities/WordFile.java +++ b/app/src/main/java/io/github/sspanak/tt9/db/entities/WordFile.java @@ -7,7 +7,7 @@ import java.io.IOException; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; -import io.github.sspanak.tt9.Logger; +import io.github.sspanak.tt9.util.Logger; public class WordFile { private static final String LOG_TAG = WordFile.class.getSimpleName(); diff --git a/app/src/main/java/io/github/sspanak/tt9/db/exporter/AbstractExporter.java b/app/src/main/java/io/github/sspanak/tt9/db/exporter/AbstractExporter.java index 55f8f46b..a97b5734 100644 --- a/app/src/main/java/io/github/sspanak/tt9/db/exporter/AbstractExporter.java +++ b/app/src/main/java/io/github/sspanak/tt9/db/exporter/AbstractExporter.java @@ -18,7 +18,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; -import io.github.sspanak.tt9.ConsumerCompat; +import io.github.sspanak.tt9.util.ConsumerCompat; public abstract class AbstractExporter { final protected static String FILE_EXTENSION = ".csv"; diff --git a/app/src/main/java/io/github/sspanak/tt9/db/exporter/DictionaryExporter.java b/app/src/main/java/io/github/sspanak/tt9/db/exporter/DictionaryExporter.java index 2322071f..2644dff0 100644 --- a/app/src/main/java/io/github/sspanak/tt9/db/exporter/DictionaryExporter.java +++ b/app/src/main/java/io/github/sspanak/tt9/db/exporter/DictionaryExporter.java @@ -7,9 +7,9 @@ import androidx.annotation.NonNull; import java.util.ArrayList; -import io.github.sspanak.tt9.Logger; +import io.github.sspanak.tt9.util.Logger; import io.github.sspanak.tt9.R; -import io.github.sspanak.tt9.Timer; +import io.github.sspanak.tt9.util.Timer; import io.github.sspanak.tt9.db.sqlite.ReadOps; import io.github.sspanak.tt9.db.sqlite.SQLiteOpener; import io.github.sspanak.tt9.languages.Language; diff --git a/app/src/main/java/io/github/sspanak/tt9/db/sqlite/ReadOps.java b/app/src/main/java/io/github/sspanak/tt9/db/sqlite/ReadOps.java index 6128b81f..d450d247 100644 --- a/app/src/main/java/io/github/sspanak/tt9/db/sqlite/ReadOps.java +++ b/app/src/main/java/io/github/sspanak/tt9/db/sqlite/ReadOps.java @@ -9,7 +9,7 @@ import androidx.annotation.NonNull; import java.util.ArrayList; -import io.github.sspanak.tt9.Logger; +import io.github.sspanak.tt9.util.Logger; import io.github.sspanak.tt9.db.SlowQueryStats; import io.github.sspanak.tt9.db.entities.WordList; import io.github.sspanak.tt9.db.entities.WordPositionsStringBuilder; diff --git a/app/src/main/java/io/github/sspanak/tt9/db/sqlite/SQLiteOpener.java b/app/src/main/java/io/github/sspanak/tt9/db/sqlite/SQLiteOpener.java index e4663c2e..249104b2 100644 --- a/app/src/main/java/io/github/sspanak/tt9/db/sqlite/SQLiteOpener.java +++ b/app/src/main/java/io/github/sspanak/tt9/db/sqlite/SQLiteOpener.java @@ -7,7 +7,7 @@ import android.database.sqlite.SQLiteOpenHelper; import java.util.ArrayList; import io.github.sspanak.tt9.BuildConfig; -import io.github.sspanak.tt9.Logger; +import io.github.sspanak.tt9.util.Logger; import io.github.sspanak.tt9.languages.EmojiLanguage; import io.github.sspanak.tt9.languages.Language; import io.github.sspanak.tt9.languages.LanguageCollection; diff --git a/app/src/main/java/io/github/sspanak/tt9/db/sqlite/UpdateOps.java b/app/src/main/java/io/github/sspanak/tt9/db/sqlite/UpdateOps.java index 16bd5d71..39f98548 100644 --- a/app/src/main/java/io/github/sspanak/tt9/db/sqlite/UpdateOps.java +++ b/app/src/main/java/io/github/sspanak/tt9/db/sqlite/UpdateOps.java @@ -5,9 +5,9 @@ import android.database.sqlite.SQLiteStatement; import androidx.annotation.NonNull; -import io.github.sspanak.tt9.Logger; +import io.github.sspanak.tt9.util.Logger; import io.github.sspanak.tt9.languages.Language; -import io.github.sspanak.tt9.languages.Text; +import io.github.sspanak.tt9.util.Text; import io.github.sspanak.tt9.preferences.SettingsStore; diff --git a/app/src/main/java/io/github/sspanak/tt9/ime/KeyPadHandler.java b/app/src/main/java/io/github/sspanak/tt9/ime/KeyPadHandler.java index dc7c6167..11a1a8e9 100644 --- a/app/src/main/java/io/github/sspanak/tt9/ime/KeyPadHandler.java +++ b/app/src/main/java/io/github/sspanak/tt9/ime/KeyPadHandler.java @@ -6,8 +6,8 @@ import android.view.View; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputConnection; -import io.github.sspanak.tt9.Logger; -import io.github.sspanak.tt9.Timer; +import io.github.sspanak.tt9.util.Logger; +import io.github.sspanak.tt9.util.Timer; import io.github.sspanak.tt9.ime.helpers.Key; import io.github.sspanak.tt9.preferences.SettingsStore; import io.github.sspanak.tt9.preferences.screens.debug.ItemInputHandlingMode; diff --git a/app/src/main/java/io/github/sspanak/tt9/ime/TraditionalT9.java b/app/src/main/java/io/github/sspanak/tt9/ime/TraditionalT9.java index 15e1838d..f02287d7 100644 --- a/app/src/main/java/io/github/sspanak/tt9/ime/TraditionalT9.java +++ b/app/src/main/java/io/github/sspanak/tt9/ime/TraditionalT9.java @@ -16,7 +16,6 @@ import androidx.annotation.NonNull; import java.util.ArrayList; import java.util.List; -import io.github.sspanak.tt9.Logger; import io.github.sspanak.tt9.R; import io.github.sspanak.tt9.db.DictionaryLoader; import io.github.sspanak.tt9.db.WordStoreAsync; @@ -30,7 +29,7 @@ import io.github.sspanak.tt9.ime.modes.ModePassthrough; import io.github.sspanak.tt9.ime.modes.ModePredictive; import io.github.sspanak.tt9.languages.Language; import io.github.sspanak.tt9.languages.LanguageCollection; -import io.github.sspanak.tt9.languages.Text; +import io.github.sspanak.tt9.languages.LanguageKind; import io.github.sspanak.tt9.preferences.SettingsStore; import io.github.sspanak.tt9.preferences.helpers.Hotkeys; import io.github.sspanak.tt9.ui.PopupDialogActivity; @@ -38,6 +37,8 @@ import io.github.sspanak.tt9.ui.UI; import io.github.sspanak.tt9.ui.main.MainView; import io.github.sspanak.tt9.ui.tray.StatusBar; import io.github.sspanak.tt9.ui.tray.SuggestionsBar; +import io.github.sspanak.tt9.util.Logger; +import io.github.sspanak.tt9.util.Text; public class TraditionalT9 extends KeyPadHandler { private InputConnection currentInputConnection = null; @@ -469,7 +470,7 @@ public class TraditionalT9 extends KeyPadHandler { } cancelAutoAccept(); - backward = systemLanguage.isRTL() != backward; + backward = LanguageKind.isRTL(systemLanguage) != backward; suggestionBar.scrollToSuggestion(backward ? -1 : 1); mInputMode.setWordStem(suggestionBar.getCurrentSuggestion(), true); setComposingTextWithHighlightedStem(suggestionBar.getCurrentSuggestion()); diff --git a/app/src/main/java/io/github/sspanak/tt9/ime/helpers/InputModeValidator.java b/app/src/main/java/io/github/sspanak/tt9/ime/helpers/InputModeValidator.java index dc03b438..378394e1 100644 --- a/app/src/main/java/io/github/sspanak/tt9/ime/helpers/InputModeValidator.java +++ b/app/src/main/java/io/github/sspanak/tt9/ime/helpers/InputModeValidator.java @@ -4,7 +4,7 @@ import android.content.Context; import java.util.ArrayList; -import io.github.sspanak.tt9.Logger; +import io.github.sspanak.tt9.util.Logger; import io.github.sspanak.tt9.ime.modes.InputMode; import io.github.sspanak.tt9.languages.Language; import io.github.sspanak.tt9.languages.LanguageCollection; diff --git a/app/src/main/java/io/github/sspanak/tt9/ime/helpers/TextField.java b/app/src/main/java/io/github/sspanak/tt9/ime/helpers/TextField.java index 7c916ddb..2cda7209 100644 --- a/app/src/main/java/io/github/sspanak/tt9/ime/helpers/TextField.java +++ b/app/src/main/java/io/github/sspanak/tt9/ime/helpers/TextField.java @@ -14,10 +14,11 @@ import androidx.annotation.NonNull; import java.util.ArrayList; -import io.github.sspanak.tt9.Logger; import io.github.sspanak.tt9.ime.modes.InputMode; import io.github.sspanak.tt9.languages.Language; -import io.github.sspanak.tt9.languages.Text; +import io.github.sspanak.tt9.languages.LanguageKind; +import io.github.sspanak.tt9.util.Logger; +import io.github.sspanak.tt9.util.Text; public class TextField { public static final int IME_ACTION_ENTER = EditorInfo.IME_MASK_ACTION + 1; @@ -191,8 +192,8 @@ public class TextField { boolean keepQuote = false; if (language != null) { // Hebrew and Ukrainian use the respective special characters as letters - keepApostrophe = language.isHebrew() || language.isUkrainian(); - keepQuote = language.isHebrew(); + keepApostrophe = LanguageKind.isHebrew(language) || LanguageKind.isUkrainian(language); + keepQuote = LanguageKind.isHebrew(language); } return before.subStringEndingWord(keepApostrophe, keepQuote) + after.subStringStartingWord(keepApostrophe, keepQuote); diff --git a/app/src/main/java/io/github/sspanak/tt9/ime/modes/InputMode.java b/app/src/main/java/io/github/sspanak/tt9/ime/modes/InputMode.java index b63665b6..b064d2b3 100644 --- a/app/src/main/java/io/github/sspanak/tt9/ime/modes/InputMode.java +++ b/app/src/main/java/io/github/sspanak/tt9/ime/modes/InputMode.java @@ -4,10 +4,11 @@ import androidx.annotation.NonNull; import java.util.ArrayList; -import io.github.sspanak.tt9.Logger; +import io.github.sspanak.tt9.util.Logger; import io.github.sspanak.tt9.ime.helpers.InputType; import io.github.sspanak.tt9.ime.helpers.TextField; import io.github.sspanak.tt9.languages.Language; +import io.github.sspanak.tt9.languages.NaturalLanguage; import io.github.sspanak.tt9.preferences.SettingsStore; abstract public class InputMode { @@ -133,7 +134,7 @@ abstract public class InputMode { return true; } - if (!language.hasUpperCase() || digitSequence.startsWith(Language.PUNCTUATION_KEY) || digitSequence.startsWith(Language.SPECIAL_CHARS_KEY)) { + if (!language.hasUpperCase() || digitSequence.startsWith(NaturalLanguage.PUNCTUATION_KEY) || digitSequence.startsWith(NaturalLanguage.SPECIAL_CHARS_KEY)) { return false; } diff --git a/app/src/main/java/io/github/sspanak/tt9/ime/modes/Mode123.java b/app/src/main/java/io/github/sspanak/tt9/ime/modes/Mode123.java index dc649f40..5f7df0d0 100644 --- a/app/src/main/java/io/github/sspanak/tt9/ime/modes/Mode123.java +++ b/app/src/main/java/io/github/sspanak/tt9/ime/modes/Mode123.java @@ -7,8 +7,9 @@ import java.util.Arrays; import java.util.Collections; import io.github.sspanak.tt9.ime.helpers.InputType; -import io.github.sspanak.tt9.languages.Characters; +import io.github.sspanak.tt9.util.Characters; import io.github.sspanak.tt9.languages.Language; +import io.github.sspanak.tt9.languages.NaturalLanguage; public class Mode123 extends ModePassthrough { @Override public int getId() { return MODE_123; } @@ -82,7 +83,7 @@ public class Mode123 extends ModePassthrough { } @Override protected boolean nextSpecialCharacters() { - return digitSequence.equals(Language.SPECIAL_CHARS_KEY) && super.nextSpecialCharacters(); + return digitSequence.equals(NaturalLanguage.SPECIAL_CHARS_KEY) && super.nextSpecialCharacters(); } @Override public boolean onNumber(int number, boolean hold, int repeat) { diff --git a/app/src/main/java/io/github/sspanak/tt9/ime/modes/ModeABC.java b/app/src/main/java/io/github/sspanak/tt9/ime/modes/ModeABC.java index 96d3c46f..2c6f683e 100644 --- a/app/src/main/java/io/github/sspanak/tt9/ime/modes/ModeABC.java +++ b/app/src/main/java/io/github/sspanak/tt9/ime/modes/ModeABC.java @@ -3,6 +3,8 @@ package io.github.sspanak.tt9.ime.modes; import androidx.annotation.NonNull; import io.github.sspanak.tt9.languages.Language; +import io.github.sspanak.tt9.languages.LanguageKind; +import io.github.sspanak.tt9.languages.NaturalLanguage; import io.github.sspanak.tt9.preferences.SettingsStore; public class ModeABC extends InputMode { @@ -61,7 +63,7 @@ public class ModeABC extends InputMode { @Override protected boolean nextSpecialCharacters() { - if (digitSequence.equals(Language.SPECIAL_CHARS_KEY) && super.nextSpecialCharacters()) { + if (digitSequence.equals(NaturalLanguage.SPECIAL_CHARS_KEY) && super.nextSpecialCharacters()) { suggestions.add(language.getKeyNumber(digitSequence.charAt(0) - '0')); return true; } @@ -102,7 +104,7 @@ public class ModeABC extends InputMode { } String langCode = ""; - if (language.isLatinBased() || language.isCyrillic()) { + if (LanguageKind.isLatinBased(language) || LanguageKind.isCyrillic(language)) { // There are many languages written using the same alphabet, so if the user has enabled multiple, // make it clear which one is it, by appending the country code to "ABC" or "АБВ". langCode = language.getLocale().getCountry(); @@ -110,7 +112,7 @@ public class ModeABC extends InputMode { langCode = langCode.isEmpty() ? language.getName() : langCode; langCode = " / " + langCode; } - String modeString = language.getAbcString() + langCode.toUpperCase(); + String modeString = language.getAbcString() + langCode.toUpperCase(); return (textCase == CASE_LOWER) ? modeString.toLowerCase(language.getLocale()) : modeString.toUpperCase(language.getLocale()); } diff --git a/app/src/main/java/io/github/sspanak/tt9/ime/modes/ModePredictive.java b/app/src/main/java/io/github/sspanak/tt9/ime/modes/ModePredictive.java index 2bcb8a92..328be71b 100644 --- a/app/src/main/java/io/github/sspanak/tt9/ime/modes/ModePredictive.java +++ b/app/src/main/java/io/github/sspanak/tt9/ime/modes/ModePredictive.java @@ -4,7 +4,6 @@ import androidx.annotation.NonNull; import java.util.ArrayList; -import io.github.sspanak.tt9.Logger; import io.github.sspanak.tt9.db.WordStoreAsync; import io.github.sspanak.tt9.ime.helpers.InputType; import io.github.sspanak.tt9.ime.helpers.TextField; @@ -13,8 +12,11 @@ import io.github.sspanak.tt9.ime.modes.helpers.AutoTextCase; import io.github.sspanak.tt9.ime.modes.helpers.Predictions; import io.github.sspanak.tt9.languages.EmojiLanguage; import io.github.sspanak.tt9.languages.Language; -import io.github.sspanak.tt9.languages.Text; +import io.github.sspanak.tt9.languages.LanguageKind; +import io.github.sspanak.tt9.languages.NaturalLanguage; import io.github.sspanak.tt9.preferences.SettingsStore; +import io.github.sspanak.tt9.util.Logger; +import io.github.sspanak.tt9.util.Text; public class ModePredictive extends InputMode { private final String LOG_TAG = getClass().getSimpleName(); @@ -91,7 +93,7 @@ public class ModePredictive extends InputMode { digitSequence = EmojiLanguage.validateEmojiSequence(digitSequence, number); disablePredictions = false; - if (digitSequence.equals(Language.PREFERRED_CHAR_SEQUENCE)) { + if (digitSequence.equals(NaturalLanguage.PREFERRED_CHAR_SEQUENCE)) { autoAcceptTimeout = 0; } } @@ -248,7 +250,7 @@ public class ModePredictive extends InputMode { * options for the current digitSequence. */ private boolean loadStaticSuggestions(Runnable onLoad) { - if (digitSequence.equals(Language.PUNCTUATION_KEY) || digitSequence.equals(Language.SPECIAL_CHARS_KEY)) { + if (digitSequence.equals(NaturalLanguage.PUNCTUATION_KEY) || digitSequence.equals(NaturalLanguage.SPECIAL_CHARS_KEY)) { super.loadSpecialCharacters(language); onLoad.run(); return true; @@ -257,7 +259,7 @@ public class ModePredictive extends InputMode { suggestions.addAll(new EmojiLanguage().getKeyCharacters(digitSequence.charAt(0) - '0', digitSequence.length() - 2)); onLoad.run(); return true; - } else if (digitSequence.startsWith(Language.PREFERRED_CHAR_SEQUENCE)) { + } else if (digitSequence.startsWith(NaturalLanguage.PREFERRED_CHAR_SEQUENCE)) { suggestions.clear(); suggestions.add(settings.getDoubleZeroChar()); onLoad.run(); @@ -313,7 +315,7 @@ public class ModePredictive extends InputMode { // emoji and punctuation are not in the database, so there is no point in // running queries that would update nothing - if (!sequence.startsWith(Language.PUNCTUATION_KEY) && !sequence.startsWith(Language.SPECIAL_CHARS_KEY)) { + if (!sequence.startsWith(NaturalLanguage.PUNCTUATION_KEY) && !sequence.startsWith(NaturalLanguage.SPECIAL_CHARS_KEY)) { WordStoreAsync.makeTopWord(language, currentWord, sequence); } } catch (Exception e) { @@ -340,7 +342,7 @@ public class ModePredictive extends InputMode { @Override protected boolean nextSpecialCharacters() { - return digitSequence.equals(Language.SPECIAL_CHARS_KEY) && super.nextSpecialCharacters(); + return digitSequence.equals(NaturalLanguage.SPECIAL_CHARS_KEY) && super.nextSpecialCharacters(); } @Override @@ -379,22 +381,22 @@ public class ModePredictive extends InputMode { } // special characters always break words - if (autoAcceptTimeout == 0 && !digitSequence.startsWith(Language.SPECIAL_CHARS_KEY)) { + if (autoAcceptTimeout == 0 && !digitSequence.startsWith(NaturalLanguage.SPECIAL_CHARS_KEY)) { return true; } // allow apostrophes in the middle or at the end of Hebrew and Ukrainian words - if (language.isHebrew() || language.isUkrainian()) { + if (LanguageKind.isHebrew(language) || LanguageKind.isUkrainian(language)) { return predictions.noDbWords() - && digitSequence.equals(Language.PUNCTUATION_KEY); + && digitSequence.equals(NaturalLanguage.PUNCTUATION_KEY); } // punctuation breaks words, unless there are database matches ('s, qu', по-, etc...) return !digitSequence.isEmpty() && predictions.noDbWords() - && digitSequence.contains(Language.PUNCTUATION_KEY) + && digitSequence.contains(NaturalLanguage.PUNCTUATION_KEY) && !digitSequence.startsWith(EmojiLanguage.EMOJI_SEQUENCE) && Text.containsOtherThan1(digitSequence); } diff --git a/app/src/main/java/io/github/sspanak/tt9/ime/modes/helpers/AutoSpace.java b/app/src/main/java/io/github/sspanak/tt9/ime/modes/helpers/AutoSpace.java index e38cb316..19303610 100644 --- a/app/src/main/java/io/github/sspanak/tt9/ime/modes/helpers/AutoSpace.java +++ b/app/src/main/java/io/github/sspanak/tt9/ime/modes/helpers/AutoSpace.java @@ -2,7 +2,7 @@ package io.github.sspanak.tt9.ime.modes.helpers; import io.github.sspanak.tt9.ime.helpers.InputType; import io.github.sspanak.tt9.ime.helpers.TextField; -import io.github.sspanak.tt9.languages.Text; +import io.github.sspanak.tt9.util.Text; import io.github.sspanak.tt9.preferences.SettingsStore; public class AutoSpace { diff --git a/app/src/main/java/io/github/sspanak/tt9/ime/modes/helpers/AutoTextCase.java b/app/src/main/java/io/github/sspanak/tt9/ime/modes/helpers/AutoTextCase.java index 67847256..89e25480 100644 --- a/app/src/main/java/io/github/sspanak/tt9/ime/modes/helpers/AutoTextCase.java +++ b/app/src/main/java/io/github/sspanak/tt9/ime/modes/helpers/AutoTextCase.java @@ -1,6 +1,6 @@ package io.github.sspanak.tt9.ime.modes.helpers; -import io.github.sspanak.tt9.languages.Text; +import io.github.sspanak.tt9.util.Text; import io.github.sspanak.tt9.ime.modes.InputMode; import io.github.sspanak.tt9.preferences.SettingsStore; diff --git a/app/src/main/java/io/github/sspanak/tt9/languages/EmojiLanguage.java b/app/src/main/java/io/github/sspanak/tt9/languages/EmojiLanguage.java index 74a55529..3947ebe4 100644 --- a/app/src/main/java/io/github/sspanak/tt9/languages/EmojiLanguage.java +++ b/app/src/main/java/io/github/sspanak/tt9/languages/EmojiLanguage.java @@ -5,6 +5,9 @@ import androidx.annotation.NonNull; import java.util.ArrayList; import java.util.Locale; +import io.github.sspanak.tt9.util.Characters; +import io.github.sspanak.tt9.util.TextTools; + public class EmojiLanguage extends Language { final public static String EMOJI_SEQUENCE = "11"; final private static int CUSTOM_EMOJI_KEY = 3; @@ -17,14 +20,21 @@ public class EmojiLanguage extends Language { name = "Emoji"; } + @NonNull @Override public String getDigitSequenceForWord(String word) { - return TextTools.isGraphic(word) ? CUSTOM_EMOJI_SEQUENCE : null; + return TextTools.isGraphic(word) ? CUSTOM_EMOJI_SEQUENCE : ""; + } + + @NonNull + @Override + public ArrayList getKeyCharacters(int key, int characterGroup) { + return key == 1 && characterGroup >= 0 ? Characters.getEmoji(characterGroup) : new ArrayList<>(); } @Override - public ArrayList getKeyCharacters(int key, int characterGroup) { - return key == 1 && characterGroup >= 0 ? new ArrayList<>(Characters.getEmoji(characterGroup)) : super.getKeyCharacters(key, characterGroup); + public boolean isValidWord(String word) { + return TextTools.isGraphic(word); } public static String validateEmojiSequence(@NonNull String sequence, int next) { diff --git a/app/src/main/java/io/github/sspanak/tt9/languages/Language.java b/app/src/main/java/io/github/sspanak/tt9/languages/Language.java index 46153adc..0be3fca8 100644 --- a/app/src/main/java/io/github/sspanak/tt9/languages/Language.java +++ b/app/src/main/java/io/github/sspanak/tt9/languages/Language.java @@ -3,303 +3,73 @@ package io.github.sspanak.tt9.languages; import androidx.annotation.NonNull; import java.util.ArrayList; -import java.util.HashMap; import java.util.Locale; +import io.github.sspanak.tt9.languages.exceptions.InvalidLanguageCharactersException; -public class Language implements Comparable { - final public static String SPECIAL_CHARS_KEY = "0"; - final public static String PUNCTUATION_KEY = "1"; - final public static String PREFERRED_CHAR_SEQUENCE = "00"; - +abstract public class Language { protected int id; - protected String name; - protected Locale locale; - protected String dictionaryFile; - protected boolean hasUpperCase = true; protected String abcString; - protected final ArrayList> layout = new ArrayList<>(); - private final HashMap characterKeyMap = new HashMap<>(); + protected String dictionaryFile; + protected Locale locale = Locale.ROOT; + protected String name; + protected boolean hasUpperCase = true; - public static Language fromDefinition(LanguageDefinition definition) throws Exception { - if (definition.dictionaryFile.isEmpty()) { - throw new Exception("Invalid definition. Dictionary file must be set."); - } - - if (definition.locale.isEmpty()) { - throw new Exception("Invalid definition. Locale cannot be empty."); - } - - Locale definitionLocale; - if (definition.locale.equals("en")) { - definitionLocale = Locale.ENGLISH; - } else { - String[] parts = definition.locale.split("-", 2); - if (parts.length == 2) { - definitionLocale = new Locale(parts[0], parts[1]); - } else if (parts.length == 1) { - definitionLocale = new Locale(parts[0]); - } else { - throw new Exception("Unrecognized locale format: '" + definition.locale + "'."); - } - } - - Language lang = new Language(); - lang.abcString = definition.abcString.isEmpty() ? null : definition.abcString; - lang.dictionaryFile = definition.getDictionaryFile(); - lang.hasUpperCase = definition.hasUpperCase; - lang.locale = definitionLocale; - lang.name = definition.name.isEmpty() ? lang.name : definition.name; - - for (int key = 0; key <= 9 && key < definition.layout.size(); key++) { - lang.layout.add(keyCharsFromDefinition(key, definition.layout.get(key))); - } - - return lang; - } - - - private static ArrayList keyCharsFromDefinition(int key, ArrayList definitionChars) { - if (key > 1) { - return definitionChars; - } - - final String specialCharsPlaceholder = "SPECIAL"; - final String punctuationPlaceholder = "PUNCTUATION"; - final String arabicStylePlaceholder = punctuationPlaceholder + "_AR"; - final String germanStylePlaceholder = punctuationPlaceholder + "_DE"; - final String frenchStylePlaceholder = punctuationPlaceholder + "_FR"; - - ArrayList keyChars = new ArrayList<>(); - for (String defChar : definitionChars) { - switch (defChar) { - case specialCharsPlaceholder: - keyChars.addAll(Characters.Special); - break; - case punctuationPlaceholder: - keyChars.addAll(Characters.PunctuationEnglish); - break; - case arabicStylePlaceholder: - keyChars.addAll(Characters.PunctuationArabic); - break; - case germanStylePlaceholder: - keyChars.addAll(Characters.PunctuationGerman); - break; - case frenchStylePlaceholder: - keyChars.addAll(Characters.PunctuationFrench); - break; - default: - keyChars.add(defChar); - break; - } - } - - return keyChars; - } - - final public int getId() { - if (id == 0) { - id = generateId(); - } - + public int getId() { return id; } - final public Locale getLocale() { - return locale; - } - - final public String getName() { - if (name == null) { - name = new Text(this, locale.getDisplayLanguage(locale)).capitalize(); - } - - return name; - } - - final public String getDictionaryFile() { - return dictionaryFile; - } - - final public String getAbcString() { - if (abcString == null) { - ArrayList lettersList = getKeyCharacters(2); - - abcString = ""; - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < lettersList.size() && i < 3; i++) { - sb.append(lettersList.get(i)); - } - abcString = sb.toString(); - } - + @NonNull public String getAbcString() { return abcString; } + @NonNull final public String getDictionaryFile() { + return dictionaryFile; + } - public boolean hasUpperCase() { - return hasUpperCase; + @NonNull final public Locale getLocale() { + return locale; } /** - * isLatinBased - * Returns "true" when the language is based on the Latin alphabet or "false" otherwise. + * Returns the characters that the key would type in ABC or Predictive mode. For example, + * the key 2 in English would return A-B-C. + * Keys that have special characters assigned, may have more than one group assigned. The specific + * group is selected by the characterGroup parameter. By default, group 0 is returned. */ - public boolean isLatinBased() { - return getKeyCharacters(2).contains("a"); - } - - public boolean isCyrillic() { - return getKeyCharacters(2).contains("а"); - } - - public boolean isRTL() { - return isArabic() || isHebrew(); - } - - public boolean isGreek() { - return getKeyCharacters(2).contains("α"); - } - - public boolean isArabic() { - return getKeyCharacters(3).contains("ا"); - } - - public boolean isUkrainian() { - return getKeyCharacters(3).contains("є"); - } - - public boolean isHebrew() { - return getKeyCharacters(3).contains("א"); - } - - /* ************ utility ************ */ - - /** - * generateId - * Uses the letters of the Locale to generate an ID for the language. - * Each letter is converted to uppercase and used as a 5-bit integer. Then the 5-bits - * are packed to form a 10-bit or a 20-bit integer, depending on the Locale length. - * - * Example (2-letter Locale) - * "en" - * -> "E" | "N" - * -> 5 | 448 (shift the 2nd number by 5 bits, so its bits would not overlap with the 1st one) - * -> 543 - * - * Example (4-letter Locale) - * "bg-BG" - * -> "B" | "G" | "B" | "G" - * -> 2 | 224 | 2048 | 229376 (shift each 5-bit number, not overlap with the previous ones) - * -> 231650 - * - * Minimum ID: "aa" -> 33 - * Maximum ID: "zz-ZZ" -> 879450 - */ - private int generateId() { - String idString = (locale.getLanguage() + locale.getCountry()).toUpperCase(); - int idInt = 0; - for (int i = 0; i < idString.length(); i++) { - idInt |= ((idString.codePointAt(i) & 31) << (i * 5)); - } - - return idInt; - } - - private void generateCharacterKeyMap() { - characterKeyMap.clear(); - for (int digit = 0; digit <= 9; digit++) { - for (String keyChar : getKeyCharacters(digit)) { - characterKeyMap.put(keyChar.charAt(0), String.valueOf(digit)); - } - } - } - - public ArrayList getKeyCharacters(int key, int characterGroup) { - if (key < 0 || key >= layout.size()) { - return new ArrayList<>(); - } - - ArrayList chars = layout.get(key); - if (key == 0) { - if (characterGroup > 1) { - chars = new ArrayList<>(); - } else if (characterGroup == 1) { - chars = new ArrayList<>(Characters.Currency); - } - } - - return chars; - } - - public ArrayList getKeyCharacters(int key) { + @NonNull abstract public ArrayList getKeyCharacters(int key, int characterGroup); + @NonNull public ArrayList getKeyCharacters(int key) { return getKeyCharacters(key, 0); } - public String getKeyNumber(int key) { - if (key > 10 || key < 0) { - return null; - } else { - return isArabic() ? Characters.ArabicNumbers.get(key) : String.valueOf(key); - } + @NonNull public String getKeyNumber(int key) { + return String.valueOf(key); } - public String getDigitSequenceForWord(String word) throws InvalidLanguageCharactersException { - StringBuilder sequence = new StringBuilder(); - String lowerCaseWord = word.toLowerCase(locale); + @NonNull public String getName() { + return name; + } - if (characterKeyMap.isEmpty()) { - generateCharacterKeyMap(); - } + final public boolean hasUpperCase() { + return hasUpperCase; + } - for (int i = 0; i < lowerCaseWord.length(); i++) { - char letter = lowerCaseWord.charAt(i); - if (!characterKeyMap.containsKey(letter)) { - throw new InvalidLanguageCharactersException(this, "Failed generating digit sequence for word: '" + word); - } - - sequence.append(characterKeyMap.get(letter)); - } - - return sequence.toString(); + @NonNull + @Override + final public String toString() { + return getName(); } /** * Checks whether the given word contains characters outside of the language alphabet. */ - public boolean isValidWord(String word) { - if (word == null || word.isEmpty() || (word.length() == 1 && Character.isDigit(word.charAt(0)))) { - return true; - } + abstract public boolean isValidWord(String word); - String lowerCaseWord = word.toLowerCase(locale); - if (characterKeyMap.isEmpty()) { - generateCharacterKeyMap(); - } - - for (int i = 0; i < lowerCaseWord.length(); i++) { - if (!characterKeyMap.containsKey(lowerCaseWord.charAt(i))) { - return false; - } - } - - return true; - } - - - @NonNull - @Override - public String toString() { - return getName(); - } - - - @Override - public int compareTo(Language other) { - String key = getName().equals("Suomi") ? "su" : getLocale().toString(); - String otherKey = other.getName().equals("Suomi") ? "su" : other.getLocale().toString(); - return key.compareTo(otherKey); - } + /** + * Converts a word to a sequence of digits based on the language's keyboard layout. + * For example: "food" -> "3663" + */ + @NonNull abstract public String getDigitSequenceForWord(String word) throws InvalidLanguageCharactersException; } diff --git a/app/src/main/java/io/github/sspanak/tt9/languages/LanguageCollection.java b/app/src/main/java/io/github/sspanak/tt9/languages/LanguageCollection.java index fc40efca..b87301d4 100644 --- a/app/src/main/java/io/github/sspanak/tt9/languages/LanguageCollection.java +++ b/app/src/main/java/io/github/sspanak/tt9/languages/LanguageCollection.java @@ -9,18 +9,18 @@ import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; -import io.github.sspanak.tt9.Logger; +import io.github.sspanak.tt9.util.Logger; import io.github.sspanak.tt9.ime.helpers.SystemSettings; public class LanguageCollection { private static LanguageCollection self; - private final HashMap languages = new HashMap<>(); + private final HashMap languages = new HashMap<>(); private LanguageCollection(Context context) { for (String file : LanguageDefinition.getAllFiles(context.getAssets())) { try { - Language lang = Language.fromDefinition(LanguageDefinition.fromFile(context.getAssets(), file)); + NaturalLanguage lang = NaturalLanguage.fromDefinition(LanguageDefinition.fromFile(context.getAssets(), file)); languages.put(lang.getId(), lang); } catch (Exception e) { Logger.e("tt9.LanguageCollection", "Skipping invalid language: '" + file + "'. " + e.getMessage()); @@ -38,7 +38,7 @@ public class LanguageCollection { } @Nullable - public static Language getLanguage(Context context, int langId) { + public static NaturalLanguage getLanguage(Context context, int langId) { if (getInstance(context).languages.containsKey(langId)) { return getInstance(context).languages.get(langId); } @@ -53,8 +53,8 @@ public class LanguageCollection { } @Nullable - public static Language getByLocale(Context context, String locale) { - for (Language lang : getInstance(context).languages.values()) { + public static NaturalLanguage getByLocale(Context context, String locale) { + for (NaturalLanguage lang : getInstance(context).languages.values()) { if (lang.getLocale().toString().equals(locale)) { return lang; } @@ -64,14 +64,13 @@ public class LanguageCollection { } public static ArrayList getAll(Context context, ArrayList languageIds, boolean sort) { - ArrayList langList = new ArrayList<>(); - if (languageIds == null) { - return langList; + return new ArrayList<>(); } + ArrayList langList = new ArrayList<>(); for (int languageId : languageIds) { - Language lang = getLanguage(context, languageId); + NaturalLanguage lang = getLanguage(context, languageId); if (lang != null) { langList.add(lang); } @@ -81,8 +80,7 @@ public class LanguageCollection { Collections.sort(langList); } - - return langList; + return new ArrayList<>(langList); } public static ArrayList getAll(Context context, ArrayList languageIds) { @@ -90,13 +88,13 @@ public class LanguageCollection { } public static ArrayList getAll(Context context, boolean sort) { - ArrayList langList = new ArrayList<>(getInstance(context).languages.values()); + ArrayList langList = new ArrayList<>(getInstance(context).languages.values()); if (sort) { Collections.sort(langList); } - return langList; + return new ArrayList<>(langList); } public static ArrayList getAll(Context context) { diff --git a/app/src/main/java/io/github/sspanak/tt9/languages/LanguageDefinition.java b/app/src/main/java/io/github/sspanak/tt9/languages/LanguageDefinition.java index 468fe1fb..9357daa6 100644 --- a/app/src/main/java/io/github/sspanak/tt9/languages/LanguageDefinition.java +++ b/app/src/main/java/io/github/sspanak/tt9/languages/LanguageDefinition.java @@ -12,7 +12,7 @@ import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; -import io.github.sspanak.tt9.Logger; +import io.github.sspanak.tt9.util.Logger; public class LanguageDefinition { private static final String languagesDir = "languages"; diff --git a/app/src/main/java/io/github/sspanak/tt9/languages/LanguageKind.java b/app/src/main/java/io/github/sspanak/tt9/languages/LanguageKind.java new file mode 100644 index 00000000..40047b71 --- /dev/null +++ b/app/src/main/java/io/github/sspanak/tt9/languages/LanguageKind.java @@ -0,0 +1,11 @@ +package io.github.sspanak.tt9.languages; + +public class LanguageKind { + public static boolean isArabic(Language language) { return language != null && language.getKeyCharacters(3).contains("ا"); } + public static boolean isCyrillic(Language language) { return language != null && language.getKeyCharacters(2).contains("а"); } + public static boolean isHebrew(Language language) { return language != null && language.getKeyCharacters(3).contains("א"); } + public static boolean isGreek(Language language) { return language != null && language.getKeyCharacters(2).contains("α"); } + public static boolean isLatinBased(Language language) { return language != null && language.getKeyCharacters(2).contains("a"); } + public static boolean isRTL(Language language) { return isArabic(language) || isHebrew(language); } + public static boolean isUkrainian(Language language) { return language != null && language.getKeyCharacters(3).contains("є"); } +} diff --git a/app/src/main/java/io/github/sspanak/tt9/languages/NaturalLanguage.java b/app/src/main/java/io/github/sspanak/tt9/languages/NaturalLanguage.java new file mode 100644 index 00000000..2acaa296 --- /dev/null +++ b/app/src/main/java/io/github/sspanak/tt9/languages/NaturalLanguage.java @@ -0,0 +1,247 @@ +package io.github.sspanak.tt9.languages; + +import androidx.annotation.NonNull; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Locale; + +import io.github.sspanak.tt9.languages.exceptions.InvalidLanguageCharactersException; +import io.github.sspanak.tt9.util.Characters; +import io.github.sspanak.tt9.util.Text; + + +public class NaturalLanguage extends Language implements Comparable { + final public static String SPECIAL_CHARS_KEY = "0"; + final public static String PUNCTUATION_KEY = "1"; + final public static String PREFERRED_CHAR_SEQUENCE = "00"; + + + protected final ArrayList> layout = new ArrayList<>(); + private final HashMap characterKeyMap = new HashMap<>(); + + + public static NaturalLanguage fromDefinition(LanguageDefinition definition) throws Exception { + if (definition.dictionaryFile.isEmpty()) { + throw new Exception("Invalid definition. Dictionary file must be set."); + } + + NaturalLanguage lang = new NaturalLanguage(); + lang.abcString = definition.abcString.isEmpty() ? null : definition.abcString; + lang.dictionaryFile = definition.getDictionaryFile(); + lang.hasUpperCase = definition.hasUpperCase; + lang.name = definition.name.isEmpty() ? lang.name : definition.name; + lang.setLocale(definition); + lang.setLayout(definition); + + return lang; + } + + + private void setLocale(LanguageDefinition definition) throws Exception { + if (definition.locale.isEmpty()) { + throw new Exception("Invalid definition. Locale cannot be empty."); + } + + if (definition.locale.equals("en")) { + locale = Locale.ENGLISH; + } else { + String[] parts = definition.locale.split("-", 2); + if (parts.length == 2) { + locale = new Locale(parts[0], parts[1]); + } else if (parts.length == 1) { + locale = new Locale(parts[0]); + } else { + throw new Exception("Unrecognized locale format: '" + definition.locale + "'."); + } + } + } + + + private void setLayout(LanguageDefinition definition) { + for (int key = 0; key <= 9 && key < definition.layout.size(); key++) { + layout.add( + key, + key > 1 ? definition.layout.get(key) : generateSpecialChars(definition.layout.get(key)) + ); + } + + generateCharacterKeyMap(); + } + + + private ArrayList generateSpecialChars(ArrayList definitionChars) { + final String SPECIAL_CHARS_PLACEHOLDER = "SPECIAL"; + final String PUNCTUATION_PLACEHOLDER = "PUNCTUATION"; + final String ARABIC_PUNCTUATION_STYLE = PUNCTUATION_PLACEHOLDER + "_AR"; + final String GERMAN_PUNCTUATION_STYLE = PUNCTUATION_PLACEHOLDER + "_DE"; + final String FRENCH_PUNCTUATION_STYLE = PUNCTUATION_PLACEHOLDER + "_FR"; + + ArrayList keyChars = new ArrayList<>(); + for (String defChar : definitionChars) { + switch (defChar) { + case SPECIAL_CHARS_PLACEHOLDER: + keyChars.addAll(Characters.Special); + break; + case PUNCTUATION_PLACEHOLDER: + keyChars.addAll(Characters.PunctuationEnglish); + break; + case ARABIC_PUNCTUATION_STYLE: + keyChars.addAll(Characters.PunctuationArabic); + break; + case GERMAN_PUNCTUATION_STYLE: + keyChars.addAll(Characters.PunctuationGerman); + break; + case FRENCH_PUNCTUATION_STYLE: + keyChars.addAll(Characters.PunctuationFrench); + break; + default: + keyChars.add(defChar); + break; + } + } + + return keyChars; + } + + + /** + * generateId + * Uses the letters of the Locale to generate an ID for the language. + * Each letter is converted to uppercase and used as a 5-bit integer. Then the 5-bits + * are packed to form a 10-bit or a 20-bit integer, depending on the Locale length. + * + * Example (2-letter Locale) + * "en" + * -> "E" | "N" + * -> 5 | 448 (shift the 2nd number by 5 bits, so its bits would not overlap with the 1st one) + * -> 543 + * + * Example (4-letter Locale) + * "bg-BG" + * -> "B" | "G" | "B" | "G" + * -> 2 | 224 | 2048 | 229376 (shift each 5-bit number, not overlap with the previous ones) + * -> 231650 + * + * Minimum ID: "aa" -> 33 + * Maximum ID: "zz-ZZ" -> 879450 + */ + @Override + public int getId() { + if (id == 0) { + String idString = (locale.getLanguage() + locale.getCountry()).toUpperCase(); + for (int i = 0; i < idString.length(); i++) { + id |= (idString.codePointAt(i) & 31) << (i * 5); + } + } + + return id; + } + + + @NonNull + @Override + public String getAbcString() { + if (abcString == null) { + ArrayList lettersList = getKeyCharacters(2); + + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < lettersList.size() && i < 3; i++) { + sb.append(lettersList.get(i)); + } + + abcString = sb.toString(); + } + + return abcString; + } + + @NonNull + @Override + public String getName() { + if (name == null) { + name = new Text(this, locale.getDisplayLanguage(locale)).capitalize(); + } + + return name; + } + + + private void generateCharacterKeyMap() { + characterKeyMap.clear(); + for (int digit = 0; digit <= 9; digit++) { + for (String keyChar : getKeyCharacters(digit)) { + characterKeyMap.put(keyChar.charAt(0), String.valueOf(digit)); + } + } + } + + + @NonNull + public ArrayList getKeyCharacters(int key, int characterGroup) { + if (key < 0 || key >= layout.size()) { + return new ArrayList<>(); + } + + ArrayList chars = layout.get(key); + if (key == 0) { + if (characterGroup > 1) { + chars = new ArrayList<>(); + } else if (characterGroup == 1) { + chars = new ArrayList<>(Characters.Currency); + } + } + + return chars; + } + + + @NonNull + public String getKeyNumber(int key) { + return key >= 0 && key < 10 && LanguageKind.isArabic(this) ? Characters.ArabicNumbers.get(key) : super.getKeyNumber(key); + } + + + @NonNull + public String getDigitSequenceForWord(String word) throws InvalidLanguageCharactersException { + StringBuilder sequence = new StringBuilder(); + String lowerCaseWord = word.toLowerCase(locale); + + for (int i = 0; i < lowerCaseWord.length(); i++) { + char letter = lowerCaseWord.charAt(i); + if (!characterKeyMap.containsKey(letter)) { + throw new InvalidLanguageCharactersException(this, "Failed generating digit sequence for word: '" + word); + } + + sequence.append(characterKeyMap.get(letter)); + } + + return sequence.toString(); + } + + + + public boolean isValidWord(String word) { + if (word == null || word.isEmpty() || (word.length() == 1 && Character.isDigit(word.charAt(0)))) { + return true; + } + + String lowerCaseWord = word.toLowerCase(locale); + + for (int i = 0; i < lowerCaseWord.length(); i++) { + if (!characterKeyMap.containsKey(lowerCaseWord.charAt(i))) { + return false; + } + } + + return true; + } + + + @Override + public int compareTo(NaturalLanguage other) { + String key = getName().equals("Suomi") ? "su" : getLocale().toString(); + String otherKey = other.getName().equals("Suomi") ? "su" : other.getLocale().toString(); + return key.compareTo(otherKey); + } +} diff --git a/app/src/main/java/io/github/sspanak/tt9/languages/NullLanguage.java b/app/src/main/java/io/github/sspanak/tt9/languages/NullLanguage.java index 0d8cc43f..e448c711 100644 --- a/app/src/main/java/io/github/sspanak/tt9/languages/NullLanguage.java +++ b/app/src/main/java/io/github/sspanak/tt9/languages/NullLanguage.java @@ -2,6 +2,9 @@ package io.github.sspanak.tt9.languages; import android.content.Context; +import androidx.annotation.NonNull; + +import java.util.ArrayList; import java.util.Locale; import io.github.sspanak.tt9.R; @@ -10,6 +13,24 @@ public class NullLanguage extends Language { public NullLanguage(Context context) { locale = Locale.ROOT; name = context.getString(R.string.no_language); - abcString = "abc"; + abcString = "ABC"; + hasUpperCase = false; + } + + @NonNull + @Override + public ArrayList getKeyCharacters(int key, int characterGroup) { + return new ArrayList<>(); + } + + @Override + public boolean isValidWord(String word) { + return false; + } + + @NonNull + @Override + public String getDigitSequenceForWord(String word) { + return ""; } } diff --git a/app/src/main/java/io/github/sspanak/tt9/languages/InvalidLanguageCharactersException.java b/app/src/main/java/io/github/sspanak/tt9/languages/exceptions/InvalidLanguageCharactersException.java similarity index 72% rename from app/src/main/java/io/github/sspanak/tt9/languages/InvalidLanguageCharactersException.java rename to app/src/main/java/io/github/sspanak/tt9/languages/exceptions/InvalidLanguageCharactersException.java index 44329c9b..21d4c41f 100644 --- a/app/src/main/java/io/github/sspanak/tt9/languages/InvalidLanguageCharactersException.java +++ b/app/src/main/java/io/github/sspanak/tt9/languages/exceptions/InvalidLanguageCharactersException.java @@ -1,4 +1,6 @@ -package io.github.sspanak.tt9.languages; +package io.github.sspanak.tt9.languages.exceptions; + +import io.github.sspanak.tt9.languages.Language; public class InvalidLanguageCharactersException extends Exception { diff --git a/app/src/main/java/io/github/sspanak/tt9/languages/InvalidLanguageException.java b/app/src/main/java/io/github/sspanak/tt9/languages/exceptions/InvalidLanguageException.java similarity index 70% rename from app/src/main/java/io/github/sspanak/tt9/languages/InvalidLanguageException.java rename to app/src/main/java/io/github/sspanak/tt9/languages/exceptions/InvalidLanguageException.java index 4c2da840..52194862 100644 --- a/app/src/main/java/io/github/sspanak/tt9/languages/InvalidLanguageException.java +++ b/app/src/main/java/io/github/sspanak/tt9/languages/exceptions/InvalidLanguageException.java @@ -1,4 +1,4 @@ -package io.github.sspanak.tt9.languages; +package io.github.sspanak.tt9.languages.exceptions; public class InvalidLanguageException extends Exception { public InvalidLanguageException() { super("Invalid Language"); } diff --git a/app/src/main/java/io/github/sspanak/tt9/preferences/PreferencesActivity.java b/app/src/main/java/io/github/sspanak/tt9/preferences/PreferencesActivity.java index 88405901..2ddc3379 100644 --- a/app/src/main/java/io/github/sspanak/tt9/preferences/PreferencesActivity.java +++ b/app/src/main/java/io/github/sspanak/tt9/preferences/PreferencesActivity.java @@ -13,7 +13,7 @@ import androidx.fragment.app.FragmentTransaction; import androidx.preference.Preference; import androidx.preference.PreferenceFragmentCompat; -import io.github.sspanak.tt9.Logger; +import io.github.sspanak.tt9.util.Logger; import io.github.sspanak.tt9.R; import io.github.sspanak.tt9.db.LegacyDb; import io.github.sspanak.tt9.db.WordStoreAsync; diff --git a/app/src/main/java/io/github/sspanak/tt9/preferences/SettingsStore.java b/app/src/main/java/io/github/sspanak/tt9/preferences/SettingsStore.java index cec04b0d..80235a1b 100644 --- a/app/src/main/java/io/github/sspanak/tt9/preferences/SettingsStore.java +++ b/app/src/main/java/io/github/sspanak/tt9/preferences/SettingsStore.java @@ -13,7 +13,7 @@ import java.util.Collections; import java.util.HashSet; import java.util.Set; -import io.github.sspanak.tt9.Logger; +import io.github.sspanak.tt9.util.Logger; import io.github.sspanak.tt9.ime.modes.InputMode; import io.github.sspanak.tt9.languages.LanguageCollection; import io.github.sspanak.tt9.preferences.screens.debug.ItemInputHandlingMode; diff --git a/app/src/main/java/io/github/sspanak/tt9/preferences/items/ItemClickable.java b/app/src/main/java/io/github/sspanak/tt9/preferences/items/ItemClickable.java index 9306347d..c3343d14 100644 --- a/app/src/main/java/io/github/sspanak/tt9/preferences/items/ItemClickable.java +++ b/app/src/main/java/io/github/sspanak/tt9/preferences/items/ItemClickable.java @@ -5,7 +5,7 @@ import androidx.preference.Preference; import java.util.ArrayList; -import io.github.sspanak.tt9.Logger; +import io.github.sspanak.tt9.util.Logger; import io.github.sspanak.tt9.preferences.SettingsStore; abstract public class ItemClickable { diff --git a/app/src/main/java/io/github/sspanak/tt9/preferences/items/ItemDropDown.java b/app/src/main/java/io/github/sspanak/tt9/preferences/items/ItemDropDown.java index 58dd5f96..c6856702 100644 --- a/app/src/main/java/io/github/sspanak/tt9/preferences/items/ItemDropDown.java +++ b/app/src/main/java/io/github/sspanak/tt9/preferences/items/ItemDropDown.java @@ -6,7 +6,7 @@ import androidx.preference.Preference; import java.util.ArrayList; import java.util.LinkedHashMap; -import io.github.sspanak.tt9.Logger; +import io.github.sspanak.tt9.util.Logger; public class ItemDropDown { private final DropDownPreference item; diff --git a/app/src/main/java/io/github/sspanak/tt9/preferences/screens/BaseScreenFragment.java b/app/src/main/java/io/github/sspanak/tt9/preferences/screens/BaseScreenFragment.java index ca2f5e18..ba03a747 100644 --- a/app/src/main/java/io/github/sspanak/tt9/preferences/screens/BaseScreenFragment.java +++ b/app/src/main/java/io/github/sspanak/tt9/preferences/screens/BaseScreenFragment.java @@ -6,7 +6,7 @@ import android.view.MenuItem; import androidx.annotation.NonNull; import androidx.preference.PreferenceFragmentCompat; -import io.github.sspanak.tt9.Logger; +import io.github.sspanak.tt9.util.Logger; import io.github.sspanak.tt9.preferences.PreferencesActivity; abstract public class BaseScreenFragment extends PreferenceFragmentCompat { diff --git a/app/src/main/java/io/github/sspanak/tt9/preferences/screens/MainSettingsScreen.java b/app/src/main/java/io/github/sspanak/tt9/preferences/screens/MainSettingsScreen.java index 0a8985d3..77c88da1 100644 --- a/app/src/main/java/io/github/sspanak/tt9/preferences/screens/MainSettingsScreen.java +++ b/app/src/main/java/io/github/sspanak/tt9/preferences/screens/MainSettingsScreen.java @@ -10,7 +10,7 @@ import java.util.Arrays; import java.util.regex.Pattern; import io.github.sspanak.tt9.BuildConfig; -import io.github.sspanak.tt9.Logger; +import io.github.sspanak.tt9.util.Logger; import io.github.sspanak.tt9.R; import io.github.sspanak.tt9.ime.helpers.SystemSettings; import io.github.sspanak.tt9.preferences.PreferencesActivity; diff --git a/app/src/main/java/io/github/sspanak/tt9/preferences/screens/debug/DebugScreen.java b/app/src/main/java/io/github/sspanak/tt9/preferences/screens/debug/DebugScreen.java index 3e7b94a0..24e1b0d6 100644 --- a/app/src/main/java/io/github/sspanak/tt9/preferences/screens/debug/DebugScreen.java +++ b/app/src/main/java/io/github/sspanak/tt9/preferences/screens/debug/DebugScreen.java @@ -12,7 +12,7 @@ import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; -import io.github.sspanak.tt9.Logger; +import io.github.sspanak.tt9.util.Logger; import io.github.sspanak.tt9.R; import io.github.sspanak.tt9.preferences.PreferencesActivity; import io.github.sspanak.tt9.preferences.screens.BaseScreenFragment; diff --git a/app/src/main/java/io/github/sspanak/tt9/preferences/screens/debug/ItemLogLevel.java b/app/src/main/java/io/github/sspanak/tt9/preferences/screens/debug/ItemLogLevel.java index 9d258cf0..854095e2 100644 --- a/app/src/main/java/io/github/sspanak/tt9/preferences/screens/debug/ItemLogLevel.java +++ b/app/src/main/java/io/github/sspanak/tt9/preferences/screens/debug/ItemLogLevel.java @@ -7,7 +7,7 @@ import androidx.preference.Preference; import java.util.LinkedHashMap; -import io.github.sspanak.tt9.Logger; +import io.github.sspanak.tt9.util.Logger; import io.github.sspanak.tt9.preferences.items.ItemDropDown; class ItemLogLevel extends ItemDropDown { diff --git a/app/src/main/java/io/github/sspanak/tt9/preferences/screens/deleteWords/PreferenceSearchWords.java b/app/src/main/java/io/github/sspanak/tt9/preferences/screens/deleteWords/PreferenceSearchWords.java index 0e3bb47c..002c295d 100644 --- a/app/src/main/java/io/github/sspanak/tt9/preferences/screens/deleteWords/PreferenceSearchWords.java +++ b/app/src/main/java/io/github/sspanak/tt9/preferences/screens/deleteWords/PreferenceSearchWords.java @@ -11,8 +11,8 @@ import androidx.preference.PreferenceViewHolder; import java.util.ArrayList; -import io.github.sspanak.tt9.ConsumerCompat; -import io.github.sspanak.tt9.Logger; +import io.github.sspanak.tt9.util.ConsumerCompat; +import io.github.sspanak.tt9.util.Logger; import io.github.sspanak.tt9.R; import io.github.sspanak.tt9.db.WordStoreAsync; import io.github.sspanak.tt9.languages.Language; diff --git a/app/src/main/java/io/github/sspanak/tt9/preferences/screens/deleteWords/TextChangeListener.java b/app/src/main/java/io/github/sspanak/tt9/preferences/screens/deleteWords/TextChangeListener.java index 6070ecfb..e43b5cd3 100644 --- a/app/src/main/java/io/github/sspanak/tt9/preferences/screens/deleteWords/TextChangeListener.java +++ b/app/src/main/java/io/github/sspanak/tt9/preferences/screens/deleteWords/TextChangeListener.java @@ -7,7 +7,7 @@ import android.text.TextWatcher; import androidx.annotation.NonNull; -import io.github.sspanak.tt9.ConsumerCompat; +import io.github.sspanak.tt9.util.ConsumerCompat; import io.github.sspanak.tt9.preferences.SettingsStore; class TextChangeListener implements TextWatcher { diff --git a/app/src/main/java/io/github/sspanak/tt9/preferences/screens/hotkeys/ItemResetKeys.java b/app/src/main/java/io/github/sspanak/tt9/preferences/screens/hotkeys/ItemResetKeys.java index 7f58433f..5bfd3318 100644 --- a/app/src/main/java/io/github/sspanak/tt9/preferences/screens/hotkeys/ItemResetKeys.java +++ b/app/src/main/java/io/github/sspanak/tt9/preferences/screens/hotkeys/ItemResetKeys.java @@ -1,7 +1,5 @@ package io.github.sspanak.tt9.preferences.screens.hotkeys; -import android.content.Context; - import androidx.preference.Preference; import io.github.sspanak.tt9.R; diff --git a/app/src/main/java/io/github/sspanak/tt9/preferences/screens/hotkeys/SectionKeymap.java b/app/src/main/java/io/github/sspanak/tt9/preferences/screens/hotkeys/SectionKeymap.java index a29b4960..4c15200e 100644 --- a/app/src/main/java/io/github/sspanak/tt9/preferences/screens/hotkeys/SectionKeymap.java +++ b/app/src/main/java/io/github/sspanak/tt9/preferences/screens/hotkeys/SectionKeymap.java @@ -6,7 +6,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Objects; -import io.github.sspanak.tt9.Logger; +import io.github.sspanak.tt9.util.Logger; import io.github.sspanak.tt9.preferences.PreferencesActivity; import io.github.sspanak.tt9.preferences.SettingsStore; import io.github.sspanak.tt9.preferences.helpers.Hotkeys; diff --git a/app/src/main/java/io/github/sspanak/tt9/preferences/screens/keypad/ItemSelectZeroKeyCharacter.java b/app/src/main/java/io/github/sspanak/tt9/preferences/screens/keypad/ItemSelectZeroKeyCharacter.java index 9b55e20f..06ef1314 100644 --- a/app/src/main/java/io/github/sspanak/tt9/preferences/screens/keypad/ItemSelectZeroKeyCharacter.java +++ b/app/src/main/java/io/github/sspanak/tt9/preferences/screens/keypad/ItemSelectZeroKeyCharacter.java @@ -7,7 +7,7 @@ import androidx.preference.DropDownPreference; import java.util.LinkedHashMap; -import io.github.sspanak.tt9.Logger; +import io.github.sspanak.tt9.util.Logger; import io.github.sspanak.tt9.R; class ItemSelectZeroKeyCharacter { diff --git a/app/src/main/java/io/github/sspanak/tt9/preferences/screens/languages/ItemExportDictionary.java b/app/src/main/java/io/github/sspanak/tt9/preferences/screens/languages/ItemExportDictionary.java index 8634d892..cfbccd63 100644 --- a/app/src/main/java/io/github/sspanak/tt9/preferences/screens/languages/ItemExportDictionary.java +++ b/app/src/main/java/io/github/sspanak/tt9/preferences/screens/languages/ItemExportDictionary.java @@ -2,7 +2,7 @@ package io.github.sspanak.tt9.preferences.screens.languages; import androidx.preference.Preference; -import io.github.sspanak.tt9.Logger; +import io.github.sspanak.tt9.util.Logger; import io.github.sspanak.tt9.db.exporter.DictionaryExporter; import io.github.sspanak.tt9.languages.LanguageCollection; import io.github.sspanak.tt9.preferences.PreferencesActivity; diff --git a/app/src/main/java/io/github/sspanak/tt9/ui/DictionaryLoadingBar.java b/app/src/main/java/io/github/sspanak/tt9/ui/DictionaryLoadingBar.java index 5e5e4b77..ce07b3b9 100644 --- a/app/src/main/java/io/github/sspanak/tt9/ui/DictionaryLoadingBar.java +++ b/app/src/main/java/io/github/sspanak/tt9/ui/DictionaryLoadingBar.java @@ -9,8 +9,8 @@ import java.util.Locale; import io.github.sspanak.tt9.R; import io.github.sspanak.tt9.db.exceptions.DictionaryImportException; -import io.github.sspanak.tt9.languages.InvalidLanguageCharactersException; -import io.github.sspanak.tt9.languages.InvalidLanguageException; +import io.github.sspanak.tt9.languages.exceptions.InvalidLanguageCharactersException; +import io.github.sspanak.tt9.languages.exceptions.InvalidLanguageException; import io.github.sspanak.tt9.languages.Language; import io.github.sspanak.tt9.languages.LanguageCollection; diff --git a/app/src/main/java/io/github/sspanak/tt9/ui/PopupDialogActivity.java b/app/src/main/java/io/github/sspanak/tt9/ui/PopupDialogActivity.java index 477d970a..a2f55503 100644 --- a/app/src/main/java/io/github/sspanak/tt9/ui/PopupDialogActivity.java +++ b/app/src/main/java/io/github/sspanak/tt9/ui/PopupDialogActivity.java @@ -5,7 +5,7 @@ import android.os.Bundle; import androidx.appcompat.app.AppCompatActivity; -import io.github.sspanak.tt9.Logger; +import io.github.sspanak.tt9.util.Logger; import io.github.sspanak.tt9.ime.TraditionalT9; import io.github.sspanak.tt9.ui.dialogs.AddWordDialog; import io.github.sspanak.tt9.ui.dialogs.ConfirmDictionaryUpdateDialog; diff --git a/app/src/main/java/io/github/sspanak/tt9/ui/dialogs/AddWordDialog.java b/app/src/main/java/io/github/sspanak/tt9/ui/dialogs/AddWordDialog.java index e935edc6..c35e4578 100644 --- a/app/src/main/java/io/github/sspanak/tt9/ui/dialogs/AddWordDialog.java +++ b/app/src/main/java/io/github/sspanak/tt9/ui/dialogs/AddWordDialog.java @@ -5,7 +5,7 @@ import android.content.Intent; import androidx.annotation.NonNull; -import io.github.sspanak.tt9.ConsumerCompat; +import io.github.sspanak.tt9.util.ConsumerCompat; import io.github.sspanak.tt9.R; import io.github.sspanak.tt9.db.WordStoreAsync; import io.github.sspanak.tt9.languages.Language; diff --git a/app/src/main/java/io/github/sspanak/tt9/ui/dialogs/ConfirmDictionaryUpdateDialog.java b/app/src/main/java/io/github/sspanak/tt9/ui/dialogs/ConfirmDictionaryUpdateDialog.java index e6f5eb64..71bc22e9 100644 --- a/app/src/main/java/io/github/sspanak/tt9/ui/dialogs/ConfirmDictionaryUpdateDialog.java +++ b/app/src/main/java/io/github/sspanak/tt9/ui/dialogs/ConfirmDictionaryUpdateDialog.java @@ -5,7 +5,7 @@ import android.content.Intent; import androidx.annotation.NonNull; -import io.github.sspanak.tt9.ConsumerCompat; +import io.github.sspanak.tt9.util.ConsumerCompat; import io.github.sspanak.tt9.R; import io.github.sspanak.tt9.db.DictionaryLoader; import io.github.sspanak.tt9.languages.Language; diff --git a/app/src/main/java/io/github/sspanak/tt9/ui/dialogs/PopupDialog.java b/app/src/main/java/io/github/sspanak/tt9/ui/dialogs/PopupDialog.java index 8fad9095..4f8f3569 100644 --- a/app/src/main/java/io/github/sspanak/tt9/ui/dialogs/PopupDialog.java +++ b/app/src/main/java/io/github/sspanak/tt9/ui/dialogs/PopupDialog.java @@ -5,7 +5,7 @@ import android.content.Intent; import androidx.annotation.NonNull; -import io.github.sspanak.tt9.ConsumerCompat; +import io.github.sspanak.tt9.util.ConsumerCompat; import io.github.sspanak.tt9.ui.UI; abstract public class PopupDialog { diff --git a/app/src/main/java/io/github/sspanak/tt9/ui/main/keys/SoftBackspaceKey.java b/app/src/main/java/io/github/sspanak/tt9/ui/main/keys/SoftBackspaceKey.java index 68d11891..449a6df7 100644 --- a/app/src/main/java/io/github/sspanak/tt9/ui/main/keys/SoftBackspaceKey.java +++ b/app/src/main/java/io/github/sspanak/tt9/ui/main/keys/SoftBackspaceKey.java @@ -3,8 +3,8 @@ package io.github.sspanak.tt9.ui.main.keys; import android.content.Context; import android.util.AttributeSet; -import io.github.sspanak.tt9.languages.Characters; -import io.github.sspanak.tt9.languages.Language; +import io.github.sspanak.tt9.languages.LanguageKind; +import io.github.sspanak.tt9.util.Characters; public class SoftBackspaceKey extends SoftKey { @@ -41,7 +41,6 @@ public class SoftBackspaceKey extends SoftKey { return "Del"; } - Language language = getCurrentLanguage(); - return language != null && language.isRTL() ? "⌦" : "⌫"; + return LanguageKind.isRTL(getCurrentLanguage()) ? "⌦" : "⌫"; } } diff --git a/app/src/main/java/io/github/sspanak/tt9/ui/main/keys/SoftKey.java b/app/src/main/java/io/github/sspanak/tt9/ui/main/keys/SoftKey.java index 50b2e83d..26a29c0f 100644 --- a/app/src/main/java/io/github/sspanak/tt9/ui/main/keys/SoftKey.java +++ b/app/src/main/java/io/github/sspanak/tt9/ui/main/keys/SoftKey.java @@ -15,7 +15,7 @@ import android.view.View; import androidx.annotation.Nullable; import androidx.core.content.ContextCompat; -import io.github.sspanak.tt9.Logger; +import io.github.sspanak.tt9.util.Logger; import io.github.sspanak.tt9.R; import io.github.sspanak.tt9.ime.TraditionalT9; import io.github.sspanak.tt9.languages.Language; diff --git a/app/src/main/java/io/github/sspanak/tt9/ui/main/keys/SoftNumberKey.java b/app/src/main/java/io/github/sspanak/tt9/ui/main/keys/SoftNumberKey.java index 09e93342..42847980 100644 --- a/app/src/main/java/io/github/sspanak/tt9/ui/main/keys/SoftNumberKey.java +++ b/app/src/main/java/io/github/sspanak/tt9/ui/main/keys/SoftNumberKey.java @@ -6,12 +6,13 @@ import android.view.KeyEvent; import java.util.ArrayList; -import io.github.sspanak.tt9.Logger; import io.github.sspanak.tt9.R; import io.github.sspanak.tt9.ime.helpers.Key; import io.github.sspanak.tt9.ime.modes.InputMode; import io.github.sspanak.tt9.languages.Language; +import io.github.sspanak.tt9.languages.LanguageKind; import io.github.sspanak.tt9.preferences.SettingsStore; +import io.github.sspanak.tt9.util.Logger; public class SoftNumberKey extends SoftKey { public SoftNumberKey(Context context) { @@ -58,7 +59,7 @@ public class SoftNumberKey extends SoftKey { int number = getNumber(getId()); Language language = getCurrentLanguage(); - if (language != null && language.isArabic() && tt9 != null && !tt9.isInputModeNumeric()) { + if (LanguageKind.isArabic(language) && tt9 != null && !tt9.isInputModeNumeric()) { complexLabelTitleSize = SettingsStore.SOFT_KEY_COMPLEX_LABEL_ARABIC_TITLE_SIZE; return language.getKeyNumber(number); } else { @@ -106,8 +107,8 @@ public class SoftNumberKey extends SoftKey { return ""; } - boolean isLatinBased = language.isLatinBased(); - boolean isGreekBased = language.isGreek(); + boolean isLatinBased = LanguageKind.isLatinBased(language); + boolean isGreekBased = LanguageKind.isGreek(language); StringBuilder sb = new StringBuilder(); ArrayList chars = language.getKeyCharacters(number); diff --git a/app/src/main/java/io/github/sspanak/tt9/ui/main/keys/SoftPunctuationKey.java b/app/src/main/java/io/github/sspanak/tt9/ui/main/keys/SoftPunctuationKey.java index 5de26ba7..1d63ec2c 100644 --- a/app/src/main/java/io/github/sspanak/tt9/ui/main/keys/SoftPunctuationKey.java +++ b/app/src/main/java/io/github/sspanak/tt9/ui/main/keys/SoftPunctuationKey.java @@ -4,7 +4,7 @@ import android.content.Context; import android.util.AttributeSet; import io.github.sspanak.tt9.R; -import io.github.sspanak.tt9.languages.Language; +import io.github.sspanak.tt9.languages.LanguageKind; public class SoftPunctuationKey extends SoftKey { public SoftPunctuationKey(Context context) { @@ -52,8 +52,7 @@ public class SoftPunctuationKey extends SoftKey { } else { if (keyId == R.id.soft_key_punctuation_1) return "!"; if (keyId == R.id.soft_key_punctuation_2) { - Language language = getCurrentLanguage(); - return language != null && language.isArabic() ? "؟" : "?"; + return LanguageKind.isArabic(getCurrentLanguage()) ? "؟" : "?"; } } diff --git a/app/src/main/java/io/github/sspanak/tt9/ui/tray/StatusBar.java b/app/src/main/java/io/github/sspanak/tt9/ui/tray/StatusBar.java index 3069d2e1..d62f4eb9 100644 --- a/app/src/main/java/io/github/sspanak/tt9/ui/tray/StatusBar.java +++ b/app/src/main/java/io/github/sspanak/tt9/ui/tray/StatusBar.java @@ -6,7 +6,7 @@ import android.widget.TextView; import androidx.core.content.ContextCompat; -import io.github.sspanak.tt9.Logger; +import io.github.sspanak.tt9.util.Logger; import io.github.sspanak.tt9.R; public class StatusBar { diff --git a/app/src/main/java/io/github/sspanak/tt9/languages/Characters.java b/app/src/main/java/io/github/sspanak/tt9/util/Characters.java similarity index 98% rename from app/src/main/java/io/github/sspanak/tt9/languages/Characters.java rename to app/src/main/java/io/github/sspanak/tt9/util/Characters.java index 41d10fc3..3ea41419 100644 --- a/app/src/main/java/io/github/sspanak/tt9/languages/Characters.java +++ b/app/src/main/java/io/github/sspanak/tt9/util/Characters.java @@ -1,4 +1,4 @@ -package io.github.sspanak.tt9.languages; +package io.github.sspanak.tt9.util; import android.graphics.Paint; import android.os.Build; diff --git a/app/src/main/java/io/github/sspanak/tt9/ConsumerCompat.java b/app/src/main/java/io/github/sspanak/tt9/util/ConsumerCompat.java similarity index 85% rename from app/src/main/java/io/github/sspanak/tt9/ConsumerCompat.java rename to app/src/main/java/io/github/sspanak/tt9/util/ConsumerCompat.java index f0cf742f..15327732 100644 --- a/app/src/main/java/io/github/sspanak/tt9/ConsumerCompat.java +++ b/app/src/main/java/io/github/sspanak/tt9/util/ConsumerCompat.java @@ -1,4 +1,4 @@ -package io.github.sspanak.tt9; +package io.github.sspanak.tt9.util; /** * ConsumerCompat diff --git a/app/src/main/java/io/github/sspanak/tt9/Logger.java b/app/src/main/java/io/github/sspanak/tt9/util/Logger.java similarity index 92% rename from app/src/main/java/io/github/sspanak/tt9/Logger.java rename to app/src/main/java/io/github/sspanak/tt9/util/Logger.java index 687de0a9..a2924a75 100644 --- a/app/src/main/java/io/github/sspanak/tt9/Logger.java +++ b/app/src/main/java/io/github/sspanak/tt9/util/Logger.java @@ -1,7 +1,9 @@ -package io.github.sspanak.tt9; +package io.github.sspanak.tt9.util; import android.util.Log; +import io.github.sspanak.tt9.BuildConfig; + public class Logger { public static final String TAG_PREFIX = "tt9/"; public static int LEVEL = BuildConfig.DEBUG ? Log.DEBUG : Log.ERROR; diff --git a/app/src/main/java/io/github/sspanak/tt9/languages/Text.java b/app/src/main/java/io/github/sspanak/tt9/util/Text.java similarity index 97% rename from app/src/main/java/io/github/sspanak/tt9/languages/Text.java rename to app/src/main/java/io/github/sspanak/tt9/util/Text.java index ee4d6160..70d14fc2 100644 --- a/app/src/main/java/io/github/sspanak/tt9/languages/Text.java +++ b/app/src/main/java/io/github/sspanak/tt9/util/Text.java @@ -1,9 +1,11 @@ -package io.github.sspanak.tt9.languages; +package io.github.sspanak.tt9.util; import androidx.annotation.NonNull; import java.util.Locale; +import io.github.sspanak.tt9.languages.Language; + public class Text extends TextTools { private final Language language; private final String text; diff --git a/app/src/main/java/io/github/sspanak/tt9/languages/TextTools.java b/app/src/main/java/io/github/sspanak/tt9/util/TextTools.java similarity index 97% rename from app/src/main/java/io/github/sspanak/tt9/languages/TextTools.java rename to app/src/main/java/io/github/sspanak/tt9/util/TextTools.java index a8df7da9..56e7ae36 100644 --- a/app/src/main/java/io/github/sspanak/tt9/languages/TextTools.java +++ b/app/src/main/java/io/github/sspanak/tt9/util/TextTools.java @@ -1,4 +1,4 @@ -package io.github.sspanak.tt9.languages; +package io.github.sspanak.tt9.util; import java.text.SimpleDateFormat; import java.util.Date; diff --git a/app/src/main/java/io/github/sspanak/tt9/Timer.java b/app/src/main/java/io/github/sspanak/tt9/util/Timer.java similarity index 95% rename from app/src/main/java/io/github/sspanak/tt9/Timer.java rename to app/src/main/java/io/github/sspanak/tt9/util/Timer.java index 6c5f192a..deac5fe2 100644 --- a/app/src/main/java/io/github/sspanak/tt9/Timer.java +++ b/app/src/main/java/io/github/sspanak/tt9/util/Timer.java @@ -1,4 +1,4 @@ -package io.github.sspanak.tt9; +package io.github.sspanak.tt9.util; import java.util.HashMap;