change to one secondary locale per keyboard, improve settings screen

This commit is contained in:
Helium 2022-03-21 00:20:16 +01:00
parent 8183de7068
commit 99260a0aa8
7 changed files with 194 additions and 104 deletions

View file

@ -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<String> locales = new ArrayList<String>(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<String> getAvailableDictionaryLocalesForScript(int script) {
final Set<String> 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;
}
}

View file

@ -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<InputMethodSubtype> 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<String> 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<String> 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<String> 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<String> 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;
}
}

View file

@ -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<String> 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) {

View file

@ -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<Locale, Locale> 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() {

View file

@ -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) {

View file

@ -189,9 +189,11 @@
<!-- Description for "secondary_locale" option. -->
<string name="secondary_locale_summary">Select a secondary dictionary to use alongside main language</string>
<!-- Text when no secondary locale chosen -->
<string name="select_main_locale">Select keyboard for secondary dictionary</string>
<!-- Text when no secondary locale chosen -->
<string name="secondary_locale_none">None</string>
<!-- Message shown when no secondary locales available -->
<string name="no_secondary_locales">No secondary locales available</string>
<string name="no_secondary_locales">No secondary dictionaries available</string>
<!-- Description for "space_trackpad" option. -->
<string name="space_trackpad_summary">Swipe on the spacebar to move the cursor</string>
<!-- Preferences item for disabling word learning -->

View file

@ -76,8 +76,9 @@
android:summary="@string/delete_swipe_summary"
android:defaultValue="true" />
<Preference
android:key="pref_secondary_locale"
<PreferenceScreen
android:fragment="org.dslul.openboard.inputmethod.latin.settings.SecondaryLocaleSettingsFragment"
android:key="pref_secondary_locales"
android:title="@string/secondary_locale"
android:summary="@string/secondary_locale_summary" />