diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/latin/settings/AdvancedSettingsFragment.java b/app/src/main/java/org/dslul/openboard/inputmethod/latin/settings/AdvancedSettingsFragment.java index 2944e6b0b..7338b3e0e 100644 --- a/app/src/main/java/org/dslul/openboard/inputmethod/latin/settings/AdvancedSettingsFragment.java +++ b/app/src/main/java/org/dslul/openboard/inputmethod/latin/settings/AdvancedSettingsFragment.java @@ -16,35 +16,14 @@ package org.dslul.openboard.inputmethod.latin.settings; -import android.app.AlertDialog; import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; import android.content.SharedPreferences; import android.content.res.Resources; import android.os.Bundle; -import android.os.LocaleList; -import android.preference.Preference; -import android.util.ArraySet; -import android.util.Log; -import androidx.core.os.LocaleListCompat; - -import org.dslul.openboard.inputmethod.dictionarypack.DictionaryPackConstants; import org.dslul.openboard.inputmethod.latin.AudioAndHapticFeedbackManager; import org.dslul.openboard.inputmethod.latin.R; import org.dslul.openboard.inputmethod.latin.SystemBroadcastReceiver; -import org.dslul.openboard.inputmethod.latin.common.LocaleUtils; -import org.dslul.openboard.inputmethod.latin.utils.DialogUtils; -import org.dslul.openboard.inputmethod.latin.utils.DictionaryInfoUtils; -import org.dslul.openboard.inputmethod.latin.utils.ScriptUtils; - -import java.io.File; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Locale; -import java.util.Set; /** * "Advanced" settings sub screen. @@ -78,16 +57,6 @@ public final class AdvancedSettingsFragment extends SubScreenFragment { } setupKeyLongpressTimeoutSettings(); - - final Preference setSecondaryLocale = findPreference("pref_secondary_locale"); - if (setSecondaryLocale != null) - setSecondaryLocale.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { - @Override - public boolean onPreferenceClick(Preference preference) { - showSecondaryLocaleDialog(); - return true; - } - }); } @@ -136,67 +105,4 @@ public final class AdvancedSettingsFragment extends SubScreenFragment { SystemBroadcastReceiver.toggleAppIcon(getActivity()); } } - - private void showSecondaryLocaleDialog() { - // only allow same script of main locale for now - // TODO: how to get enabled keyboard locales? then setting a secondary locale per - // main locale would be rather simple - final SettingsValues settingsValues = Settings.getInstance().getCurrent(); - final int scriptOfCurrentLocale = ScriptUtils.getScriptFromSpellCheckerLocale(settingsValues.mLocale); - final List locales = new ArrayList(getAvailableDictionaryLocalesForScript(scriptOfCurrentLocale)); - final AlertDialog.Builder builder = new AlertDialog.Builder( - DialogUtils.getPlatformDialogThemeContext(getActivity())) - .setTitle(R.string.select_language) - .setPositiveButton(android.R.string.ok, null); - - if (locales.isEmpty()) { - builder.setMessage(R.string.no_secondary_locales) - .show(); - return; - } - - // add "no secondary language" option - locales.add(getResources().getString(R.string.secondary_locale_none)); - - final CharSequence[] titles = locales.toArray(new CharSequence[0]); - for (int i = 0; i < titles.length - 1 ; i++) { - final Locale loc = LocaleUtils.constructLocaleFromString(titles[i].toString()); - titles[i] = loc.getDisplayLanguage(settingsValues.mLocale); - } - - Locale currentSecondaryLocale = settingsValues.mSecondaryLocale; - int checkedItem; - if (currentSecondaryLocale == null) - checkedItem = locales.size() - 1; - else - checkedItem = locales.indexOf(currentSecondaryLocale.toString()); - - builder.setSingleChoiceItems(titles, checkedItem, (dialogInterface, i) -> { - String locale = locales.get(i); - if (i == locales.size() - 1) - locale = ""; - getSharedPreferences().edit().putString(Settings.PREF_SECONDARY_LOCALE, locale).apply(); - final Intent newDictBroadcast = new Intent(DictionaryPackConstants.NEW_DICTIONARY_INTENT_ACTION); - getActivity().sendBroadcast(newDictBroadcast); - }); - - builder.show(); - } - - private Set getAvailableDictionaryLocalesForScript(int script) { - final Set locales = new HashSet<>(); - // TODO: get from assets - // need merged "compress" PR - // filter by script! - final File[] directoryList = DictionaryInfoUtils.getCachedDirectoryList(getActivity()); - for (File directory : directoryList) { - if (!directory.isDirectory()) continue; - final String dirLocale = - DictionaryInfoUtils.getWordListIdFromFileName(directory.getName()); - final Locale locale = LocaleUtils.constructLocaleFromString(dirLocale); - if (ScriptUtils.getScriptFromSpellCheckerLocale(locale) != script) continue; - locales.add(locale.toString()); - } - return locales; - } } diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/latin/settings/SecondaryLocaleSettingsFragment.java b/app/src/main/java/org/dslul/openboard/inputmethod/latin/settings/SecondaryLocaleSettingsFragment.java new file mode 100644 index 000000000..ce3a3f635 --- /dev/null +++ b/app/src/main/java/org/dslul/openboard/inputmethod/latin/settings/SecondaryLocaleSettingsFragment.java @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.dslul.openboard.inputmethod.latin.settings; + +import android.app.AlertDialog; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.preference.Preference; +import android.view.inputmethod.InputMethodSubtype; + +import org.dslul.openboard.inputmethod.dictionarypack.DictionaryPackConstants; +import org.dslul.openboard.inputmethod.latin.BuildConfig; +import org.dslul.openboard.inputmethod.latin.R; +import org.dslul.openboard.inputmethod.latin.RichInputMethodManager; +import org.dslul.openboard.inputmethod.latin.common.LocaleUtils; +import org.dslul.openboard.inputmethod.latin.utils.DialogUtils; +import org.dslul.openboard.inputmethod.latin.utils.DictionaryInfoUtils; +import org.dslul.openboard.inputmethod.latin.utils.ScriptUtils; + +import java.io.File; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.Set; + +public final class SecondaryLocaleSettingsFragment extends SubScreenFragment { + private RichInputMethodManager mRichImm; + private SharedPreferences mPrefs; + + @Override + public void onCreate(final Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + RichInputMethodManager.init(getActivity()); + mRichImm = RichInputMethodManager.getInstance(); + addPreferencesFromResource(R.xml.additional_subtype_settings); + resetKeyboardLocales(); + } + + private void resetKeyboardLocales() { + getPreferenceScreen().removeAll(); + final Context context = getActivity(); + List subtypes = mRichImm.getMyEnabledInputMethodSubtypeList(false); + + for (InputMethodSubtype subtype : subtypes) { + final Locale secondaryLocale = Settings.getSecondaryLocale(getSharedPreferences(), subtype.getLocale()); + final Preference pref = new Preference(context); + pref.setTitle(subtype.getDisplayName(context, BuildConfig.APPLICATION_ID, context.getApplicationInfo())); + if (secondaryLocale != null) + pref.setSummary(secondaryLocale.getDisplayLanguage(getResources().getConfiguration().locale)); + + pref.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { + @Override + public boolean onPreferenceClick(Preference preference) { + showSecondaryLocaleDialog(subtype.getLocale(), subtype.isAsciiCapable()); + return true; + } + }); + getPreferenceScreen().addPreference(pref); + } + + } + + private void showSecondaryLocaleDialog(String mainLocale, boolean asciiCapable) { + final SettingsValues settingsValues = Settings.getInstance().getCurrent(); + // TODO: does this really return latin for persian? might need that huge map from somewhere github + // or maybe extend the script map, also with exception for sr_ZZ + // but: could this break other things? + + final List locales = new ArrayList<>(getAvailableDictionaryLocales(mainLocale, asciiCapable)); + final AlertDialog.Builder builder = new AlertDialog.Builder( + DialogUtils.getPlatformDialogThemeContext(getActivity())) + .setTitle(R.string.language_selection_title) + .setPositiveButton(android.R.string.ok, null); + + if (locales.isEmpty()) { + builder.setMessage(R.string.no_secondary_locales) + .show(); + return; + } + + // add "no secondary language" option + locales.add(getResources().getString(R.string.secondary_locale_none)); + + final CharSequence[] titles = locales.toArray(new CharSequence[0]); + for (int i = 0; i < titles.length - 1 ; i++) { + final Locale loc = LocaleUtils.constructLocaleFromString(titles[i].toString()); + titles[i] = loc.getDisplayLanguage(settingsValues.mLocale); + } + + Locale currentSecondaryLocale = settingsValues.mSecondaryLocale; + int checkedItem; + if (currentSecondaryLocale == null) + checkedItem = locales.size() - 1; + else + checkedItem = locales.indexOf(currentSecondaryLocale.toString()); + + builder.setSingleChoiceItems(titles, checkedItem, (dialogInterface, i) -> { + String locale = locales.get(i); + if (i == locales.size() - 1) + locale = ""; + final Set encodedLocales = new HashSet<>(); + boolean updated = false; + for (String encodedLocale : getSharedPreferences().getStringSet(Settings.PREF_SECONDARY_LOCALES, new HashSet<>())) { + String[] locs = encodedLocale.split("§"); + if (locs.length == 2 && locs[0].equals(mainLocale)) { + if (!locale.isEmpty()) + encodedLocales.add(mainLocale + "§" + locale); + updated = true; + } else { + encodedLocales.add(encodedLocale); + } + } + if (!updated) + encodedLocales.add(mainLocale + "§" + locale); + getSharedPreferences().edit().putStringSet(Settings.PREF_SECONDARY_LOCALES, encodedLocales).apply(); + // getSharedPreferences().edit().putString(Settings.PREF_SECONDARY_LOCALE, locale).apply(); + final Intent newDictBroadcast = new Intent(DictionaryPackConstants.NEW_DICTIONARY_INTENT_ACTION); + getActivity().sendBroadcast(newDictBroadcast); + resetKeyboardLocales(); + }); + + builder.show(); + } + + // get locales with same script as main locale, but different language + private Set getAvailableDictionaryLocales(String mainLocale, boolean asciiCapable) { + final Locale mainL = LocaleUtils.constructLocaleFromString(mainLocale); + final int mainScript; + if (asciiCapable) + mainScript = ScriptUtils.SCRIPT_LATIN; + else + mainScript = ScriptUtils.getScriptFromSpellCheckerLocale(mainL); + + final Set locales = new HashSet<>(); + + // TODO: also get locales from assets + // need merged "compress" PR + final File[] directoryList = DictionaryInfoUtils.getCachedDirectoryList(getActivity()); + for (File directory : directoryList) { + if (!directory.isDirectory()) continue; + final String dirLocale = + DictionaryInfoUtils.getWordListIdFromFileName(directory.getName()); + if (dirLocale.equals(mainLocale)) continue; + final Locale locale = LocaleUtils.constructLocaleFromString(dirLocale); + if (locale.getLanguage().equals(mainL.getLanguage())) continue; + int localeScript = ScriptUtils.getScriptFromSpellCheckerLocale(locale); + if (localeScript != mainScript) continue; + locales.add(locale.toString()); + } + return locales; + } + +} diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/latin/settings/Settings.java b/app/src/main/java/org/dslul/openboard/inputmethod/latin/settings/Settings.java index 65c1bc5b3..036db5822 100644 --- a/app/src/main/java/org/dslul/openboard/inputmethod/latin/settings/Settings.java +++ b/app/src/main/java/org/dslul/openboard/inputmethod/latin/settings/Settings.java @@ -36,9 +36,11 @@ import org.dslul.openboard.inputmethod.latin.utils.DeviceProtectedUtils; import org.dslul.openboard.inputmethod.latin.utils.JniUtils; import org.dslul.openboard.inputmethod.latin.utils.ResourceUtils; import org.dslul.openboard.inputmethod.latin.utils.RunInLocale; +import org.dslul.openboard.inputmethod.latin.utils.ScriptUtils; import org.dslul.openboard.inputmethod.latin.utils.StatsUtils; import java.util.Collections; +import java.util.HashSet; import java.util.Locale; import java.util.Set; import java.util.concurrent.locks.ReentrantLock; @@ -127,7 +129,7 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang public static final String PREF_ENABLE_CLIPBOARD_HISTORY = "pref_enable_clipboard_history"; public static final String PREF_CLIPBOARD_HISTORY_RETENTION_TIME = "pref_clipboard_history_retention_time"; - public static final String PREF_SECONDARY_LOCALE = "pref_secondary_locale"; + public static final String PREF_SECONDARY_LOCALES = "pref_secondary_locales"; // This preference key is deprecated. Use {@link #PREF_SHOW_LANGUAGE_SWITCH_KEY} instead. // This is being used only for the backward compatibility. @@ -512,11 +514,15 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang return prefs.getInt(PREF_LAST_SHOWN_EMOJI_CATEGORY_PAGE_ID, defValue); } - public static Locale readSecondaryLocale(final SharedPreferences prefs) { - final String localeString = prefs.getString(PREF_SECONDARY_LOCALE, ""); - if (localeString.isEmpty()) - return null; - return LocaleUtils.constructLocaleFromString(localeString); + // TODO: test whether this gets updated on keyboard language switch! + public static Locale getSecondaryLocale(final SharedPreferences prefs, final String mainLocaleString) { + final Set encodedLocales = prefs.getStringSet(PREF_SECONDARY_LOCALES, new HashSet<>()); + for (String loc : encodedLocales) { + String[] locales = loc.split("§"); + if (locales.length == 2 && locales[0].equals(mainLocaleString)) + return LocaleUtils.constructLocaleFromString(locales[1]); + } + return null; } private void upgradeAutocorrectionSettings(final SharedPreferences prefs, final Resources res) { diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/latin/settings/SettingsValues.java b/app/src/main/java/org/dslul/openboard/inputmethod/latin/settings/SettingsValues.java index c22ff3a7f..a1cd7c7bc 100644 --- a/app/src/main/java/org/dslul/openboard/inputmethod/latin/settings/SettingsValues.java +++ b/app/src/main/java/org/dslul/openboard/inputmethod/latin/settings/SettingsValues.java @@ -36,6 +36,7 @@ import org.dslul.openboard.inputmethod.latin.utils.TargetPackageInfoGetterTask; import java.util.Arrays; import java.util.Locale; +import java.util.Map; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -85,6 +86,7 @@ public class SettingsValues { public final boolean mOneHandedModeEnabled; public final int mOneHandedModeGravity; public final Locale mSecondaryLocale; +// public final Map mSecondaryLocales; // Use bigrams to predict the next word when there is no input for it yet public final boolean mBigramPredictionEnabled; public final boolean mGestureInputEnabled; @@ -242,7 +244,7 @@ public class SettingsValues { mClipboardHistoryRetentionTime = Settings.readClipboardHistoryRetentionTime(prefs, res); mOneHandedModeEnabled = Settings.readOneHandedModeEnabled(prefs); mOneHandedModeGravity = Settings.readOneHandedModeGravity(prefs); - mSecondaryLocale = Settings.readSecondaryLocale(prefs); + mSecondaryLocale = Settings.getSecondaryLocale(prefs, RichInputMethodManager.getInstance().getCurrentSubtypeLocale().toString()); } public boolean isMetricsLoggingEnabled() { diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/latin/utils/ScriptUtils.java b/app/src/main/java/org/dslul/openboard/inputmethod/latin/utils/ScriptUtils.java index 56a7b5efb..add344661 100644 --- a/app/src/main/java/org/dslul/openboard/inputmethod/latin/utils/ScriptUtils.java +++ b/app/src/main/java/org/dslul/openboard/inputmethod/latin/utils/ScriptUtils.java @@ -201,6 +201,9 @@ public class ScriptUtils { * {@see http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes} */ public static int getScriptFromSpellCheckerLocale(final Locale locale) { + // need special treatment of serbian latin, which would get detected as cyrillic + if (locale.toString().equals("sr_ZZ")) + return ScriptUtils.SCRIPT_LATIN; String language = locale.getLanguage(); Integer script = mLanguageCodeToScriptCode.get(language); if (script == null) { diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ad0281455..7e7241c45 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -189,9 +189,11 @@ Select a secondary dictionary to use alongside main language + Select keyboard for secondary dictionary + None - No secondary locales available + No secondary dictionaries available Swipe on the spacebar to move the cursor diff --git a/app/src/main/res/xml/prefs_screen_advanced.xml b/app/src/main/res/xml/prefs_screen_advanced.xml index 79244ce1e..e964d4eef 100644 --- a/app/src/main/res/xml/prefs_screen_advanced.xml +++ b/app/src/main/res/xml/prefs_screen_advanced.xml @@ -76,8 +76,9 @@ android:summary="@string/delete_swipe_summary" android:defaultValue="true" /> -