1
0
Fork 0

YAML language definitions (#292)

This commit is contained in:
Dimo Karaivanov 2023-06-26 15:14:22 +03:00 committed by GitHub
parent 241a4125b0
commit 6756de4466
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
89 changed files with 689 additions and 522 deletions

View file

@ -25,7 +25,7 @@ jobs:
# validation
- name: Validate Dictionaries
run: ./gradlew validateDictionaries
run: ./gradlew validateLanguages
- name: Lint
run: ./gradlew lint
- name: Build Release APK

View file

@ -48,17 +48,15 @@ Make sure you have a signing key. If you don't have one, follow the [official ma
## Adding a New Language
To support a new language one needs to:
- Find a suitable dictionary and add it to the `assets/` folder. Two file formats are supported, [see below](#dictionary-formats).
- Find a suitable dictionary and add it to the `assets/languages/dictionaries/` folder. Two file formats are supported, [see below](#dictionary-formats).
- Do not forget to include the dictionary license (or readme) file in the `docs/` folder.
- Create a new language class in `languages/definitions/` and define its properties.
- Create a new `.yml` file in `assets/languages/definitions/` and define the language properties.
- `locale` contains the language and the country codes (e.g. "en-US", "es-AR", "it-IT"). Refer to the list of [supported locales in Java](https://www.oracle.com/java/technologies/javase/jdk8-jre8-suported-locales.html#util-text).
- `dictionaryFile` is the name of the dictionary in `assets/` folder.
- `characterMap` contains the letters and punctuation marks associated with each key.
- `dictionaryFile` is the name of the dictionary in `assets/languages/dictionaries/` folder.
- `layout` contains the letters and punctuation marks associated with each key. For 0-key and 1-key using `[DEFAULT]`, will be fine for most languages. However, if the language has extra punctuation marks, like Spanish, you could complement the list: `[DEFAULT, ¡, ¿]`
- `abcString` _(optional)_. A custom string to display in ABC mode. By default, the first three letters on 2-key are used (e.g. "ABC" or "АБВ"). Set this if the first letters of the alphabet are _not_ on 2-key, like in Hebrew, or if a different string makes more sense.
- `hasUpperCase` _(optional)_ set to `false` when the language has no upper- and lowercase letters. For example: Arabic, Hebrew, East Asian languages, and so on. The default is `true`.
- `hasUpperCase` _(optional)_ set to `no` when the language has no upper- and lowercase letters. For example: Arabic, Hebrew, East Asian languages, and so on. The default is `yes`.
- `name` _(optional)_ is automatically generated and equals the native name of the language (e.g. "English", "Deutsch", "Українська"). However, sometimes, the automatically selected name may be ambiguous. For example, both Portuguese in Portugal and Brazil will default to "Português", so assigning "Português brasileiro" would make it clear it's the language used in Brazil.
- Finally, add the new language to the list in `LanguageCollection.java`. You only need to add it in one place, in the constructor. Please, be nice and maintain the alphabetical order.
### Dictionary Formats

View file

@ -1,5 +1,5 @@
# Traditional T9
TT9 is an IME (Input Method Editor) for Android devices with a hardware keypad. It supports [multiple languages](src/io/github/sspanak/tt9/languages/definitions) and predictive text typing.
TT9 is an IME (Input Method Editor) for Android devices with a hardware keypad. It supports [multiple languages](assets/languages/definitions) and predictive text typing.
This is an updated version of the [original project](https://github.com/Clam-/TraditionalT9) by Clam-.
@ -36,7 +36,7 @@ If you like Traditional T9, buy me a beer. Donations are currently accepted on [
## License
- The source code, the logo image and the icons are licensed under the conditions described in [LICENSE.txt](LICENSE.txt).
- The dictionaries are licensed under the licenses provided in the [respective readme files](docs/dictionaries/), where applicable. Detailed information about the dictionaries is also available there.
- The dictionaries are licensed under the licenses provided in the [respective readme files](docs/dictionaries), where applicable. Detailed information about the dictionaries is also available there.
- [Silver foil photo created by rawpixel.com - www.freepik.com](https://www.freepik.com/photos/silver-foil)
- "Negotiate" and "Vibrocentric" fonts are under [The Fontspring Desktop/Ebook Font End User License](docs/desktop-ebook-EULA-1.8.txt).

View file

@ -0,0 +1,15 @@
---
locale: pt-BR
dictionaryFile: pt-BR-utf8.csv
name: Português brasileiro
layout:
- [DEFAULT] # 0
- [DEFAULT] # 1
- [a, b, c, ç, á, â, ã, à] # 2
- [d, e, f, é, ê, è] # 3
- [g, h, i, í] # 4
- [j, k, l] # 5
- [m, n, o, ó, ô, õ] # 6
- [p, q, r, s] # 7
- [t, u, v, ú] # 8
- [w, x, y, z] # 9

View file

@ -0,0 +1,14 @@
---
locale: bg-BG
dictionaryFile: bg-utf8.csv
layout:
- [DEFAULT] # 0
- [DEFAULT] # 1
- [а, б, в, г] # 2
- [д, е, ж, з] # 3
- [и, й, к, л, ѝ] # 4
- [м, н, о, п] # 5
- [р, с, т, у] # 6
- [ф, х, ц, ч] # 7
- [ш, щ, ъ] # 8
- [ь, ю, я] # 9

View file

@ -0,0 +1,14 @@
---
locale: nl-NL
dictionaryFile: nl-utf8.csv
layout:
- [DEFAULT] # 0
- [DEFAULT] # 1
- [a, b, c, à, ä, ç] # 2
- [d, e, f, é, è, ê, ë] # 3
- [g, h, i, î, ï] # 4
- [j, k, l] # 5
- [m, n, o, ö] # 6
- [p, q, r, s] # 7
- [t, u, v, û, ü] # 8
- [w, x, y, z] # 9

View file

@ -0,0 +1,14 @@
---
locale: en
dictionaryFile: en-utf8.csv
layout:
- [DEFAULT] # 0
- [DEFAULT] # 1
- [a, b, c] # 2
- [d, e, f] # 3
- [g, h, i] # 4
- [j, k, l] # 5
- [m, n, o] # 6
- [p, q, r, s] # 7
- [t, u, v] # 8
- [w, x, y, z] # 9

View file

@ -0,0 +1,14 @@
---
locale: fi-FI
dictionaryFile: fi-utf8.csv
layout:
- [DEFAULT] # 0
- [DEFAULT] # 1
- [a, b, c, ä, å] # 2
- [d, e, f] # 3
- [g, h, i] # 4
- [j, k, l] # 5
- [m, n, o, ö] # 6
- [p, q, r, s] # 7
- [t, u, v] # 8
- [w, x, y, z] # 9

View file

@ -0,0 +1,14 @@
---
locale: fr
dictionaryFile: fr-utf8.csv
layout:
- [DEFAULT] # 0
- [DEFAULT] # 1
- [a, b, c, à, â, æ, ç] # 2
- [d, e, f, é, è, ê, ë] # 3
- [g, h, i, î, ï] # 4
- [j, k, l] # 5
- [m, n, o, ô, œ] # 6
- [p, q, r, s] # 7
- [t, u, v, ù, û, ü] # 8
- [w, x, y, z, ÿ] # 9

View file

@ -0,0 +1,14 @@
---
locale: de
dictionaryFile: de-utf8.csv
layout:
- [DEFAULT] # 0
- [DEFAULT] # 1
- [a, b, c, ä] # 2
- [d, e, f] # 3
- [g, h, i] # 4
- [j, k, l] # 5
- [m, n, o, ö] # 6
- [p, q, r, s, ß] # 7
- [t, u, v, ü] # 8
- [w, x, y, z] # 9

View file

@ -0,0 +1,16 @@
---
locale: iw-IL
dictionaryFile: he-utf8.csv
abcString: אבג
hasUpperCase: no
layout:
- [DEFAULT] # 0
- [DEFAULT] # 1
- [ד, ה, ו] # 2
- [א, ב, ג] # 3
- [מ, ם, נ, ן] # 4
- [י, כ, ך, ל] # 5
- [ז, ח, ט] # 6
- [ר, ש, ת] # 7
- [צ, ץ, ק] # 8
- [ס, ע, פ, ף] # 9

View file

@ -0,0 +1,15 @@
---
locale: in-ID
dictionaryFile: id-utf8.csv
name: Bahasa Indonesia
layout:
- [DEFAULT] # 0
- [DEFAULT] # 1
- [a, b, c] # 2
- [d, e, f] # 3
- [g, h, i] # 4
- [j, k, l] # 5
- [m, n, o] # 6
- [p, q, r, s] # 7
- [t, u, v] # 8
- [w, x, y, z] # 9

View file

@ -0,0 +1,14 @@
---
locale: it
dictionaryFile: it-utf8.csv
layout:
- [DEFAULT] # 0
- [DEFAULT] # 1
- [a, b, c, à] # 2
- [d, e, f, é, è] # 3
- [g, h, i, ì, í, î] # 4
- [j, k, l] # 5
- [m, n, o, ò, ó] # 6
- [p, q, r, s] # 7
- [t, u, v, ù, ú] # 8
- [w, x, y, z] # 9

View file

@ -0,0 +1,14 @@
---
locale: nb-NO
dictionaryFile: nb-utf8.csv
layout:
- [DEFAULT] # 0
- [DEFAULT] # 1
- [a, b, c, æ, å] # 2
- [d, e, f, é, è] # 3
- [g, h, i] # 4
- [j, k, l] # 5
- [m, n, o, ø, ó, ò, ô] # 6
- [p, q, r, s] # 7
- [t, u, v, ü] # 8
- [w, x, y, z] # 9

View file

@ -0,0 +1,14 @@
---
locale: pl-PL
dictionaryFile: pl-utf8.csv
layout:
- [DEFAULT] # 0
- [DEFAULT] # 1
- [a, b, c, ą, ć] # 2
- [d, e, f, ę] # 3
- [g, h, i] # 4
- [j, k, l, ł] # 5
- [m, n, o, ó, ń] # 6
- [p, q, r, s, ś] # 7
- [t, u, v] # 8
- [w, x, y, z, ź, ż] # 9

View file

@ -0,0 +1,14 @@
---
locale: ru-RU
dictionaryFile: ru-utf8.csv
layout:
- [DEFAULT] # 0
- [DEFAULT] # 1
- [а, б, в, г] # 2
- [д, е, ё, ж, з] # 3
- [и, й, к, л] # 4
- [м, н, о, п] # 5
- [р, с, т, у] # 6
- [ф, х, ц, ч] # 7
- [ш, щ, ъ, ы] # 8
- [ь, э, ю, я] # 9

View file

@ -0,0 +1,14 @@
---
locale: es-ES
dictionaryFile: es-utf8.csv
layout:
- [DEFAULT] # 0
- [DEFAULT, ¡, ¿] # 1
- [a, b, c, á] # 2
- [d, e, f, é] # 3
- [g, h, i, í] # 4
- [j, k, l] # 5
- [m, n, ñ, o, ó] # 6
- [p, q, r, s] # 7
- [t, u, v, ú, ü] # 8
- [w, x, y, z] # 9

View file

@ -0,0 +1,14 @@
---
locale: sv-SE
dictionaryFile: sv-utf8.csv
layout:
- [DEFAULT] # 0
- [DEFAULT] # 1
- [a, b, c, å, ä] # 2
- [d, e, f, é] # 3
- [g, h, i] # 4
- [j, k, l] # 5
- [m, n, o, ö] # 6
- [p, q, r, s] # 7
- [t, u, v] # 8
- [w, x, y, z] # 9

View file

@ -0,0 +1,14 @@
---
locale: uk-UA
dictionaryFile: uk-utf8.csv
layout:
- [DEFAULT] # 0
- [DEFAULT] # 1
- [а, б, в, г, ґ] # 2
- [д, е, є, ж, з] # 3
- [и, і, ї, й, к, л] # 4
- [м, н, о, п] # 5
- [р, с, т, у] # 6
- [ф, х, ц, ч] # 7
- [ш, щ] # 8
- [ь, ю, я] # 9

View file

@ -0,0 +1,16 @@
---
locale: ji-JI
dictionaryFile: ji-utf8.csv
abcString: אבג
hasUpperCase: no
layout:
- [DEFAULT] # 0
- [DEFAULT] # 1
- [ד, ה, ו] # 2
- [א, ב, ג] # 3
- [מ, ם, נ, ן] # 4
- [י, כ, ך, ל] # 5
- [ז, ח, ט] # 6
- [ר, ש, ת] # 7
- [צ, ץ, ק] # 8
- [ס, ע, פ, ף] # 9

View file

Can't render this file because it is too large.

View file

Can't render this file because it is too large.

View file

Can't render this file because it is too large.

View file

Can't render this file because it is too large.

View file

Can't render this file because it is too large.

View file

Can't render this file because it is too large.

View file

Can't render this file because it is too large.

View file

Can't render this file because it is too large.

View file

Can't render this file because it is too large.

View file

Can't render this file because it is too large.

View file

Can't render this file because it is too large.

View file

Can't render this file because it is too large.

View file

Can't render this file because it is too large.

View file

Can't render this file because it is too large.

View file

Can't render this file because it is too large.

View file

Can't render this file because it is too large.

View file

Can't render this file because it is too large.

View file

@ -2,17 +2,26 @@ buildscript {
repositories {
mavenCentral()
google()
maven {
url "https://plugins.gradle.org/m2/"
}
}
dependencies {
classpath 'com.android.tools.build:gradle:8.0.2'
classpath 'gradle.plugin.at.zierler:yaml-validator-plugin:1.5.0'
}
}
apply plugin: 'com.android.application'
apply plugin: 'at.zierler.yamlvalidator'
configurations.all {
// fixes 'duplicate class error', when using these combine: androidx.core:1.10.1, androidx.preference:1.2.0 and androidx.room:2.5.1
// see: https://stackoverflow.com/questions/75274720/a-failure-occurred-while-executing-appcheckdebugduplicateclasses/75315276#75315276
exclude group: 'org.jetbrains.kotlin', module: 'kotlin-stdlib-jdk8'
yamlValidator {
searchPaths = ['assets/languages/definitions']
}
}
dependencies {
@ -20,11 +29,15 @@ dependencies {
implementation 'androidx.preference:preference:1.2.0'
implementation 'androidx.room:room-runtime:2.5.1'
annotationProcessor 'androidx.room:room-compiler:2.5.1'
implementation 'org.yaml:snakeyaml:2.0'
}
repositories {
mavenCentral()
google()
maven {
url "https://plugins.gradle.org/m2/"
}
}
def execThing ( String cmdStr ) {
@ -86,9 +99,13 @@ def getReleaseVersion = { ->
return "${getVersionName()} (${getCurrentGitHash()})"
}
task validateDictionaries {
inputs.dir fileTree(dir:'assets', excludes:['dict.properties'])
outputs.file "${project.buildDir}/dict.validation.txt"
task validateLanguages {
final baseDir = "${project.rootDir}/assets/languages"
final definitionsDir = "${baseDir}/definitions"
final dictionariesDir = "${baseDir}/dictionaries"
inputs.dir fileTree(dir:baseDir, excludes:['dict.properties'])
outputs.file "${project.buildDir}/lang.validation.txt"
doLast {
final String CSV_DELIMITER = ' ' // TAB
@ -100,19 +117,86 @@ task validateDictionaries {
outputs.files.singleFile.text = ""
inputs.getFiles().each {File file ->
fileTree(definitionsDir).getFiles().each { File languageFile ->
if (errorCount >= MAX_ERRORS) {
return
}
println "Validating dictionary: " + file.name
println "Validating language: ${languageFile.name}"
def uniqueWords = [:]
int lineNumber = 0
boolean isFileValid = true
file.eachLine {line ->
boolean hasLayout = false
boolean isLocaleValid = false
def localeString = ''
def dictionaryFileName = ''
languageFile.eachLine { line ->
if (
line.matches("^[a-zA-Z].*")
&& !line.startsWith("abcString")
&& !line.startsWith("dictionaryFile")
&& !line.startsWith("hasUpperCase")
&& !line.startsWith("layout")
&& !line.startsWith("locale")
&& !line.startsWith("name")
) {
isFileValid = false
def parts = line.split(":")
def property = parts.length > 0 ? parts[0] : line
errorCount++
errors += "Language '${languageFile.name}' is invalid. Found unknown property: '${property}'.\n"
}
if (line.startsWith("hasUpperCase") && !line.endsWith("yes") && !line.endsWith("no")) {
def invalidVal = line.replace("hasUpperCase:", "").trim()
isFileValid = false
errorCount++
errors += "Language '${languageFile.name}' is invalid. Unrecognized 'hasUpperCase' value: '${invalidVal}'. Only 'yes' and 'no' are allowed.\n"
}
if (line.startsWith("layout")) {
hasLayout = true
}
if (line.startsWith("locale")) {
localeString = line.replace("locale:", "").trim()
isLocaleValid = line.matches("^locale:\\s*[a-z]{2}(?:-[A-Z]{2})?")
}
if (line.startsWith("dictionaryFile")) {
dictionaryFileName = line.replace("dictionaryFile:", "").trim()
}
}
if (!hasLayout) {
isFileValid = false
errorCount++
errors += "Language '${languageFile.name}' is invalid. Missing 'layout' property.\n"
}
if (!isLocaleValid) {
isFileValid = false
errorCount++
def msg = localeString.isEmpty() ? "Missing 'locale' property." : "Unrecognized locale format: '${localeString}'"
errors += "Language '${languageFile.name}' is invalid. ${msg}\n"
}
def dictionaryFile = new File("$dictionariesDir/${dictionaryFileName}")
if (dictionaryFileName.isEmpty() || !dictionaryFile.exists()) {
errorCount++
errors += "Could not find dictionary file: '${dictionaryFileName}' in: '${dictionariesDir}'. Make sure 'dictionaryFile' is set correctly in: '${languageFile.name}'.\n"
outputs.files.singleFile.text += "${languageFile.name} INVALID \n"
return
}
def uniqueWords = [:]
int lineNumber = 0
dictionaryFile.eachLine {line ->
if (errorCount >= MAX_ERRORS) {
return
}
@ -122,14 +206,14 @@ task validateDictionaries {
if (line == "") {
isFileValid = false
errorCount++
errors += "Dictionary '" + file.name + "' is invalid. There is no word on line " + lineNumber + ". Remove all empty lines.\n"
errors += "Dictionary '${dictionaryFile.name}' is invalid. There is no word on line ${lineNumber}. Remove all empty lines.\n"
return
}
if (line.contains(" ")) {
isFileValid = false
errorCount++
errors += "Dictionary '" + file.name + "' is invalid. Found space on line " + lineNumber + ". Make sure each word is on a new line. Phrases are not allowed.\n"
errors += "Dictionary '${dictionaryFile.name}' is invalid. Found space on line ${lineNumber}. Make sure each word is on a new line. Phrases are not allowed.\n"
return
}
@ -140,32 +224,32 @@ task validateDictionaries {
if (frequency.length() > 0 && !frequency.matches("^\\d+\$")) {
isFileValid = false
errorCount++
errors += "Dictionary '" + file.name + "' is invalid. Found out-of-range word frequency: '" + frequency + "' on line " + lineNumber + ". Frequency must be a non-negative integer.\n"
errors += "Dictionary '${dictionaryFile.name}' is invalid. Found out-of-range word frequency: '${frequency}' on line ${lineNumber}. Frequency must be a non-negative integer.\n"
}
if (word.matches("(\\d.+?|.+?\\d|\\d)")) {
isFileValid = false
errorCount++
errors += "Dictionary '" + file.name + "' is invalid. Found numbers on line " + lineNumber + ". Remove all numbers.\n"
errors += "Dictionary '${dictionaryFile.name}' is invalid. Found numbers on line ${lineNumber}. Remove all numbers.\n"
}
if (word.matches("^\\P{L}+\$")) {
isFileValid = false
errorCount++
errors += "Dictionary '" + file.name + "' is invalid. Found a garbage word: '" + word + "' on line " + lineNumber + ".\n"
errors += "Dictionary '${dictionaryFile.name}' is invalid. Found a garbage word: '${word}' on line ${lineNumber}.\n"
}
if (word.matches("^.\$") && !Character.isUpperCase(word.charAt(0))) {
isFileValid = false
errorCount++
errors += "Dictionary '" + file.name + "' is invalid. Found a single letter: '" + word + "' on line " + lineNumber + ". Only uppercase single letters are allowed. The rest of the alphabet will be added automatically.\n"
errors += "Dictionary '${dictionaryFile.name}' is invalid. Found a single letter: '${word}' on line ${lineNumber}. Only uppercase single letters are allowed. The rest of the alphabet will be added automatically.\n"
}
String uniqueWordKey = word ==~ GEOGRAPHICAL_NAME ? word : word.toLowerCase()
if (uniqueWords[uniqueWordKey] != null && uniqueWords[uniqueWordKey] == true) {
isFileValid = false
errorCount++
errors += "Dictionary '" + file.name + "' is invalid. Found a repeating word: '" + word + "' on line " + lineNumber + ". Ensure all words appear only once.\n"
errors += "Dictionary '${dictionaryFile.name}' is invalid. Found a repeating word: '${word}' on line ${lineNumber}. Ensure all words appear only once.\n"
} else {
uniqueWords[uniqueWordKey] = true
}
@ -175,7 +259,7 @@ task validateDictionaries {
}
}
outputs.files.singleFile.text += file.name + " " + (isFileValid ? "OK" : "INVALID") + "\n"
outputs.files.singleFile.text += "${languageFile.name} ${isFileValid ? 'OK' : 'INVALID'}\n"
}
if (errors != "") {
@ -239,7 +323,10 @@ android {
aidl true
}
applicationVariants.all { variant ->
tasks["merge${variant.name.capitalize()}Assets"].dependsOn(validateDictionaries)
tasks["merge${variant.name.capitalize()}Assets"]
.dependsOn(validateYaml)
.dependsOn(validateLanguages)
}
}

View file

@ -19,4 +19,16 @@
# public *;
#}
-keep class io.github.sspanak.tt9.languages.definitions.** { *; }
# LanguageDefinition properties must be preserved so that SnakeYAML can map them
# to the language YAMLs.
-keepclassmembers class io.github.sspanak.tt9.languages.LanguageDefinition {
public *;
}
# SnakeYAML shall not complain. As resolved by Mozilla:
# https://github.com/mozilla-mobile/focus-android/blob/main/app/proguard-rules.pro
-dontwarn java.beans.BeanInfo
-dontwarn java.beans.FeatureDescriptor
-dontwarn java.beans.IntrospectionException
-dontwarn java.beans.Introspector
-dontwarn java.beans.PropertyDescriptor

View file

@ -2,19 +2,18 @@
<resources>
<string name="app_settings">Настройки на TT9</string>
<string name="completed">Завършено</string>
<string name="no_language">Няма език</string>
<string name="error_unexpected">Възникна неочаквана грешка.</string>
<string name="failed_loading_language_definitions">Не може да се заредят езиковите дефиниции.</string>
<string name="add_word_add">Добави</string>
<string name="add_word_blank">Не може да се въведе празна дума.</string>
<string name="add_word_exist">Думата „%1$s“ е вече речника.</string>
<string name="add_word_title">Добавяне на дума</string>
<string name="pref_category_about">За приложението</string>
<string name="pref_help">Помощ</string>
<string name="pref_dark_theme">Тъмен облик</string>
<string name="pref_choose_languages">Езици</string>
<string name="dictionary_truncate_title">Изтрий всички</string>
<string name="dictionary_cancel_load">Отмени зареждането</string>
<string name="dictionary_load_bad_char">Неуспешно зареждане. Невалидна дума „%1$s“ на ред %2$d за език „%3$s“.</string>
<string name="dictionary_load_error">Несупешно зареждане на речник за език „%1$s“ (%2$s).</string>
@ -69,4 +68,5 @@
<string name="key_volume_down">Намаляне на звук</string>
<string name="key_volume_up">Усилване на звук</string>
<string name="dictionary_truncating">Изтриване…</string>
<string name="error">Грешка</string>
</resources>

View file

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_settings">TT9 Einstellungen</string>
<string name="no_language">Keine Sprache</string>
<string name="error_unexpected">Unerwarteter Fehler aufgetreten.</string>
<string name="add_word_add">Hinzufügen</string>
@ -21,4 +22,5 @@
<string name="char_space">Leerzeichen</string>
<string name="char_newline">Neue Zeile</string>
<string name="pref_category_setup">Ersteinrichtung</string>
<string name="error">Fehler</string>
</resources>

View file

@ -5,6 +5,7 @@
<string name="add_word_blank">Palabra en blanco no agregada.</string>
<string name="pref_help">Ayuda</string>
<string name="completed">Acabado</string>
<string name="no_language">Sin idioma</string>
<string name="error_unexpected">Ocurrió un error inesperado.</string>
<string name="add_word_exist">La palabra \"%1$s\" ya esta en el diccionario.</string>
<string name="add_word_title">Agregar palabra</string>
@ -35,4 +36,5 @@
<string name="pref_upside_down_keys_summary">Habilite la configuración si hay 789 en la primera fila, en lugar de 123.</string>
<string name="pref_category_setup">Configuración inicial</string>
<string name="pref_show_soft_numpad">Teclado numérico en pantalla</string>
<string name="error">Error</string>
</resources>

View file

@ -2,19 +2,18 @@
<resources>
<string name="app_settings">Paramètres de TT9</string>
<string name="completed">Fini</string>
<string name="no_language">Aucun langue</string>
<string name="error_unexpected">Une erreur inattendue s\'est produite.</string>
<string name="failed_loading_language_definitions">Impossible de charger aucune définition de langue</string>
<string name="add_word_add">Ajouter</string>
<string name="add_word_blank">Mot vide non ajouté.</string>
<string name="add_word_exist">Le mot «%1$s» est déjà dans le dictionnaire.</string>
<string name="add_word_title">Ajouter un mot</string>
<string name="pref_category_about">À propos de l\'application</string>
<string name="pref_help">Aide</string>
<string name="pref_dark_theme">Thème sombre</string>
<string name="pref_choose_languages">Langues</string>
<string name="dictionary_truncate_title">Supprimer tous</string>
<string name="dictionary_cancel_load">Annuler le chargement</string>
<string name="dictionary_load_error">Echec du chargement de dictionnaire pour langue «%1$s» (%2$s).</string>
<string name="dictionary_loaded">Chargement du dictionnaire terminé.</string>
@ -64,4 +63,5 @@
<string name="pref_category_setup">Configuration initiale</string>
<string name="pref_show_soft_numpad">Pavé numérique à l\'écran</string>
<string name="dictionary_truncating">Suppression…</string>
<string name="error">Erreur</string>
</resources>

View file

@ -2,6 +2,7 @@
<resources>
<string name="app_settings">TT9 Impostazioni</string>
<string name="completed">Completato</string>
<string name="no_language">Nessuna lingua</string>
<string name="error_unexpected">Si è verificato un errore imprevisto.</string>
<string name="add_word_add">Aggiungere</string>
@ -30,5 +31,6 @@
<string name="pref_upside_down_keys">Invertire l\'ordine delle chiavi</string>
<string name="pref_upside_down_keys_summary">Abilita l\'impostazione se ci sono 789 sulla prima riga, invece di 123.</string>
<string name="pref_category_setup">Configurazione iniziale</string>
<string name="error">Errore</string>
</resources>

View file

@ -2,6 +2,7 @@
<resources>
<string name="app_settings">TT9 הגדרות</string>
<string name="completed">הסתיים</string>
<string name="no_language">אין שפה</string>
<string name="error_unexpected">אירעה שגיאה לא צפויה.</string>
<string name="add_word_add">הוסף</string>
@ -53,4 +54,5 @@
<string name="char_newline">שורה חדשה</string>
<string name="char_space">רווח</string>
<string name="error">טעות</string>
</resources>

View file

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_settings">TT9 Opties</string>
<string name="no_language">Geen taal</string>
<string name="error_unexpected">Er is een onverwachte fout opgetreden.</string>
<string name="add_word_add">Toevoegen</string>
@ -21,4 +22,5 @@
<string name="char_newline">Nieuwe regel</string>
<string name="pref_category_setup">Initiële setup</string>
<string name="dictionary_truncating">Verwijderen…</string>
<string name="error">Fout</string>
</resources>

View file

@ -2,6 +2,7 @@
<resources>
<string name="app_settings">Configurações do Teclado</string>
<string name="completed">Concluído</string>
<string name="no_language">Sem idioma</string>
<string name="error_unexpected">Um erro inesperado aconteceu.</string>
<string name="add_word_add">Adicionar</string>
@ -56,4 +57,5 @@
<string name="pref_upside_down_keys_summary">Utilize essa opção se você possuir as teclas 789 na linha de cima, ao invés de 123.</string>
<string name="pref_upside_down_keys">Inverter Ordem das Teclas</string>
<string name="dictionary_truncate_unselected">Limpar Não Selecionados</string>
<string name="error">Erro</string>
</resources>

View file

@ -2,19 +2,18 @@
<resources>
<string name="app_settings">TT9 настройки</string>
<string name="completed">Выполнено</string>
<string name="no_language">Нет языка</string>
<string name="error_unexpected">Произошла непредвиденная ошибка.</string>
<string name="failed_loading_language_definitions">Не удалось загрузить какое-либо определение языка.</string>
<string name="add_word_add">Добавить</string>
<string name="add_word_blank">Невозможно добавить слово.</string>
<string name="add_word_exist">Слово «%1$s» уже есть в словаре.</string>
<string name="add_word_title">Добавить слово</string>
<string name="pref_category_about">О приложении</string>
<string name="pref_help">Помощь</string>
<string name="pref_dark_theme">Темная тема</string>
<string name="pref_choose_languages">Языки</string>
<string name="dictionary_truncate_title">Удалить все</string>
<string name="dictionary_cancel_load">Отменить загрузку</string>
<string name="dictionary_load_error">Ошибка загрузки словаря для языка «%1$s» (%2$s).</string>
<string name="dictionary_loaded">Загрузка словаря завершена.</string>
@ -69,4 +68,5 @@
<string name="key_volume_down">Уменьшить громкости</string>
<string name="key_volume_up">Увеличить громкости</string>
<string name="dictionary_truncating">Удаление…</string>
<string name="error">Ошибка</string>
</resources>

View file

@ -2,19 +2,18 @@
<resources>
<string name="app_settings">Налаштування TT9</string>
<string name="completed">Зроблено</string>
<string name="no_language">Немає мови</string>
<string name="error_unexpected">Сталася неочікувана помилка.</string>
<string name="failed_loading_language_definitions">Не вдалося завантажити всі визначення мови.</string>
<string name="add_word_add">Додати</string>
<string name="add_word_blank">Неможливо додати слово.</string>
<string name="add_word_exist">Слово «%1$s» вже є в словнику.</string>
<string name="add_word_title">Додати слово</string>
<string name="pref_category_about">Про додаток</string>
<string name="pref_help">Допомога</string>
<string name="pref_dark_theme">Темна тема</string>
<string name="pref_choose_languages">Мови</string>
<string name="dictionary_truncate_title">Видалити усі</string>
<string name="dictionary_cancel_load">Скасувати завантаження</string>
<string name="dictionary_load_error">Помилка завантаження словника для мови «%1$s» (%2$s).</string>
<string name="dictionary_loaded">Завантаження словника завершено.</string>
@ -69,4 +68,5 @@
<string name="key_volume_up">Збільшення гучності</string>
<string name="key_volume_down">Зменшення гучності</string>
<string name="dictionary_truncating">Видалення…</string>
<string name="error">Помилка</string>
</resources>

View file

@ -5,7 +5,11 @@
<string name="app_name_short" translatable="false">TT9</string>
<string name="app_settings">TT9 Settings</string>
<string name="completed">Completed</string>
<string name="error">Error</string>
<string name="no_language">No Language</string>
<string name="error_unexpected">Unexpected error occurred.</string>
<string name="failed_loading_language_definitions">Failed loading all language definitions.</string>
<string name="add_word_add">Add</string>
<string name="add_word_blank">Blank word not added.</string>

View file

@ -1,25 +1,41 @@
package io.github.sspanak.tt9.db.migrations;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.room.migration.Migration;
import androidx.sqlite.db.SupportSQLiteDatabase;
import java.util.Locale;
import io.github.sspanak.tt9.Logger;
import io.github.sspanak.tt9.languages.definitions.Dutch;
import io.github.sspanak.tt9.languages.definitions.English;
import io.github.sspanak.tt9.languages.Language;
import io.github.sspanak.tt9.languages.LanguageCollection;
public class DB11 {
public static final Migration MIGRATION = new Migration(10, 11) {
private Context ctx;
public Migration getMigration(Context context) {
ctx = context;
return MIGRATION;
}
private final Migration MIGRATION = new Migration(10, 11) {
@Override
public void migrate(@NonNull SupportSQLiteDatabase database) {
final String enWords = "'I''d','I''m','d''annunzio','I''ll','I''ve','prud''hon','an''t','bo''s''n','bo''s''ns','bo''sun','bo''suns','bos''n','bos''ns','br''er','ca''canny','could''ve','d''arezzo','d''estaing','e''en','e''er','fo''c''s''le','fo''c''s''les','fo''c''sle','fo''c''sles','ha''penny','he''d','he''ll','how''d','how''re','howe''er','it''d','it''ll','might''ve','must''ve','n''importe','ne''er','nor''easter','nor''wester','o''er','rec''d','sec''y','she''d','she''ll','should''ve','sou''wester','ta''en','that''d','that''ll','they''d','they''ll','they''re','they''ve','we''d','we''ll','we''re','we''ve','whate''er','whatsoe''er','whene''er','where''er','who''d','who''ll','who''re','who''ve','why''d','would''ve','you''d','you''ll','you''re','you''ve','Ch''in','L''Amour','L''Enfant','L''Oreal','L''Ouverture','T''ang','Xi''an'";
final String nlWords = "'''s-Graveland','''s-Gravendeel','''s-Gravenhaags','''s-Gravenhage','''s-Gravenhagenaar','''s-Gravenmoer','''s-Gravenzande','''s-Gravenzander','''s-Gravenzands','''s-Hertogenbosch','''t','A.D.','az.','chin.','d.v.','h.k.h.','h.m.','l.b.','mgr.','n.b.','n.h.','n.n.','n.o.','n.v.','n.w.','ned.','o.l.v.','openoffice.org','r.i.p.','st.-eustatius','st.-maarten','stct.','w.','w.v.str.','z.h.','z.k.h.','a.d.h.v.','a.g.v.','a.h.w.','a.j.b.','a.m.','a.s.','a.u.b.','aanw.','afb.','afd.','afz.','an.','arr.','b.d.','b.g.g.','b.v.d.','bc.','bett.','bijl.','bijv.','blz.','bv.','bw.','c.q.','c.s.','ca.','d.d.','d.i.','d.m.v.','d.w.z.','dd.','dhr.','div.','dr.','dra.','drs.','drs.-titel','ds.','e.a.','e.d.','e.e.a.','e.o.','e.v.','e.v.a.','enz.','etc.','evt.','excl.','fa.','fam.','fig.','fr.','g.g.d.','geb.','gem.','get.','i.c.','i.c.m.','i.e.','i.h.a.','i.h.b.','i.m.','i.o.','i.o.v.','i.p.v.','i.s.m.','i.t.t.','i.v.m.','i.z.g.st.','incl.','ing.','ir.','jhr.','jkvr.','jl.','jr.','k.k.','lic.','m.','m.a.w.','m.b.t.','m.b.v.','m.i.','m.i.v.','m.m.','m.m.v.','m.n.','m.u.v.','max.','mevr.','min.','mld.','mln.','mr.','mw.','n.a.v.','n.o.t.k.','n.v.t.','nl.','nl.openoffice.org','no.','nr.','nrs.','o.a.','o.b.v.','o.i.','o.i.d.','o.m.','o.t.t.','o.v.t.','o.v.v.','ong.','p.','p.a.','p.m.','p.o.','p.p.','p.w.','pag.','plm.','plv.','prof.','q.e.d.','q.q.','r.-k.','red.','resp.','s.j.','s.v.p.','sr.','t.a.v.','t.b.v.','t.g.v.','t.h.t.','t.h.v.','t.n.v.','t.o.v.','t.w.','t.w.v.','t.z.t.','v.','v.chr.','v.d.','v.h.','v.l.n.r.','v.r.n.l.','v.t.t.','v.v.','v.v.t.','v.w.b.','verg.','vgl.','vnl.','vnw.','voorz.','vs.','w.o.','w.v.t.t.k.','ww.','z.g.','z.g.a.n.','z.i.','z.o.z.','z.s.m.','zgn.'";
try {
Language English = LanguageCollection.getByLocale(ctx, Locale.ENGLISH.toString());
Language Dutch = LanguageCollection.getByLocale(ctx, "nl_NL");
assert English != null;
assert Dutch != null;
database.beginTransaction();
database.execSQL(getDeleteEnglishSwordsQuery());
database.execSQL(getDeleteWordsQuery(new English().getId(), enWords));
database.execSQL(getDeleteWordsQuery(new Dutch().getId(), nlWords));
database.execSQL(getDeleteEnglishSwordsQuery(English));
database.execSQL(getDeleteWordsQuery(English.getId(), enWords));
database.execSQL(getDeleteWordsQuery(Dutch.getId(), nlWords));
database.setTransactionSuccessful();
} catch (Exception e) {
Logger.e("Migrate to DB11", "Migration failed. " + e.getMessage());
@ -29,11 +45,11 @@ public class DB11 {
}
};
private static String getDeleteEnglishSwordsQuery() {
return "DELETE FROM words WHERE lang=" + new English().getId() + " AND word LIKE '%''s'";
private String getDeleteEnglishSwordsQuery(Language English) {
return "DELETE FROM words WHERE lang=" + English.getId() + " AND word LIKE '%''s'";
}
private static String getDeleteWordsQuery(int langId, String wordList) {
private String getDeleteWordsQuery(int langId, String wordList) {
return "DELETE FROM words WHERE lang=" + langId + " AND word IN(" + wordList + ")";
}
}

View file

@ -7,16 +7,10 @@ import androidx.room.migration.Migration;
import androidx.sqlite.db.SupportSQLiteDatabase;
import java.util.ArrayList;
import java.util.Locale;
import io.github.sspanak.tt9.languages.definitions.Bulgarian;
import io.github.sspanak.tt9.languages.definitions.Dutch;
import io.github.sspanak.tt9.languages.definitions.English;
import io.github.sspanak.tt9.languages.definitions.French;
import io.github.sspanak.tt9.languages.definitions.German;
import io.github.sspanak.tt9.languages.definitions.Italian;
import io.github.sspanak.tt9.languages.definitions.Russian;
import io.github.sspanak.tt9.languages.definitions.Spanish;
import io.github.sspanak.tt9.languages.definitions.Ukrainian;
import io.github.sspanak.tt9.languages.Language;
import io.github.sspanak.tt9.languages.LanguageCollection;
import io.github.sspanak.tt9.preferences.SettingsStore;
public class DB7 {
@ -28,28 +22,41 @@ public class DB7 {
}
private int getNewLanguageId(int oldId) {
Language language;
switch (oldId) {
default:
return oldId;
case 1:
return new English().getId();
language = LanguageCollection.getByLocale(ctx, Locale.ENGLISH.toString());
break;
case 2:
return new Russian().getId();
language = LanguageCollection.getByLocale(ctx, "ru_RU");
break;
case 3:
return new German().getId();
language = LanguageCollection.getByLocale(ctx, Locale.GERMAN.toString());
break;
case 4:
return new French().getId();
language = LanguageCollection.getByLocale(ctx, Locale.FRENCH.toString());
break;
case 5:
return new Italian().getId();
language = LanguageCollection.getByLocale(ctx, Locale.ITALIAN.toString());
break;
case 6:
return new Ukrainian().getId();
language = LanguageCollection.getByLocale(ctx, "uk_UA");
break;
case 7:
return new Bulgarian().getId();
language = LanguageCollection.getByLocale(ctx, "bg_BG");
break;
case 8:
return new Dutch().getId();
language = LanguageCollection.getByLocale(ctx, "nl_NL");
break;
case 9:
return new Spanish().getId();
language = LanguageCollection.getByLocale(ctx, "es_ES");
break;
}
return language != null ? language.getId() : -1;
}
private final Migration migration = new Migration(6, 7) {

View file

@ -27,7 +27,7 @@ public abstract class TT9Room extends RoomDatabase {
DB8.MIGRATION,
DB9.MIGRATION,
DB10.MIGRATION,
DB11.MIGRATION
new DB11().getMigration(context)
)
.build();
}

View file

@ -21,7 +21,7 @@ public class EmptyDatabaseWarning {
public EmptyDatabaseWarning(SettingsStore settings) {
WARNING_INTERVAL = settings.getDictionaryMissingWarningInterval();
for (Language lang : LanguageCollection.getAll()) {
for (Language lang : LanguageCollection.getAll(context)) {
if (!warningDisplayedTime.containsKey(lang.getId())) {
warningDisplayedTime.put(lang.getId(), 0L);
}

View file

@ -69,7 +69,7 @@ public class TraditionalT9 extends KeyPadHandler {
private void loadSettings() {
mLanguage = LanguageCollection.getLanguage(settings.getInputLanguage());
mLanguage = LanguageCollection.getLanguage(getMainContext(), settings.getInputLanguage());
mEnabledLanguages = settings.getEnabledLanguageIds();
validateLanguages();
@ -79,8 +79,8 @@ public class TraditionalT9 extends KeyPadHandler {
private void validateLanguages() {
mEnabledLanguages = InputModeValidator.validateEnabledLanguages(mEnabledLanguages);
mLanguage = InputModeValidator.validateLanguage(mLanguage, mEnabledLanguages);
mEnabledLanguages = InputModeValidator.validateEnabledLanguages(getMainContext(), mEnabledLanguages);
mLanguage = InputModeValidator.validateLanguage(getMainContext(), mLanguage, mEnabledLanguages);
settings.saveEnabledLanguageIds(mEnabledLanguages);
settings.saveInputLanguage(mLanguage.getId());
@ -689,7 +689,7 @@ public class TraditionalT9 extends KeyPadHandler {
// select the next language
int previous = mEnabledLanguages.indexOf(mLanguage.getId());
int next = (previous + 1) % mEnabledLanguages.size();
mLanguage = LanguageCollection.getLanguage(mEnabledLanguages.get(next));
mLanguage = LanguageCollection.getLanguage(getMainContext(), mEnabledLanguages.get(next));
validateLanguages();

View file

@ -1,5 +1,7 @@
package io.github.sspanak.tt9.ime.helpers;
import android.content.Context;
import java.util.ArrayList;
import io.github.sspanak.tt9.Logger;
@ -8,29 +10,29 @@ import io.github.sspanak.tt9.languages.Language;
import io.github.sspanak.tt9.languages.LanguageCollection;
public class InputModeValidator {
public static ArrayList<Integer> validateEnabledLanguages(ArrayList<Integer> enabledLanguageIds) {
ArrayList<Language> validLanguages = LanguageCollection.getAll(enabledLanguageIds);
public static ArrayList<Integer> validateEnabledLanguages(Context context, ArrayList<Integer> enabledLanguageIds) {
ArrayList<Language> validLanguages = LanguageCollection.getAll(context, enabledLanguageIds);
ArrayList<Integer> validLanguageIds = new ArrayList<>();
for (Language lang : validLanguages) {
validLanguageIds.add(lang.getId());
}
if (validLanguageIds.size() == 0) {
validLanguageIds.add(LanguageCollection.getDefault().getId());
validLanguageIds.add(LanguageCollection.getDefault(context).getId());
Logger.e("tt9/validateEnabledLanguages", "The language list seems to be corrupted. Resetting to first language only.");
}
return validLanguageIds;
}
public static Language validateLanguage(Language language, ArrayList<Integer> validLanguageIds) {
public static Language validateLanguage(Context context, Language language, ArrayList<Integer> validLanguageIds) {
if (language != null && validLanguageIds.contains(language.getId())) {
return language;
}
String error = language != null ? "Language: " + language.getId() + " is not enabled." : "Invalid language.";
Language validLanguage = LanguageCollection.getLanguage(validLanguageIds.get(0));
validLanguage = validLanguage != null ? validLanguage : LanguageCollection.getDefault();
Language validLanguage = LanguageCollection.getLanguage(context, validLanguageIds.get(0));
validLanguage = validLanguage != null ? validLanguage : LanguageCollection.getDefault(context);
Logger.w("tt9/validateLanguage", error + " Enforcing language: " + validLanguage.getId());

View file

@ -91,7 +91,8 @@ public class ModeABC extends InputMode {
}
String langCode = language.getLocale().getCountry();
langCode = langCode.length() == 0 ? language.getLocale().getLanguage() : langCode;
langCode = langCode.isEmpty() ? language.getLocale().getLanguage() : langCode;
langCode = langCode.isEmpty() ? language.getName() : langCode;
String modeString = language.getAbcString() + " / " + langCode.toUpperCase();

View file

@ -13,12 +13,82 @@ public class Language {
protected Locale locale;
protected String dictionaryFile;
protected String abcString;
protected ArrayList<ArrayList<String>> characterMap = new ArrayList<>();
private final HashMap<Character, String> reverseCharacterMap = new HashMap<>();
protected final ArrayList<ArrayList<String>> layout = new ArrayList<>();
private final HashMap<Character, String> characterKeyMap = new HashMap<>();
// settings
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;
switch (definition.locale) {
case "de":
definitionLocale = Locale.GERMAN;
break;
case "en":
definitionLocale = Locale.ENGLISH;
break;
case "fr":
definitionLocale = Locale.FRENCH;
break;
case "it":
definitionLocale = Locale.ITALIAN;
break;
default:
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() ? lang.abcString : 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<String> keyCharsFromDefinition(int key, ArrayList<String> definitionChars) {
final String defaultCharsPlaceholder = "DEFAULT";
if (key > 1 || !definitionChars.contains(defaultCharsPlaceholder)) {
return definitionChars;
}
ArrayList<String> keyChars = new ArrayList<>();
for (String defChar : definitionChars) {
if (defChar.equals(defaultCharsPlaceholder)) {
keyChars.addAll(key == 0 ? Characters.Special : Characters.Sentence);
} else {
keyChars.add(defChar);
}
}
return keyChars;
}
final public int getId() {
if (id == 0) {
id = generateId();
@ -95,11 +165,11 @@ public class Language {
return idInt;
}
private void generateReverseCharacterMap() {
reverseCharacterMap.clear();
private void generateCharacterKeyMap() {
characterKeyMap.clear();
for (int digit = 0; digit <= 9; digit++) {
for (String keyChar : getKeyCharacters(digit)) {
reverseCharacterMap.put(keyChar.charAt(0), String.valueOf(digit));
characterKeyMap.put(keyChar.charAt(0), String.valueOf(digit));
}
}
}
@ -131,11 +201,11 @@ public class Language {
}
public ArrayList<String> getKeyCharacters(int key, boolean includeDigit) {
if (key < 0 || key >= characterMap.size()) {
if (key < 0 || key >= layout.size()) {
return new ArrayList<>();
}
ArrayList<String> chars = new ArrayList<>(characterMap.get(key));
ArrayList<String> chars = new ArrayList<>(layout.get(key));
if (includeDigit && chars.size() > 0) {
chars.add(String.valueOf(key));
}
@ -151,17 +221,17 @@ public class Language {
StringBuilder sequence = new StringBuilder();
String lowerCaseWord = word.toLowerCase(locale);
if (reverseCharacterMap.isEmpty()) {
generateReverseCharacterMap();
if (characterKeyMap.isEmpty()) {
generateCharacterKeyMap();
}
for (int i = 0; i < lowerCaseWord.length(); i++) {
char letter = lowerCaseWord.charAt(i);
if (!reverseCharacterMap.containsKey(letter)) {
if (!characterKeyMap.containsKey(letter)) {
throw new InvalidLanguageCharactersException(this, "Failed generating digit sequence for word: '" + word);
}
sequence.append(reverseCharacterMap.get(letter));
sequence.append(characterKeyMap.get(letter));
}
return sequence.toString();

View file

@ -1,84 +1,70 @@
package io.github.sspanak.tt9.languages;
import android.content.Context;
import android.os.Build;
import androidx.annotation.Nullable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import io.github.sspanak.tt9.Logger;
import io.github.sspanak.tt9.languages.definitions.*;
public class LanguageCollection {
private static LanguageCollection self;
private final Language defaultLanguage = new English();
private final HashMap<Integer, Language> languages = new HashMap<>();
private LanguageCollection() {
List<Class<? extends Language>> languageList = Arrays.asList(
// Add languages here, to enable them in the UI and
// please, maintain the alphabetical order.
BrazilianPortuguese.class,
Bulgarian.class,
Dutch.class,
English.class,
Finnish.class,
French.class,
German.class,
Hebrew.class,
Indonesian.class,
Italian.class,
Norwegian.class,
Polish.class,
Russian.class,
Spanish.class,
Swedish.class,
Ukrainian.class,
Yiddish.class
);
// initialize the language objects from the class list above
for (Class<? extends Language> languageClass : languageList) {
private LanguageCollection(Context context) {
for (String file : LanguageDefinition.getAllFiles(context.getAssets())) {
try {
Language lang = languageClass.newInstance();
if (languages.containsKey(lang.getId())) {
throw new Exception("Duplicate language ID: " + lang.getId() + " for language: " + lang.getName());
}
Language lang = Language.fromDefinition(LanguageDefinition.fromFile(context.getAssets(), file));
languages.put(lang.getId(), lang);
} catch (Exception e) {
Logger.e("tt9.LanguageCollection", "Skipping an invalid language. " + e.getMessage());
Logger.e("tt9.LanguageCollection", "Skipping invalid language: '" + file + "'. " + e.getMessage());
}
}
}
public static LanguageCollection getInstance() {
public static LanguageCollection getInstance(Context context) {
if (self == null) {
self = new LanguageCollection();
self = new LanguageCollection(context);
}
return self;
}
public static Language getLanguage(int langId) {
if (getInstance().languages.containsKey(langId)) {
return getInstance().languages.get(langId);
public static Language getLanguage(Context context, int langId) {
if (getInstance(context).languages.containsKey(langId)) {
return getInstance(context).languages.get(langId);
}
return null;
}
public static Language getDefault() {
return getInstance().defaultLanguage;
public static Language getDefault(Context context) {
Language language = getByLocale(context, "en");
return language == null ? new NullLanguage(context) : language;
}
public static ArrayList<Language> getAll(ArrayList<Integer> languageIds, boolean sort) {
@Nullable
public static Language getByLocale(Context context, String locale) {
for (Language lang : getInstance(context).languages.values()) {
if (lang.getLocale().toString().equals(locale)) {
return lang;
}
}
return null;
}
public static ArrayList<Language> getAll(Context context, ArrayList<Integer> languageIds, boolean sort) {
ArrayList<Language> langList = new ArrayList<>();
for (int languageId : languageIds) {
Language lang = getLanguage(languageId);
Language lang = getLanguage(context, languageId);
if (lang != null) {
langList.add(lang);
}
@ -91,12 +77,12 @@ public class LanguageCollection {
return langList;
}
public static ArrayList<Language> getAll(ArrayList<Integer> languageIds) {
return getAll(languageIds, false);
public static ArrayList<Language> getAll(Context context, ArrayList<Integer> languageIds) {
return getAll(context, languageIds, false);
}
public static ArrayList<Language> getAll(boolean sort) {
ArrayList<Language> langList = new ArrayList<>(getInstance().languages.values());
public static ArrayList<Language> getAll(Context context, boolean sort) {
ArrayList<Language> langList = new ArrayList<>(getInstance(context).languages.values());
if (sort && Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
langList.sort(Comparator.comparing(l -> l.getLocale().toString()));
@ -105,8 +91,8 @@ public class LanguageCollection {
return langList;
}
public static ArrayList<Language> getAll() {
return getAll(false);
public static ArrayList<Language> getAll(Context context) {
return getAll(context,false);
}

View file

@ -0,0 +1,47 @@
package io.github.sspanak.tt9.languages;
import android.content.res.AssetManager;
import org.yaml.snakeyaml.Yaml;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import io.github.sspanak.tt9.Logger;
public class LanguageDefinition {
private static final String languagesDir = "languages";
private static final String definitionsDir = languagesDir + "/definitions";
public String abcString = "";
public String dictionaryFile = "";
public boolean hasUpperCase = true;
public ArrayList<ArrayList<String>> layout = new ArrayList<>();
public String locale = "";
public String name = "";
public static ArrayList<String> getAllFiles(AssetManager assets) {
ArrayList<String> files = new ArrayList<>();
try {
for (String file : assets.list(definitionsDir)) {
files.add(definitionsDir + "/" + file);
}
} catch (IOException e) {
Logger.e("tt9.LanguageDefinition", "Failed reading language definitions from: '" + definitionsDir + "'. " + e.getMessage());
}
return files;
}
public static LanguageDefinition fromFile(AssetManager assets, String definitionFile) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(assets.open(definitionFile), StandardCharsets.UTF_8));
return new Yaml().loadAs(reader, LanguageDefinition.class);
}
public String getDictionaryFile() {
return languagesDir + "/dictionaries/" + dictionaryFile;
}
}

View file

@ -0,0 +1,15 @@
package io.github.sspanak.tt9.languages;
import android.content.Context;
import java.util.Locale;
import io.github.sspanak.tt9.R;
public class NullLanguage extends Language {
public NullLanguage(Context context) {
locale = Locale.ROOT;
name = context.getString(R.string.no_language);
abcString = "abc";
}
}

View file

@ -1,20 +0,0 @@
package io.github.sspanak.tt9.languages.definitions;
import java.util.Arrays;
import java.util.Locale;
public class BrazilianPortuguese extends English {
public BrazilianPortuguese() {
super();
name = "Português brasileiro";
locale = new Locale("pt","BR");
dictionaryFile = "pt-BR-utf8.csv";
characterMap.get(2).addAll(Arrays.asList("ç", "á", "â", "ã", "à"));
characterMap.get(3).addAll(Arrays.asList("é", "ê", "è"));
characterMap.get(4).add("í");
characterMap.get(6).addAll(Arrays.asList("ó", "ô", "õ"));
characterMap.get(8).add("ú");
}
}

View file

@ -1,28 +0,0 @@
package io.github.sspanak.tt9.languages.definitions;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Locale;
import io.github.sspanak.tt9.languages.Language;
import io.github.sspanak.tt9.languages.Characters;
public class Bulgarian extends Language {
public Bulgarian() {
locale = new Locale("bg","BG");
dictionaryFile = "bg-utf8.csv";
characterMap = new ArrayList<>(Arrays.asList(
Characters.Special, // 0
Characters.Sentence, // 1
new ArrayList<>(Arrays.asList("а", "б", "в", "г")), // 2
new ArrayList<>(Arrays.asList("д", "е", "ж", "з")), // 3
new ArrayList<>(Arrays.asList("и", "й", "к", "л", "ѝ")), // 4
new ArrayList<>(Arrays.asList("м", "н", "о", "п")), // 5
new ArrayList<>(Arrays.asList("р", "с", "т", "у")), // 6
new ArrayList<>(Arrays.asList("ф", "х", "ц", "ч")), // 7
new ArrayList<>(Arrays.asList("ш", "щ", "ъ")), // 8
new ArrayList<>(Arrays.asList("ь", "ю", "я")) // 9
));
}
}

View file

@ -1,19 +0,0 @@
package io.github.sspanak.tt9.languages.definitions;
import java.util.Arrays;
import java.util.Locale;
public class Dutch extends English {
public Dutch() {
super();
locale = new Locale("nl","NL");
dictionaryFile = "nl-utf8.csv";
characterMap.get(2).addAll(Arrays.asList("à", "ä", "ç"));
characterMap.get(3).addAll(Arrays.asList("é", "è", "ê", "ë"));
characterMap.get(4).addAll(Arrays.asList("î", "ï"));
characterMap.get(6).add("ö");
characterMap.get(8).addAll(Arrays.asList("û", "ü"));
}
}

View file

@ -1,28 +0,0 @@
package io.github.sspanak.tt9.languages.definitions;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Locale;
import io.github.sspanak.tt9.languages.Characters;
import io.github.sspanak.tt9.languages.Language;
public class English extends Language {
public English() {
locale = Locale.ENGLISH;
dictionaryFile = "en-utf8.csv";
characterMap = new ArrayList<>(Arrays.asList(
Characters.Special, // 0
Characters.Sentence, // 1
new ArrayList<>(Arrays.asList("a", "b", "c")), // 2
new ArrayList<>(Arrays.asList("d", "e", "f")), // 3
new ArrayList<>(Arrays.asList("g", "h", "i")), // 4
new ArrayList<>(Arrays.asList("j", "k", "l")), // 5
new ArrayList<>(Arrays.asList("m", "n", "o")), // 6
new ArrayList<>(Arrays.asList("p", "q", "r", "s")), // 7
new ArrayList<>(Arrays.asList("t", "u", "v")), // 8
new ArrayList<>(Arrays.asList("w", "x", "y", "z")) // 9
));
}
}

View file

@ -1,16 +0,0 @@
package io.github.sspanak.tt9.languages.definitions;
import java.util.Arrays;
import java.util.Locale;
public class Finnish extends English {
public Finnish() {
super();
locale = new Locale("fi","FI");
dictionaryFile = "fi-utf8.csv";
characterMap.get(2).addAll(Arrays.asList("ä", "å"));
characterMap.get(6).add("ö");
}
}

View file

@ -1,20 +0,0 @@
package io.github.sspanak.tt9.languages.definitions;
import java.util.Arrays;
import java.util.Locale;
public class French extends English {
public French() {
super();
locale = Locale.FRENCH;
dictionaryFile = "fr-utf8.csv";
characterMap.get(2).addAll(Arrays.asList("à", "â", "æ", "ç"));
characterMap.get(3).addAll(Arrays.asList("é", "è", "ê", "ë"));
characterMap.get(4).addAll(Arrays.asList("î", "ï"));
characterMap.get(6).addAll(Arrays.asList("ô", "œ"));
characterMap.get(8).addAll(Arrays.asList("ù", "û", "ü"));
characterMap.get(9).add("ÿ");
}
}

View file

@ -1,17 +0,0 @@
package io.github.sspanak.tt9.languages.definitions;
import java.util.Locale;
public class German extends English {
public German() {
super();
locale = Locale.GERMAN;
dictionaryFile = "de-utf8.csv";
characterMap.get(2).add("ä");
characterMap.get(6).add("ö");
characterMap.get(7).add("ß");
characterMap.get(8).add("ü");
}
}

View file

@ -1,31 +0,0 @@
package io.github.sspanak.tt9.languages.definitions;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Locale;
import io.github.sspanak.tt9.languages.Characters;
import io.github.sspanak.tt9.languages.Language;
public class Hebrew extends Language {
public Hebrew() {
locale = new Locale("iw","IL");
dictionaryFile = "he-utf8.csv";
abcString = "אבג";
hasUpperCase = false;
characterMap = new ArrayList<>(Arrays.asList(
Characters.Special, // 0
Characters.Sentence, // 1
new ArrayList<>(Arrays.asList("ד", "ה", "ו")), // 2
new ArrayList<>(Arrays.asList("א", "ב", "ג")), // 3
new ArrayList<>(Arrays.asList("מ", "ם", "נ", "ן")), // 4
new ArrayList<>(Arrays.asList("י", "כ", "ך", "ל")), // 5
new ArrayList<>(Arrays.asList("ז", "ח", "ט")), // 6
new ArrayList<>(Arrays.asList("ר", "ש", "ת")), // 7
new ArrayList<>(Arrays.asList("צ", "ץ", "ק")), // 8
new ArrayList<>(Arrays.asList("ס", "ע", "פ", "ף")) // 9
));
}
}

View file

@ -1,13 +0,0 @@
package io.github.sspanak.tt9.languages.definitions;
import java.util.Locale;
public class Indonesian extends English {
public Indonesian() {
super();
name = "Bahasa Indonesia";
locale = new Locale("in", "ID");
dictionaryFile = "id-utf8.csv";
}
}

View file

@ -1,19 +0,0 @@
package io.github.sspanak.tt9.languages.definitions;
import java.util.Arrays;
import java.util.Locale;
public class Italian extends English {
public Italian() {
super();
locale = Locale.ITALIAN;
dictionaryFile = "it-utf8.csv";
characterMap.get(2).add("à");
characterMap.get(3).addAll(Arrays.asList("é", "è"));
characterMap.get(4).addAll(Arrays.asList("ì", "í", "î"));
characterMap.get(6).addAll(Arrays.asList("ò", "ó"));
characterMap.get(8).addAll(Arrays.asList("ù", "ú"));
}
}

View file

@ -1,18 +0,0 @@
package io.github.sspanak.tt9.languages.definitions;
import java.util.Arrays;
import java.util.Locale;
public class Norwegian extends English {
public Norwegian() {
super();
locale = new Locale("nb","NO");
dictionaryFile = "nb-utf8.csv";
characterMap.get(2).addAll(Arrays.asList("æ", "å"));
characterMap.get(3).addAll(Arrays.asList("é", "è"));
characterMap.get(6).addAll(Arrays.asList("ø", "ó", "ò", "ô"));
characterMap.get(8).add("ü");
}
}

View file

@ -1,20 +0,0 @@
package io.github.sspanak.tt9.languages.definitions;
import java.util.Arrays;
import java.util.Locale;
public class Polish extends English {
public Polish() {
super();
locale = new Locale("pl","PL");
dictionaryFile = "pl-utf8.csv";
characterMap.get(2).addAll(Arrays.asList("ą", "ć"));
characterMap.get(3).add("ę");
characterMap.get(5).add("ł");
characterMap.get(6).addAll(Arrays.asList("ó", "ń"));
characterMap.get(7).add("ś");
characterMap.get(9).addAll(Arrays.asList("ź", "ż"));
}
}

View file

@ -1,28 +0,0 @@
package io.github.sspanak.tt9.languages.definitions;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Locale;
import io.github.sspanak.tt9.languages.Characters;
import io.github.sspanak.tt9.languages.Language;
public class Russian extends Language {
public Russian() {
locale = new Locale("ru","RU");
dictionaryFile = "ru-utf8.csv";
characterMap = new ArrayList<>(Arrays.asList(
Characters.Special, // 0
Characters.Sentence, // 1
new ArrayList<>(Arrays.asList("а", "б", "в", "г")), // 2
new ArrayList<>(Arrays.asList("д", "е", "ё", "ж", "з")), // 3
new ArrayList<>(Arrays.asList("и", "й", "к", "л")), // 4
new ArrayList<>(Arrays.asList("м", "н", "о", "п")), // 5
new ArrayList<>(Arrays.asList("р", "с", "т", "у")), // 6
new ArrayList<>(Arrays.asList("ф", "х", "ц", "ч")), // 7
new ArrayList<>(Arrays.asList("ш", "щ", "ъ", "ы")), // 8
new ArrayList<>(Arrays.asList("ь", "э", "ю", "я")) // 9
));
}
}

View file

@ -1,25 +0,0 @@
package io.github.sspanak.tt9.languages.definitions;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Locale;
import io.github.sspanak.tt9.languages.Characters;
public class Spanish extends English {
public Spanish() {
super();
locale = new Locale("es", "ES");
dictionaryFile = "es-utf8.csv";
characterMap.set(1, new ArrayList<>(Characters.Sentence));
characterMap.get(1).addAll(Arrays.asList("¡", "¿"));
characterMap.get(2).add("á");
characterMap.get(3).add("é");
characterMap.get(4).add("í");
characterMap.set(6, new ArrayList<>(Arrays.asList("m", "n", "ñ", "o", "ó")));
characterMap.get(8).addAll(Arrays.asList("ú", "ü"));
}
}

View file

@ -1,17 +0,0 @@
package io.github.sspanak.tt9.languages.definitions;
import java.util.Arrays;
import java.util.Locale;
public class Swedish extends English {
public Swedish() {
super();
locale = new Locale("sv","SE");
dictionaryFile = "sv-utf8.csv";
characterMap.get(2).addAll(Arrays.asList("å", "ä"));
characterMap.get(3).add("é");
characterMap.get(6).add("ö");
}
}

View file

@ -1,28 +0,0 @@
package io.github.sspanak.tt9.languages.definitions;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Locale;
import io.github.sspanak.tt9.languages.Characters;
import io.github.sspanak.tt9.languages.Language;
public class Ukrainian extends Language {
public Ukrainian() {
locale = new Locale("uk","UA");
dictionaryFile = "uk-utf8.csv";
characterMap = new ArrayList<>(Arrays.asList(
Characters.Special, // 0
Characters.Sentence, // 1
new ArrayList<>(Arrays.asList("а", "б", "в", "г", "ґ")), // 2
new ArrayList<>(Arrays.asList("д", "е", "є", "ж", "з")), // 3
new ArrayList<>(Arrays.asList("и", "і", "ї", "й", "к", "л")), // 4
new ArrayList<>(Arrays.asList("м", "н", "о", "п")), // 5
new ArrayList<>(Arrays.asList("р", "с", "т", "у")), // 6
new ArrayList<>(Arrays.asList("ф", "х", "ц", "ч")), // 7
new ArrayList<>(Arrays.asList("ш", "щ")), // 8
new ArrayList<>(Arrays.asList("ь", "ю", "я")) // 9
));
}
}

View file

@ -1,12 +0,0 @@
package io.github.sspanak.tt9.languages.definitions;
import java.util.Locale;
public class Yiddish extends Hebrew {
public Yiddish() {
super();
locale = new Locale("ji","JI");
dictionaryFile = "ji-utf8.csv";
}
}

View file

@ -41,7 +41,7 @@ public class PreferencesActivity extends AppCompatActivity implements Preference
DictionaryDb.init(this);
DictionaryDb.normalizeWordFrequencies(settings);
InputModeValidator.validateEnabledLanguages(settings.getEnabledLanguageIds());
InputModeValidator.validateEnabledLanguages(this, settings.getEnabledLanguageIds());
validateFunctionKeys();
super.onCreate(savedInstanceState);

View file

@ -19,11 +19,13 @@ import io.github.sspanak.tt9.preferences.items.SectionKeymap;
public class SettingsStore {
private final Context context;
private final SharedPreferences prefs;
private final SharedPreferences.Editor prefsEditor;
public SettingsStore(Context context) {
this.context = context;
prefs = PreferenceManager.getDefaultSharedPreferences(context);
prefsEditor = prefs.edit();
}
@ -32,7 +34,7 @@ public class SettingsStore {
/************* validators *************/
private boolean doesLanguageExist(int langId) {
return LanguageCollection.getLanguage(langId) != null;
return LanguageCollection.getLanguage(context, langId) != null;
}
private boolean validateSavedLanguage(int langId, String logTag) {
@ -70,7 +72,7 @@ public class SettingsStore {
public Set<String> getEnabledLanguagesIdsAsStrings() {
return prefs.getStringSet("pref_languages", new HashSet<>(Collections.singletonList(
String.valueOf(LanguageCollection.getDefault().getId())
String.valueOf(LanguageCollection.getDefault(context).getId())
)));
}
@ -124,7 +126,7 @@ public class SettingsStore {
public int getInputLanguage() {
return prefs.getInt("pref_input_language", LanguageCollection.getDefault().getId());
return prefs.getInt("pref_input_language", LanguageCollection.getDefault(context).getId());
}
public void saveInputLanguage(int language) {

View file

@ -38,7 +38,7 @@ abstract class ItemClickable {
*
* My smashed Qin F21 Pro+ occasionally does this, if I press the keys hard.
* There were reports the same happens on Kyocera KYF31, causing absolutely undesirable side effects.
* @see: <a href="https://github.com/sspanak/tt9/issues/117">...</a>
* See: <a href="https://github.com/sspanak/tt9/issues/117">...</a>
*/
protected boolean debounceClick(Preference p) {
long now = System.currentTimeMillis();

View file

@ -45,7 +45,7 @@ public class ItemLoadDictionary extends ItemClickable {
private void onLoadingStatusChange(Bundle status) {
progressBar.show(status);
progressBar.show(context, status);
item.setSummary(progressBar.getTitle() + " " + progressBar.getMessage());
if (progressBar.isCancelled()) {
@ -62,7 +62,7 @@ public class ItemLoadDictionary extends ItemClickable {
@Override
protected boolean onClick(Preference p) {
ArrayList<Language> languages = LanguageCollection.getAll(settings.getEnabledLanguageIds());
ArrayList<Language> languages = LanguageCollection.getAll(context, settings.getEnabledLanguageIds());
try {
loader.load(languages);

View file

@ -1,21 +1,27 @@
package io.github.sspanak.tt9.preferences.items;
import android.content.Context;
import androidx.preference.MultiSelectListPreference;
import java.util.ArrayList;
import java.util.HashSet;
import io.github.sspanak.tt9.R;
import io.github.sspanak.tt9.languages.Language;
import io.github.sspanak.tt9.languages.LanguageCollection;
import io.github.sspanak.tt9.preferences.SettingsStore;
import io.github.sspanak.tt9.ui.UI;
public class ItemSelectLanguage {
public static final String NAME = "pref_languages";
private final Context context;
private final SettingsStore settings;
private final MultiSelectListPreference item;
public ItemSelectLanguage(MultiSelectListPreference multiSelect, SettingsStore settings) {
public ItemSelectLanguage(Context context, MultiSelectListPreference multiSelect, SettingsStore settings) {
this.context = context;
this.item = multiSelect;
this.settings = settings;
}
@ -25,7 +31,11 @@ public class ItemSelectLanguage {
return this;
}
ArrayList<Language> languages = LanguageCollection.getAll(true);
ArrayList<Language> languages = LanguageCollection.getAll(context, true);
if (languages.isEmpty()) {
UI.alert(context, R.string.error, R.string.failed_loading_language_definitions);
// do not return, the MultiSelect component requires arrays, even if empty, otherwise it crashes
}
ArrayList<CharSequence> values = new ArrayList<>();
for (Language l : languages) {
@ -70,7 +80,7 @@ public class ItemSelectLanguage {
private void previewSelection() {
item.setSummary(
LanguageCollection.toString(LanguageCollection.getAll(settings.getEnabledLanguageIds(), true))
LanguageCollection.toString(LanguageCollection.getAll(context, settings.getEnabledLanguageIds(), true))
);
}
}

View file

@ -39,7 +39,7 @@ public class ItemTruncateUnselected extends ItemTruncateAll {
ArrayList<Integer> unselectedLanguageIds = new ArrayList<>();
ArrayList<Integer> selectedLanguageIds = settings.getEnabledLanguageIds();
for (Language lang : LanguageCollection.getAll(false)) {
for (Language lang : LanguageCollection.getAll(activity, false)) {
if (!selectedLanguageIds.contains(lang.getId())) {
unselectedLanguageIds.add(lang.getId());
}

View file

@ -17,6 +17,7 @@ public class DictionariesScreen extends BaseScreenFragment {
@Override
protected void onCreate() {
ItemSelectLanguage multiSelect = new ItemSelectLanguage(
activity,
findPreference(ItemSelectLanguage.NAME),
activity.settings
);

View file

@ -75,7 +75,7 @@ public class AddWordAct extends AppCompatActivity {
word = ((EditText) main.findViewById(R.id.add_word_text)).getText().toString();
Logger.d("addWord", "Attempting to add word: '" + word + "'...");
DictionaryDb.insertWord(this::onAddedWord, LanguageCollection.getLanguage(lang), word);
DictionaryDb.insertWord(this::onAddedWord, LanguageCollection.getLanguage(this, lang), word);
} catch (InsertBlankWordException e) {
Logger.e("AddWordAct.addWord", e.getMessage());
UI.toastLong(this, R.string.add_word_blank);

View file

@ -103,13 +103,14 @@ public class DictionaryLoadingBar {
}
public void show(Bundle data) {
public void show(Context context, Bundle data) {
String error = data.getString("error", null);
int fileCount = data.getInt("fileCount", -1);
if (error != null) {
hasFailed = true;
showError(
context,
error,
data.getInt("languageId", -1),
data.getLong("fileLine", -1),
@ -120,6 +121,7 @@ public class DictionaryLoadingBar {
} else {
hasFailed = false;
showProgress(
context,
data.getLong("time", 0),
data.getInt("currentFile", 0),
data.getInt("progress", 0),
@ -129,8 +131,8 @@ public class DictionaryLoadingBar {
}
private String generateTitle(int languageId) {
Language lang = LanguageCollection.getLanguage(languageId);
private String generateTitle(Context context, int languageId) {
Language lang = LanguageCollection.getLanguage(context, languageId);
if (lang != null) {
return resources.getString(R.string.dictionary_loading, lang.getName());
@ -140,7 +142,7 @@ public class DictionaryLoadingBar {
}
private void showProgress(long time, int currentFile, int currentFileProgress, int languageId) {
private void showProgress(Context context, long time, int currentFile, int currentFileProgress, int languageId) {
if (currentFileProgress < 0) {
hide();
isStopped = true;
@ -154,12 +156,12 @@ public class DictionaryLoadingBar {
if (progress >= maxProgress) {
progress = maxProgress = 0;
title = generateTitle(-1);
title = generateTitle(context, -1);
String timeFormat = time > 60000 ? " (%1.0fs)" : " (%1.1fs)";
message = resources.getString(R.string.completed) + String.format(Locale.ENGLISH, timeFormat, time / 1000.0);
} else {
title = generateTitle(languageId);
title = generateTitle(context, languageId);
message = currentFileProgress + "%";
}
@ -167,8 +169,8 @@ public class DictionaryLoadingBar {
}
private void showError(String errorType, int langId, long line, String word) {
Language lang = LanguageCollection.getLanguage(langId);
private void showError(Context context, String errorType, int langId, long line, String word) {
Language lang = LanguageCollection.getLanguage(context, langId);
if (lang == null || errorType.equals(InvalidLanguageException.class.getSimpleName())) {
message = resources.getString(R.string.add_word_invalid_language);
@ -180,7 +182,7 @@ public class DictionaryLoadingBar {
message = resources.getString(R.string.dictionary_load_error, lang.getName(), errorType);
}
title = generateTitle(-1);
title = generateTitle(context, -1);
progress = maxProgress = 0;
renderError();

View file

@ -1,5 +1,6 @@
package io.github.sspanak.tt9.ui;
import android.app.AlertDialog;
import android.content.Context;
import android.content.Intent;
import android.os.Looper;
@ -27,6 +28,15 @@ public class UI {
tt9.startActivity(prefIntent);
}
public static void alert(Context context, int titleResource, int messageResource) {
new AlertDialog.Builder(context)
.setTitle(titleResource)
.setMessage(messageResource)
.setCancelable(false)
.setPositiveButton(android.R.string.ok, (dialog, which) -> dialog.cancel())
.show();
}
public static void toast(Context context, CharSequence msg) {
Toast.makeText(context, msg, Toast.LENGTH_SHORT).show();
}

View file

@ -89,7 +89,7 @@ public class SoftNumberKey extends SoftKey {
}
// 2-9
Language language = LanguageCollection.getLanguage(tt9.getSettings().getInputLanguage());
Language language = LanguageCollection.getLanguage(tt9.getApplicationContext(), tt9.getSettings().getInputLanguage());
if (language == null) {
Logger.d("SoftNumberKey.getLabel", "Cannot generate a label when the language is NULL.");
return "";