the search for language files is now 4 times faster, resulting in faster initial start up (or faster opening the Settings screen)
This commit is contained in:
parent
ae619e1f0f
commit
c844db1fa1
7 changed files with 180 additions and 157 deletions
4
.github/workflows/build.yml
vendored
4
.github/workflows/build.yml
vendored
|
|
@ -29,7 +29,9 @@ jobs:
|
||||||
- name: Validate Dictionaries
|
- name: Validate Dictionaries
|
||||||
run: ./gradlew validateLanguages
|
run: ./gradlew validateLanguages
|
||||||
- name: Build Languages
|
- name: Build Languages
|
||||||
run: ./gradlew copyDefinitions convertHelp buildDictionaryDownloads copyDownloadsToAssets
|
run: ./gradlew buildDefinition buildDictionaryDownloads
|
||||||
|
- name: Copy Downloads
|
||||||
|
run: ./gradlew copyDownloadsToAssets
|
||||||
- name: Lint
|
- name: Lint
|
||||||
run: ./gradlew lint # this actually runs mergeResources, so it must come after the dictionary tasks
|
run: ./gradlew lint # this actually runs mergeResources, so it must come after the dictionary tasks
|
||||||
- name: Build all APK variants
|
- name: Build all APK variants
|
||||||
|
|
|
||||||
22
app/build-definitions.gradle
Normal file
22
app/build-definitions.gradle
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
ext.mergeDefinitions = { String definitionsInputDir, String definitionsOutputPath ->
|
||||||
|
def merged = new File(definitionsOutputPath)
|
||||||
|
merged.delete()
|
||||||
|
|
||||||
|
boolean isFirst = true
|
||||||
|
fileTree(dir: definitionsInputDir).getFiles().each { file ->
|
||||||
|
if (!file.isFile() || !file.name.endsWith(".yml")) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isFirst) {
|
||||||
|
isFirst = false
|
||||||
|
} else {
|
||||||
|
merged << "\n---\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
merged << file.text
|
||||||
|
.replaceAll("\\s*#[^\n]+", "")
|
||||||
|
.replaceAll("^[ ]+\n", "")
|
||||||
|
.trim()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,19 +3,13 @@ plugins {
|
||||||
}
|
}
|
||||||
|
|
||||||
apply from: 'constants.gradle'
|
apply from: 'constants.gradle'
|
||||||
apply from: 'build-dictionary.gradle'
|
apply from: 'build-definitions.gradle'
|
||||||
|
apply from: 'build-dictionaries.gradle'
|
||||||
apply from: 'validate-languages.gradle'
|
apply from: 'validate-languages.gradle'
|
||||||
apply from: 'help-tools.gradle'
|
apply from: 'help-tools.gradle'
|
||||||
apply from: 'version-tools.gradle'
|
apply from: 'version-tools.gradle'
|
||||||
|
|
||||||
|
|
||||||
tasks.register('copyDefinitions', Copy) {
|
|
||||||
from LANGUAGES_INPUT_DIR
|
|
||||||
include '**/*.yml'
|
|
||||||
into LANGUAGES_OUTPUT_DIR
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
tasks.register('validateLanguages') {
|
tasks.register('validateLanguages') {
|
||||||
inputs.dir LANGUAGES_INPUT_DIR
|
inputs.dir LANGUAGES_INPUT_DIR
|
||||||
outputs.dir LANGUAGE_VALIDATION_DIR
|
outputs.dir LANGUAGE_VALIDATION_DIR
|
||||||
|
|
@ -26,8 +20,18 @@ tasks.register('validateLanguages') {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
tasks.register('buildDefinition') {
|
||||||
|
inputs.dir DEFINITIONS_INPUT_DIR
|
||||||
|
outputs.file DEFINITIONS_OUTPUT_FILE
|
||||||
|
|
||||||
|
doLast {
|
||||||
|
mergeDefinitions(DEFINITIONS_INPUT_DIR, DEFINITIONS_OUTPUT_FILE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
tasks.register('buildDictionaryDownloads') {
|
tasks.register('buildDictionaryDownloads') {
|
||||||
inputs.dir DICTIONARIES_INPUT_DIR
|
inputs.dir LANGUAGES_INPUT_DIR
|
||||||
outputs.dir DICTIONARIES_DOWNLOAD_DIR
|
outputs.dir DICTIONARIES_DOWNLOAD_DIR
|
||||||
outputs.dir DICTIONARY_META_OUTPUT_DIR
|
outputs.dir DICTIONARY_META_OUTPUT_DIR
|
||||||
|
|
||||||
|
|
@ -139,7 +143,7 @@ android {
|
||||||
].each { taskName ->
|
].each { taskName ->
|
||||||
try {
|
try {
|
||||||
tasks.named(taskName)?.configure {
|
tasks.named(taskName)?.configure {
|
||||||
dependsOn(copyDefinitions, convertHelp, validateLanguages, buildDictionaryDownloads)
|
dependsOn(buildDefinition, convertHelp, validateLanguages, buildDictionaryDownloads)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (taskName.toLowerCase().contains("full")) {
|
if (taskName.toLowerCase().contains("full")) {
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ ext.DICTIONARIES_INPUT_DIR = "${LANGUAGES_INPUT_DIR}/${DICTIONARIES_DIR_NAME}"
|
||||||
ext.DICTIONARIES_DOWNLOAD_DIR = "${project.rootDir}/${DICTIONARIES_DOWNLOAD_DIR_NAME}"
|
ext.DICTIONARIES_DOWNLOAD_DIR = "${project.rootDir}/${DICTIONARIES_DOWNLOAD_DIR_NAME}"
|
||||||
|
|
||||||
ext.LANGUAGES_OUTPUT_DIR = "${MAIN_ASSETS_DIR}/${LANGUAGES_DIR_NAME}"
|
ext.LANGUAGES_OUTPUT_DIR = "${MAIN_ASSETS_DIR}/${LANGUAGES_DIR_NAME}"
|
||||||
ext.DEFINITIONS_OUTPUT_DIR = "${LANGUAGES_OUTPUT_DIR}/${DEFINITIONS_DIR_NAME}"
|
ext.DEFINITIONS_OUTPUT_FILE = "${LANGUAGES_OUTPUT_DIR}/${DEFINITIONS_DIR_NAME}.yml"
|
||||||
ext.DICTIONARY_META_OUTPUT_DIR = "${LANGUAGES_OUTPUT_DIR}/${DICTIONARIES_DIR_NAME}"
|
ext.DICTIONARY_META_OUTPUT_DIR = "${LANGUAGES_OUTPUT_DIR}/${DICTIONARIES_DIR_NAME}"
|
||||||
ext.DICTIONARIES_OUTPUT_DIR = "${FULL_VERSION_ASSETS_DIR}/${LANGUAGES_DIR_NAME}/${DICTIONARIES_DIR_NAME}"
|
ext.DICTIONARIES_OUTPUT_DIR = "${FULL_VERSION_ASSETS_DIR}/${LANGUAGES_DIR_NAME}/${DICTIONARIES_DIR_NAME}"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,12 +19,12 @@ public class LanguageCollection {
|
||||||
private final HashMap<Integer, NaturalLanguage> languages = new HashMap<>();
|
private final HashMap<Integer, NaturalLanguage> languages = new HashMap<>();
|
||||||
|
|
||||||
private LanguageCollection(Context context) {
|
private LanguageCollection(Context context) {
|
||||||
for (String file : LanguageDefinition.getAllFiles(context.getAssets())) {
|
for (LanguageDefinition definition : LanguageDefinition.getAll(context.getAssets())) {
|
||||||
try {
|
try {
|
||||||
NaturalLanguage lang = NaturalLanguage.fromDefinition(LanguageDefinition.fromFile(context.getAssets(), file));
|
NaturalLanguage lang = NaturalLanguage.fromDefinition(definition);
|
||||||
languages.put(lang.getId(), lang);
|
languages.put(lang.getId(), lang);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Logger.e("tt9.LanguageCollection", "Skipping invalid language: '" + file + "'. " + e.getMessage());
|
Logger.e("tt9.LanguageCollection", "Skipping invalid language: '" + definition.name + "'. " + e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,17 +8,17 @@ import androidx.annotation.Nullable;
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
import io.github.sspanak.tt9.BuildConfig;
|
import io.github.sspanak.tt9.BuildConfig;
|
||||||
import io.github.sspanak.tt9.util.AssetFile;
|
import io.github.sspanak.tt9.util.AssetFile;
|
||||||
import io.github.sspanak.tt9.util.Logger;
|
import io.github.sspanak.tt9.util.Logger;
|
||||||
|
|
||||||
public class LanguageDefinition extends AssetFile {
|
public class LanguageDefinition {
|
||||||
private static final String LOG_TAG = LanguageDefinition.class.getSimpleName();
|
private static final String LOG_TAG = LanguageDefinition.class.getSimpleName();
|
||||||
|
|
||||||
private static final String languagesDir = "languages";
|
private static final String LANGUAGES_DIR = "languages";
|
||||||
private static final String definitionsDir = languagesDir + "/definitions";
|
private static final String DEFINITIONS_PATH = LANGUAGES_DIR + "/definitions.yml";
|
||||||
|
private static final String YAML_SEPARATOR = "---";
|
||||||
|
|
||||||
public String abcString = "";
|
public String abcString = "";
|
||||||
public String currency = "";
|
public String currency = "";
|
||||||
|
|
@ -30,186 +30,181 @@ public class LanguageDefinition extends AssetFile {
|
||||||
public String locale = "";
|
public String locale = "";
|
||||||
public String name = "";
|
public String name = "";
|
||||||
|
|
||||||
|
private boolean inLayout = false;
|
||||||
|
|
||||||
public LanguageDefinition(AssetManager assets, String name) {
|
|
||||||
super(assets, definitionsDir + "/" + name);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* getAllFiles
|
* Converts YAML definitions to a LanguageDefinition objects. All properties in the YAML are
|
||||||
* Returns a list of the paths of all language definition files in the assets folder or an empty list on error.
|
* considered optional, so the LanguageDefinition defaults will be used when some property is omitted.
|
||||||
*/
|
|
||||||
public static ArrayList<String> getAllFiles(AssetManager assets) {
|
|
||||||
ArrayList<String> files = new ArrayList<>();
|
|
||||||
try {
|
|
||||||
files.addAll(Arrays.asList(assets.list(definitionsDir)));
|
|
||||||
Logger.d(LOG_TAG, "Found: " + files.size() + " languages.");
|
|
||||||
} catch (IOException | NullPointerException e) {
|
|
||||||
Logger.e(LOG_TAG, "Failed reading language definitions from: '" + definitionsDir + "'. " + e.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
return files;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* fromFile
|
|
||||||
* Takes the path to a language definition in the assets folder and parses that file into a LanguageDefinition
|
|
||||||
* or throws an IOException on error.
|
|
||||||
*/
|
|
||||||
public static LanguageDefinition fromFile(AssetManager assetManager, String definitionFile) throws IOException {
|
|
||||||
LanguageDefinition definition = new LanguageDefinition(assetManager, definitionFile);
|
|
||||||
definition.parse(definition.load(definition));
|
|
||||||
return definition;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* load
|
|
||||||
* Loads a language definition file from the assets folder into a String or throws an IOException on error.
|
|
||||||
*/
|
|
||||||
private ArrayList<String> load(LanguageDefinition definitionFile) throws IOException {
|
|
||||||
BufferedReader reader = definitionFile.getReader();
|
|
||||||
ArrayList<String> fileContents = new ArrayList<>();
|
|
||||||
String line;
|
|
||||||
while ((line = reader.readLine()) != null) {
|
|
||||||
fileContents.add(line);
|
|
||||||
}
|
|
||||||
|
|
||||||
return fileContents;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* parse
|
|
||||||
* Converts "yaml" to a LanguageDefinition object. All properties in the YAML are considered optional,
|
|
||||||
* so the LanguageDefinition defaults will be used when some property is omitted.
|
|
||||||
* Had to write all this, because the only usable library, SnakeYAML, works fine on Android 10+,
|
* Had to write all this, because the only usable library, SnakeYAML, works fine on Android 10+,
|
||||||
* but causes crashes on older devices.
|
* but causes crashes on older devices.
|
||||||
*/
|
*/
|
||||||
|
private LanguageDefinition() {}
|
||||||
|
|
||||||
private void parse(ArrayList<String> yaml) {
|
|
||||||
abcString = getPropertyFromYaml(yaml, "abcString", abcString);
|
|
||||||
currency = getPropertyFromYaml(yaml, "currency", currency);
|
|
||||||
|
|
||||||
dictionaryFile = getPropertyFromYaml(yaml, "dictionaryFile", dictionaryFile);
|
/**
|
||||||
if (dictionaryFile != null) {
|
* Returns a list of all language definitions contained in the asset at DEFINITIONS_PATH,
|
||||||
dictionaryFile = dictionaryFile.replaceFirst("\\.\\w+$", "." + BuildConfig.DICTIONARY_EXTENSION);
|
* or an empty list on error.
|
||||||
|
*/
|
||||||
|
public static ArrayList<LanguageDefinition> getAll(AssetManager assets) {
|
||||||
|
String[] definitionLines = readDefinitions(assets);
|
||||||
|
if (definitionLines.length == 0) {
|
||||||
|
return new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
hasSpaceBetweenWords = getPropertyFromYaml(yaml, "hasSpaceBetweenWords", hasSpaceBetweenWords);
|
ArrayList<LanguageDefinition> definitions = new ArrayList<>();
|
||||||
hasUpperCase = getPropertyFromYaml(yaml, "hasUpperCase", hasUpperCase);
|
|
||||||
isSyllabary = hasYamlProperty(yaml, "sounds");
|
LanguageDefinition definition = new LanguageDefinition();
|
||||||
layout = getLayoutFromYaml(yaml);
|
for (String line : definitionLines) {
|
||||||
locale = getPropertyFromYaml(yaml, "locale", locale);
|
if (YAML_SEPARATOR.equals(line)) {
|
||||||
name = getPropertyFromYaml(yaml, "name", name);
|
definitions.add(definition);
|
||||||
|
definition = new LanguageDefinition();
|
||||||
|
} else if (!definition.setLayoutEntry(line)) {
|
||||||
|
definition.setProperty(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
definitions.add(definition);
|
||||||
|
|
||||||
|
Logger.d("tt9.LanguageCollection", "Found " + definitions.size() + " languages");
|
||||||
|
return definitions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* getPropertyFromYaml
|
* Reads the language definitions from DEFINITIONS_PATH and returns them as an array of strings.
|
||||||
* Finds "property" in the "yaml" and returns its value.
|
|
||||||
* Optional properties are allowed. If the property is not found, "defaultValue" will be returned.
|
|
||||||
*/
|
*/
|
||||||
@Nullable
|
private static String[] readDefinitions(AssetManager assets) {
|
||||||
private String getPropertyFromYaml(ArrayList<String> yaml, String property, String defaultValue) {
|
try {
|
||||||
for (String line : yaml) {
|
BufferedReader reader = new AssetFile(assets, DEFINITIONS_PATH).getReader();
|
||||||
line = line.replaceAll("#.+$", "").trim();
|
StringBuilder contents = new StringBuilder();
|
||||||
String[] parts = line.split(":");
|
char[] buffer = new char[10000];
|
||||||
if (parts.length < 2) {
|
int read;
|
||||||
continue;
|
|
||||||
|
while ((read = reader.read(buffer)) != -1) {
|
||||||
|
contents.append(buffer, 0, read);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (property.equals(parts[0].trim())) {
|
return contents.toString().split("\n");
|
||||||
return parts[1].trim();
|
} catch (IOException e) {
|
||||||
}
|
Logger.e(LOG_TAG, "Failed reading language definitions from: '" + DEFINITIONS_PATH + "'. " + e.getMessage());
|
||||||
|
return new String[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
return defaultValue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private boolean hasYamlProperty(ArrayList<String> yaml, String property) {
|
/**
|
||||||
final String yamlProperty = property + ":";
|
* Normalizes a YAML boolean to a Java boolean.
|
||||||
|
*/
|
||||||
|
private boolean parseYamlBoolean(@Nullable String value) {
|
||||||
|
if (value == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
for (String line : yaml) {
|
return switch (value.toLowerCase()) {
|
||||||
if (line.startsWith(yamlProperty)) {
|
case "true", "on", "yes", "y" -> true;
|
||||||
return true;
|
default -> false;
|
||||||
}
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a property based on the key-value pair in a YAML line. If the key does not match any
|
||||||
|
* property, the line is ignored.
|
||||||
|
*/
|
||||||
|
private void setProperty(@NonNull String line) {
|
||||||
|
int colonIndex = line.indexOf(':');
|
||||||
|
if (colonIndex == -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String key = line.substring(0, colonIndex).trim();
|
||||||
|
String value = (colonIndex + 1 < line.length()) ? line.substring(colonIndex + 1).trim() : "";
|
||||||
|
|
||||||
|
switch (key) {
|
||||||
|
case "abcString":
|
||||||
|
abcString = value;
|
||||||
|
break;
|
||||||
|
case "currency":
|
||||||
|
currency = value;
|
||||||
|
break;
|
||||||
|
case "dictionaryFile":
|
||||||
|
dictionaryFile = value.replaceFirst("\\.\\w+$", "." + BuildConfig.DICTIONARY_EXTENSION);
|
||||||
|
break;
|
||||||
|
case "hasSpaceBetweenWords":
|
||||||
|
hasSpaceBetweenWords = parseYamlBoolean(value);
|
||||||
|
break;
|
||||||
|
case "hasUpperCase":
|
||||||
|
hasUpperCase = parseYamlBoolean(value);
|
||||||
|
break;
|
||||||
|
case "sounds":
|
||||||
|
isSyllabary = true;
|
||||||
|
break;
|
||||||
|
case "locale":
|
||||||
|
locale = value;
|
||||||
|
break;
|
||||||
|
case "name":
|
||||||
|
name = value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds the key layout line by line. Returns true when a layout entry is successfully set.
|
||||||
|
*/
|
||||||
|
private boolean setLayoutEntry(@NonNull String line) {
|
||||||
|
if (!inLayout) {
|
||||||
|
return inLayout = "layout:".equals(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
ArrayList<String> layoutEntry = getLayoutEntryFromYamlLine(line);
|
||||||
|
if (layoutEntry == null) {
|
||||||
|
inLayout = false;
|
||||||
|
} else {
|
||||||
|
layout.add(layoutEntry);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The boolean variant of getPropertyFromYaml. It returns true if the property is found and is:
|
|
||||||
* "true", "on", "yes" or "y".
|
|
||||||
*/
|
|
||||||
private boolean getPropertyFromYaml(ArrayList<String> yaml, String property, boolean defaultValue) {
|
|
||||||
String value = getPropertyFromYaml(yaml, property, null);
|
|
||||||
if (value == null) {
|
|
||||||
return defaultValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
value = value.toLowerCase();
|
|
||||||
return value.equals("true") || value.equals("on") || value.equals("yes") || value.equals("y");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* getLayoutFromYaml
|
|
||||||
* Finds and extracts the keypad layout. Less than 10 keys are accepted allowed leaving the ones up to 9-key empty.
|
|
||||||
*/
|
|
||||||
@NonNull
|
|
||||||
private ArrayList<ArrayList<String>> getLayoutFromYaml(ArrayList<String> yaml) {
|
|
||||||
ArrayList<ArrayList<String>> layout = new ArrayList<>();
|
|
||||||
|
|
||||||
boolean inLayout = false;
|
|
||||||
for (int i = 0; i < yaml.size(); i++) {
|
|
||||||
if (yaml.get(i).contains("layout")) {
|
|
||||||
inLayout = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (inLayout) {
|
|
||||||
ArrayList<String> lineChars = getLayoutEntryFromYamlLine(yaml.get(i));
|
|
||||||
if (lineChars != null) {
|
|
||||||
layout.add(lineChars);
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return layout;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* getLayoutEntryFromYamlLine
|
* getLayoutEntryFromYamlLine
|
||||||
* Validates a YAML line as an array and returns the character list to be assigned to a given key (a layout entry).
|
* Validates a YAML line as an array and returns the character list to be assigned to a given key (a layout entry).
|
||||||
* If the YAML line is invalid, NULL will be returned.
|
* If the YAML line is invalid, NULL will be returned.
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
private ArrayList<String> getLayoutEntryFromYamlLine(String yamlLine) {
|
private ArrayList<String> getLayoutEntryFromYamlLine(@NonNull String yamlLine) {
|
||||||
if (!yamlLine.contains("[") || !yamlLine.contains("]")) {
|
int start = yamlLine.indexOf('[');
|
||||||
|
int end = yamlLine.indexOf(']');
|
||||||
|
if (start == -1 || end == -1 || start >= end) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
String line = yamlLine
|
String entryTxt = yamlLine.substring(start + 1, end).replace(" ", "");
|
||||||
.replaceAll("#.+$", "")
|
|
||||||
.replace('-', ' ')
|
|
||||||
.replace('[', ' ')
|
|
||||||
.replace(']', ' ')
|
|
||||||
.replace(" ", "");
|
|
||||||
|
|
||||||
return new ArrayList<>(Arrays.asList(line.split(",")));
|
ArrayList<String> entry = new ArrayList<>();
|
||||||
|
int last = 0, len = entryTxt.length();
|
||||||
|
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
if (entryTxt.charAt(i) == ',') {
|
||||||
|
entry.add(entryTxt.substring(last, i));
|
||||||
|
last = i + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (last < len) {
|
||||||
|
entry.add(entryTxt.substring(last));
|
||||||
|
}
|
||||||
|
|
||||||
|
return entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public String getDictionaryFile() {
|
public String getDictionaryFile() {
|
||||||
return languagesDir + "/dictionaries/" + dictionaryFile;
|
return LANGUAGES_DIR + "/dictionaries/" + dictionaryFile;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue