Skip to content

Commit

Permalink
allow users to select dictionaries
Browse files Browse the repository at this point in the history
openboard-team/openboard#569
openboard-team/openboard#578
modified so dictionaries use the correct type instead of always main
  • Loading branch information
Helium314 committed Jun 26, 2023
1 parent 62e55fd commit 29c2520
Show file tree
Hide file tree
Showing 62 changed files with 502 additions and 16 deletions.
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ Plan / to do:
* ~upgrade dependencies~
* upgrade NDK, https://github.com/openboard-team/openboard/issues/782
* maybe: rename (package, app, icon), so it can be installed parallel to OpenBoard, and published on F-Droid
* user-selectable dictionaries, https://github.com/openboard-team/openboard/pull/578
* make additional dictionaries available for download, and link from app
* ~user-selectable dictionaries, https://github.com/openboard-team/openboard/pull/578~
* make additional dictionaries available for download (from OpenBoard PRs)
* multi-lingual typing, https://github.com/openboard-team/openboard/pull/593
* suggestion fixes, https://github.com/openboard-team/openboard/pull/694, https://github.com/openboard-team/openboard/issues/795, https://github.com/openboard-team/openboard/issues/660
* improve auto-space insertion, https://github.com/openboard-team/openboard/pull/576
Expand All @@ -23,6 +23,10 @@ Plan / to do:
Changes:
* Updated dependencies
* Debug version can be installed along OpenBoard
* Allow users to add and replace built-in dictionaries
* modified / improved from https://github.com/openboard-team/openboard/pull/569 and https://github.com/openboard-team/openboard/pull/578
* dictionaries are available at https://github.com/Helium314/openboard/dictionaries/dict
* dictionary files starting with "main_" replace the built-in dictionary for the language, all other names work as add-on dictionaries

-----

Expand Down
3 changes: 2 additions & 1 deletion app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ android {

ndkVersion '23.2.8568313'
androidResources {
noCompress 'dict'
noCompress 'main.dict'
noCompress 'empty.dict'
}
}

Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import android.content.res.AssetFileDescriptor;
import android.util.Log;

import org.dslul.openboard.inputmethod.latin.common.FileUtils;
import org.dslul.openboard.inputmethod.latin.common.LocaleUtils;
import org.dslul.openboard.inputmethod.latin.define.DecoderSpecificConstants;
import org.dslul.openboard.inputmethod.latin.makedict.DictionaryHeader;
Expand Down Expand Up @@ -62,6 +63,8 @@ final public class BinaryDictionaryGetter {
public static final String MAIN_DICTIONARY_CATEGORY = "main";
public static final String ID_CATEGORY_SEPARATOR = ":";

public static final String ASSETS_DICTIONARY_FOLDER = "dicts";

// The key considered to read the version attribute in a dictionary file.
private static String VERSION_KEY = "version";

Expand Down Expand Up @@ -170,8 +173,8 @@ public static File[] getCachedWordLists(final String locale, final Context conte
for (File directory : directoryList) {
if (!directory.isDirectory()) continue;
final String dirLocale =
DictionaryInfoUtils.getWordListIdFromFileName(directory.getName());
final int matchLevel = LocaleUtils.getMatchLevel(dirLocale, locale);
DictionaryInfoUtils.getWordListIdFromFileName(directory.getName()).toLowerCase(Locale.ENGLISH);
final int matchLevel = LocaleUtils.getMatchLevel(dirLocale, locale.toLowerCase(Locale.ENGLISH));
if (LocaleUtils.isMatch(matchLevel)) {
final File[] wordLists = directory.listFiles();
if (null != wordLists) {
Expand Down Expand Up @@ -265,14 +268,92 @@ public static ArrayList<AssetFileAddress> getDictionaryFiles(final Locale locale
}

if (!foundMainDict && dictPackSettings.isWordListActive(mainDictId)) {
final int fallbackResId =
DictionaryInfoUtils.getMainDictionaryResourceId(context.getResources(), locale);
final AssetFileAddress fallbackAsset = loadFallbackResource(context, fallbackResId);
final File dict = loadDictionaryFromAssets(locale.toString(), context);
final AssetFileAddress fallbackAsset;
if (dict == null) {
// fall back to the old way (maybe remove? will not work if files are compressed)
final int fallbackResId =
DictionaryInfoUtils.getMainDictionaryResourceId(context.getResources(), locale);
fallbackAsset = loadFallbackResource(context, fallbackResId);
} else {
fallbackAsset = AssetFileAddress.makeFromFileName(dict.getPath());
}
if (null != fallbackAsset) {
fileList.add(fallbackAsset);
}
}

return fileList;
}

/**
* Returns the best matching main dictionary from assets.
*
* Actually copies the dictionary to cache folder, and then returns that file. This allows
* the dictionaries to be stored in a compressed way, reducing APK size.
* On next load, the dictionary in cache folder is found by getCachedWordLists
*
* Returns null on IO errors or if no matching dictionary is found
*/
public static File loadDictionaryFromAssets(final String locale, final Context context) {
final String[] dictionaryList = getAssetsDictionaryList(context);
if (null == dictionaryList) return null;
String bestMatchName = null;
int bestMatchLevel = 0;
for (String dictionary : dictionaryList) {
final String dictLocale =
extractLocaleFromAssetsDictionaryFile(dictionary);
if (dictLocale == null) continue;
// assets files may contain the locale in lowercase, but dictionary headers usually
// have an upper case country code, so we compare lowercase here
final int matchLevel = LocaleUtils.getMatchLevel(dictLocale.toLowerCase(Locale.ENGLISH), locale.toLowerCase(Locale.ENGLISH));
if (LocaleUtils.isMatch(matchLevel) && matchLevel > bestMatchLevel) {
bestMatchName = dictionary;
}
}
if (bestMatchName == null) return null;

// we have a match, now copy contents of the dictionary to cached word lists folder
final String bestMatchLocale = extractLocaleFromAssetsDictionaryFile(bestMatchName);
if (bestMatchLocale == null) return null;
File dictFile = new File(DictionaryInfoUtils.getCacheDirectoryForLocale(bestMatchLocale, context) +
File.separator + DictionaryInfoUtils.getMainDictFilename(bestMatchLocale));
try {
FileUtils.copyStreamToNewFile(
context.getAssets().open(ASSETS_DICTIONARY_FOLDER + File.separator + bestMatchName),
dictFile);
return dictFile;
} catch (IOException e) {
Log.e(TAG, "exception while looking for locale " + locale, e);
return null;
}
}

/**
* Returns the locale for a dictionary file name stored in assets.
*
* Assumes file name main_[locale].dict
*
* Returns the locale, or null if file name does not match the pattern
*/
public static String extractLocaleFromAssetsDictionaryFile(final String dictionaryFileName) {
if (dictionaryFileName.startsWith(DictionaryInfoUtils.MAIN_DICT_PREFIX)
&& dictionaryFileName.endsWith(".dict")) {
return dictionaryFileName.substring(
DictionaryInfoUtils.MAIN_DICT_PREFIX.length(),
dictionaryFileName.lastIndexOf('.')
);
}
return null;
}

public static String[] getAssetsDictionaryList(final Context context) {
final String[] dictionaryList;
try {
dictionaryList = context.getAssets().list(ASSETS_DICTIONARY_FOLDER);
} catch (IOException e) {
return null;
}
return dictionaryList;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@
package org.dslul.openboard.inputmethod.latin.common;

import java.io.File;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;

/**
* A simple class to help with removing directories recursively.
Expand Down Expand Up @@ -58,4 +61,19 @@ public static boolean renameTo(final File fromFile, final File toFile) {
toFile.delete();
return fromFile.renameTo(toFile);
}

public static void copyStreamToNewFile(InputStream in, File outfile) throws IOException {
File parentFile = outfile.getParentFile();
if (parentFile == null || (!parentFile.exists() && !parentFile.mkdirs())) {
throw new IOException("could not create parent folder");
}
FileOutputStream out = new FileOutputStream(outfile);
byte[] buf = new byte[1024];
int len;
while ((len = in.read(buf)) > 0) {
out.write(buf, 0, len);
}
out.flush();
}

}

0 comments on commit 29c2520

Please sign in to comment.