diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index ab9f2726c..8092e3845 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -111,6 +111,22 @@
+
+
+
+
+
+
+
cachedDictionaryFile.delete() }
- .setPositiveButton(R.string.dictionary_file_wrong_locale_ok) { _, _ ->
- addDictAndAskToReplace(newHeader)
- }
- .show()
- return
- }
- addDictAndAskToReplace(newHeader)
- }
-
- private fun addDictAndAskToReplace(header: DictionaryHeader) {
- val dictionaryType = header.mIdString.substringBefore(":")
- val dictFilename = DictionaryInfoUtils.getCacheDirectoryForLocale(mainLocaleString, context) +
- File.separator + dictionaryType + "_" + USER_DICTIONARY_SUFFIX
- val dictFile = File(dictFilename)
-
- fun moveDict(replaced: Boolean) {
- if (!cachedDictionaryFile.renameTo(dictFile)) {
- return onDictionaryLoadingError(R.string.dictionary_load_error)
- }
- if (dictionaryType == DictionaryInfoUtils.DEFAULT_MAIN_DICT) {
- // replaced main dict, remove the one created from internal data
- // todo: currently not, see also BinaryDictionaryGetter.getDictionaryFiles
-// val internalMainDictFilename = DictionaryInfoUtils.getCacheDirectoryForLocale(mainLocaleString, context) +
-// File.separator + DictionaryInfoUtils.getMainDictFilename(mainLocaleString)
-// File(internalMainDictFilename).delete()
- }
- val newDictBroadcast = Intent(DictionaryPackConstants.NEW_DICTIONARY_INTENT_ACTION)
- fragment?.activity?.sendBroadcast(newDictBroadcast)
+ NewDictionaryAdder(context) { replaced, dictFile ->
if (!replaced)
addDictionaryToView(dictFile, view.findViewById(R.id.dictionaries))
- }
-
- if (!dictFile.exists()) {
- return moveDict(false)
- }
- confirmDialog(context, context.getString(R.string.replace_dictionary_message, dictionaryType), context.getString(
- R.string.replace_dictionary)) {
- moveDict(true)
- }
- }
-
- private fun onDictionaryLoadingError(messageId: Int) = onDictionaryLoadingError(context.getString(messageId))
-
- private fun onDictionaryLoadingError(message: String) {
- cachedDictionaryFile.delete()
- Toast.makeText(context, message, Toast.LENGTH_LONG).show()
+ }.addDictionary(uri, mainLocale)
}
private fun addDictionaryToView(dictFile: File, dictionariesView: LinearLayout) {
@@ -395,5 +319,4 @@ private fun getAvailableDictionaryLocales(context: Context, mainLocaleString: St
return locales
}
-private fun String.toLocale() = LocaleUtils.constructLocaleFromString(this)
private const val DICTIONARY_URL = "https://codeberg.org/Helium314/aosp-dictionaries"
diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/latin/settings/SettingsActivity.java b/app/src/main/java/org/dslul/openboard/inputmethod/latin/settings/SettingsActivity.java
index 8ceda811b..a27c0b146 100644
--- a/app/src/main/java/org/dslul/openboard/inputmethod/latin/settings/SettingsActivity.java
+++ b/app/src/main/java/org/dslul/openboard/inputmethod/latin/settings/SettingsActivity.java
@@ -23,6 +23,7 @@ import android.preference.PreferenceActivity;
import org.dslul.openboard.inputmethod.latin.permissions.PermissionsManager;
import org.dslul.openboard.inputmethod.latin.utils.FragmentUtils;
+import org.dslul.openboard.inputmethod.latin.utils.NewDictionaryAdder;
import androidx.core.app.ActivityCompat;
@@ -46,6 +47,10 @@ public final class SettingsActivity extends PreferenceActivity
actionBar.setDisplayHomeAsUpEnabled(true);
actionBar.setHomeButtonEnabled(true);
}
+ final Intent i = getIntent();
+ if (Intent.ACTION_VIEW.equals(i.getAction()) && i.getData() != null) {
+ new NewDictionaryAdder(this, null).addDictionary(i.getData(), null);
+ }
}
@Override
diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/latin/settings/SubtypeSettings.kt b/app/src/main/java/org/dslul/openboard/inputmethod/latin/settings/SubtypeSettings.kt
index f84da9727..a628bae46 100644
--- a/app/src/main/java/org/dslul/openboard/inputmethod/latin/settings/SubtypeSettings.kt
+++ b/app/src/main/java/org/dslul/openboard/inputmethod/latin/settings/SubtypeSettings.kt
@@ -133,6 +133,16 @@ fun getSystemLocales(): List {
return systemLocales
}
+fun hasMatchingSubtypeForLocaleString(localeString: String): Boolean {
+ require(initialized)
+ return !resourceSubtypesByLocale[localeString].isNullOrEmpty()
+}
+
+fun getAvailableSubtypeLocaleStrings(): Collection {
+ require(initialized)
+ return resourceSubtypesByLocale.keys
+}
+
fun init(context: Context) {
if (initialized) return
SubtypeLocaleUtils.init(context) // necessary to get the correct getKeyboardLayoutSetName
diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/latin/utils/NewDictionaryAdder.kt b/app/src/main/java/org/dslul/openboard/inputmethod/latin/utils/NewDictionaryAdder.kt
new file mode 100644
index 000000000..41084626a
--- /dev/null
+++ b/app/src/main/java/org/dslul/openboard/inputmethod/latin/utils/NewDictionaryAdder.kt
@@ -0,0 +1,143 @@
+package org.dslul.openboard.inputmethod.latin.utils
+
+import android.app.AlertDialog
+import android.content.Context
+import android.content.Intent
+import android.net.Uri
+import android.widget.Toast
+import org.dslul.openboard.inputmethod.dictionarypack.DictionaryPackConstants
+import org.dslul.openboard.inputmethod.latin.R
+import org.dslul.openboard.inputmethod.latin.common.FileUtils
+import org.dslul.openboard.inputmethod.latin.common.LocaleUtils
+import org.dslul.openboard.inputmethod.latin.makedict.DictionaryHeader
+import org.dslul.openboard.inputmethod.latin.settings.*
+import java.io.File
+import java.io.IOException
+import java.util.*
+
+class NewDictionaryAdder(private val context: Context, private val onAdded: ((Boolean, File) -> Unit)?) {
+ private val cachedDictionaryFile = File(context.cacheDir.path + File.separator + "temp_dict")
+
+ fun addDictionary(uri: Uri?, mainLocale: Locale?) {
+ if (uri == null)
+ return onDictionaryLoadingError(R.string.dictionary_load_error)
+
+ cachedDictionaryFile.delete()
+ try {
+ val i = context.contentResolver.openInputStream(uri)
+ FileUtils.copyStreamToNewFile(
+ i,
+ cachedDictionaryFile
+ )
+ } catch (e: IOException) {
+ return onDictionaryLoadingError(R.string.dictionary_load_error)
+ }
+
+ val newHeader = DictionaryInfoUtils.getDictionaryFileHeaderOrNull(cachedDictionaryFile, 0, cachedDictionaryFile.length())
+ ?: return onDictionaryLoadingError(R.string.dictionary_file_error)
+ val locale = newHeader.mLocaleString.toLocale()
+
+ if (mainLocale == null) {
+ val localeName = LocaleUtils.getLocaleDisplayNameInSystemLocale(locale, context)
+ val message = context.getString(R.string.add_new_dictionary_ask_locale,
+ newHeader.mIdString.substringBefore(":"),
+ localeName
+ )
+ val b = AlertDialog.Builder(context)
+ .setTitle(R.string.add_new_dictionary_title)
+ .setMessage(message)
+ .setNeutralButton(android.R.string.cancel) { _, _ -> cachedDictionaryFile.delete() }
+ .setNegativeButton(R.string.button_select_language) { _, _ -> selectLocaleForDictionary(newHeader, locale) }
+ if (hasMatchingSubtypeForLocaleString(locale.toString())) {
+ val buttonText = context.getString(R.string.button_add_to_language, localeName)
+ b.setPositiveButton(buttonText) { _, _ ->
+ addDictAndAskToReplace(newHeader, locale)
+ }
+ }
+ b.show()
+ return
+ }
+
+ // ScriptUtils.getScriptFromSpellCheckerLocale may return latin when it should not,
+ // e.g. for Persian or Chinese. But at least fail when dictionary certainly is incompatible
+ if (ScriptUtils.getScriptFromSpellCheckerLocale(locale) != ScriptUtils.getScriptFromSpellCheckerLocale(mainLocale))
+ return onDictionaryLoadingError(R.string.dictionary_file_wrong_script)
+
+ if (locale != mainLocale) {
+ val message = context.resources.getString(
+ R.string.dictionary_file_wrong_locale,
+ LocaleUtils.getLocaleDisplayNameInSystemLocale(locale, context),
+ LocaleUtils.getLocaleDisplayNameInSystemLocale(mainLocale, context)
+ )
+ AlertDialog.Builder(context)
+ .setMessage(message)
+ .setNegativeButton(android.R.string.cancel) { _, _ -> cachedDictionaryFile.delete() }
+ .setPositiveButton(R.string.dictionary_file_wrong_locale_ok) { _, _ ->
+ addDictAndAskToReplace(newHeader, mainLocale)
+ }
+ .show()
+ return
+ }
+ addDictAndAskToReplace(newHeader, mainLocale)
+ }
+
+ private fun selectLocaleForDictionary(newHeader: DictionaryHeader, dictLocale: Locale) {
+ val localeStrings = getAvailableSubtypeLocaleStrings()
+ val locales = linkedSetOf()
+ localeStrings.forEach {
+ if (it.substringBefore("_") == dictLocale.language)
+ locales.add(it.toLocale())
+ }
+ localeStrings.forEach { locales.add(it.toLocale()) }
+ val displayNamesArray = locales.map { LocaleUtils.getLocaleDisplayNameInSystemLocale(it, context) }.toTypedArray()
+ AlertDialog.Builder(context)
+ .setTitle(R.string.button_select_language)
+ .setItems(displayNamesArray) { di, i ->
+ di.dismiss()
+ locales.forEachIndexed { index, locale ->
+ if (index == i)
+ addDictAndAskToReplace(newHeader, locale)
+ }
+ }
+ .setNegativeButton(android.R.string.cancel) { _, _ -> cachedDictionaryFile.delete() }
+ .show()
+ }
+
+ private fun addDictAndAskToReplace(header: DictionaryHeader, mainLocale: Locale) {
+ val dictionaryType = header.mIdString.substringBefore(":")
+ val dictFilename = DictionaryInfoUtils.getCacheDirectoryForLocale(mainLocale.toString(), context) +
+ File.separator + dictionaryType + "_" + USER_DICTIONARY_SUFFIX
+ val dictFile = File(dictFilename)
+
+ fun moveDict(replaced: Boolean) {
+ if (!cachedDictionaryFile.renameTo(dictFile)) {
+ return onDictionaryLoadingError(R.string.dictionary_load_error)
+ }
+ if (dictionaryType == DictionaryInfoUtils.DEFAULT_MAIN_DICT) {
+ // replaced main dict, remove the one created from internal data
+ // todo: currently not, see also BinaryDictionaryGetter.getDictionaryFiles
+// val internalMainDictFilename = DictionaryInfoUtils.getCacheDirectoryForLocale(mainLocaleString, context) +
+// File.separator + DictionaryInfoUtils.getMainDictFilename(mainLocaleString)
+// File(internalMainDictFilename).delete()
+ }
+ val newDictBroadcast = Intent(DictionaryPackConstants.NEW_DICTIONARY_INTENT_ACTION)
+ context.sendBroadcast(newDictBroadcast)
+ onAdded?.let { it(replaced, dictFile) }
+ }
+
+ if (!dictFile.exists()) {
+ return moveDict(false)
+ }
+ confirmDialog(context, context.getString(R.string.replace_dictionary_message, dictionaryType), context.getString(
+ R.string.replace_dictionary)) {
+ moveDict(true)
+ }
+ }
+
+ private fun onDictionaryLoadingError(messageId: Int) {
+ cachedDictionaryFile.delete()
+ Toast.makeText(context, messageId, Toast.LENGTH_LONG).show()
+ }
+}
+
+fun String.toLocale() = LocaleUtils.constructLocaleFromString(this)
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index bdedc77fe..a41992cef 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -491,12 +491,18 @@ disposition rather than other common dispositions for Latin languages. [CHAR LIM
Built-in dictionary
"Add dictionary from file"
+
+ "To which language should the dictionary \"%1$s\" for %2$s be added?"
+
+ "Select language"
+
+ "Add to %s"
"Really replace user-added dictionary \"%s\"?"
"Replace dictionary"
- "Really remove user-added dictionary \"%1$s\"?"
+ "Really remove user-added dictionary \"%s\"?"
"Select a dictionary to add. Dictionaries in .dict format can be downloaded %s."