mirror of
https://github.com/Helium314/HeliBoard.git
synced 2025-05-18 16:03:12 +00:00
Merge branch 'new' into hangul_update
This commit is contained in:
commit
9c091e7f31
8 changed files with 130 additions and 85 deletions
23
README.md
23
README.md
|
@ -15,12 +15,31 @@ Might end up on F-Droid...
|
||||||
* additional dictionaries for emojis or scientific symbols can be used to provide suggestions ("emoji search")
|
* additional dictionaries for emojis or scientific symbols can be used to provide suggestions ("emoji search")
|
||||||
* note that for Korean layouts, suggestions only work using [this dictionary](https://github.com/openboard-team/openboard/commit/83fca9533c03b9fecc009fc632577226bbd6301f), the tools in the dictionary repository are not able to create working dictionaries
|
* note that for Korean layouts, suggestions only work using [this dictionary](https://github.com/openboard-team/openboard/commit/83fca9533c03b9fecc009fc632577226bbd6301f), the tools in the dictionary repository are not able to create working dictionaries
|
||||||
* Adjust keyboard themes (style and colors)
|
* Adjust keyboard themes (style and colors)
|
||||||
* can follow the system's day/night setting
|
* can follow the system's day/night setting on Android 10+ (and on some versions of Android 9)
|
||||||
* Split keyboard
|
* Split keyboard (if the screen is large enough)
|
||||||
* Number row
|
* Number row
|
||||||
* Number pad
|
* Number pad
|
||||||
* Show all available extra characters on long pressing a key
|
* Show all available extra characters on long pressing a key
|
||||||
|
|
||||||
|
## Hidden functionality
|
||||||
|
Features that may go unnoticed
|
||||||
|
* Long pressing the clipboard key (the optional one in suggestion strip) pastes system clipboard contents
|
||||||
|
* Long-press comma to access clipboard view, emoji view, one-handed mode, settings, or switch language
|
||||||
|
* emoji view and language switch will disappear if you have the corresponding key enabled
|
||||||
|
* for some layouts it's not the comma-key, but the key at the same position (e.g. it's `q` for Dvorak layout)
|
||||||
|
* Sliding key input: swipe from shift to another key to type a single uppercase key
|
||||||
|
* also works for the `?123` key to type a single symbol from the symbols keyboard, and for related keys
|
||||||
|
* Long-press a suggestion to show more suggestions, and a delete button to remove this suggestion
|
||||||
|
* Swipe up from a suggestion to open more suggestions, and release on the suggestion to select it
|
||||||
|
* You can add dictionaries by opening them in a file explorer
|
||||||
|
* only works with content-uris and not with file-uris, meaning that it may not work with some file explorers
|
||||||
|
* Debug APK only
|
||||||
|
* Long-press a suggestion to show the source dictionary
|
||||||
|
* Debug settings in advanced preferences, though not very useful except for dumping dictionaries into the log
|
||||||
|
* When the app crashes, you will be asked whether you want crash logs when you open the settings
|
||||||
|
* For users doing manual backups with root access: starting at Android 7, the shared preferences file is not in the default location, because the app is using [device protected storage](https://developer.android.com/reference/android/content/Context#createDeviceProtectedStorageContext()). This is necessary so the settings can be read before the device is unlocked, e.g. at boot.
|
||||||
|
* file is located in `/data/user_de/0/<package_id>/shared_prefs/`, though this may depend on the device and Android version
|
||||||
|
|
||||||
## Important differences and changes to OpenBoard
|
## Important differences and changes to OpenBoard
|
||||||
* Debug version can be installed along OpenBoard
|
* Debug version can be installed along OpenBoard
|
||||||
* Allow users to add and replace built-in dictionaries
|
* Allow users to add and replace built-in dictionaries
|
||||||
|
|
|
@ -124,6 +124,8 @@ public class KeyboardView extends View {
|
||||||
private Bitmap mOffscreenBuffer;
|
private Bitmap mOffscreenBuffer;
|
||||||
/** Flag for whether the key hints should be displayed */
|
/** Flag for whether the key hints should be displayed */
|
||||||
private boolean mShowsHints;
|
private boolean mShowsHints;
|
||||||
|
/** Scale for downscaling icons and fixed size backgrounds if keyboard height is set below 80% */
|
||||||
|
private float mIconScaleFactor;
|
||||||
/** The canvas for the above mutable keyboard bitmap */
|
/** The canvas for the above mutable keyboard bitmap */
|
||||||
@NonNull
|
@NonNull
|
||||||
private final Canvas mOffscreenCanvas = new Canvas();
|
private final Canvas mOffscreenCanvas = new Canvas();
|
||||||
|
@ -301,6 +303,8 @@ public class KeyboardView extends View {
|
||||||
}
|
}
|
||||||
|
|
||||||
mShowsHints = Settings.getInstance().getCurrent().mShowsHints;
|
mShowsHints = Settings.getInstance().getCurrent().mShowsHints;
|
||||||
|
final float scale = Settings.getInstance().getCurrent().mKeyboardHeightScale;
|
||||||
|
mIconScaleFactor = scale < 0.8f ? scale + 0.2f : scale;
|
||||||
final Paint paint = mPaint;
|
final Paint paint = mPaint;
|
||||||
final Drawable background = getBackground();
|
final Drawable background = getBackground();
|
||||||
// Calculate clip region and set.
|
// Calculate clip region and set.
|
||||||
|
@ -370,8 +374,8 @@ public class KeyboardView extends View {
|
||||||
if (key.needsToKeepBackgroundAspectRatio(mDefaultKeyLabelFlags)
|
if (key.needsToKeepBackgroundAspectRatio(mDefaultKeyLabelFlags)
|
||||||
// HACK: To disable expanding normal/functional key background.
|
// HACK: To disable expanding normal/functional key background.
|
||||||
&& !key.hasCustomActionLabel()) {
|
&& !key.hasCustomActionLabel()) {
|
||||||
bgWidth = background.getIntrinsicWidth();
|
bgWidth = (int) (background.getIntrinsicWidth() * mIconScaleFactor);
|
||||||
bgHeight = background.getIntrinsicHeight();
|
bgHeight = (int) (background.getIntrinsicHeight() * mIconScaleFactor);
|
||||||
bgX = (keyWidth - bgWidth) / 2;
|
bgX = (keyWidth - bgWidth) / 2;
|
||||||
bgY = (keyHeight - bgHeight) / 2;
|
bgY = (keyHeight - bgHeight) / 2;
|
||||||
} else {
|
} else {
|
||||||
|
@ -412,8 +416,9 @@ public class KeyboardView extends View {
|
||||||
labelBaseline = centerY + labelCharHeight / 2.0f;
|
labelBaseline = centerY + labelCharHeight / 2.0f;
|
||||||
|
|
||||||
// Horizontal label text alignment
|
// Horizontal label text alignment
|
||||||
if (key.isAlignLabelOffCenter()) {
|
if (key.isAlignLabelOffCenter() && mShowsHints) {
|
||||||
// The label is placed off center of the key. Used mainly on "phone number" layout.
|
// The label is placed off center of the key. Currently used only on "phone number" layout
|
||||||
|
// to have letter hints shown nicely. We don't want to align it off center if hints are off.
|
||||||
labelX = centerX + params.mLabelOffCenterRatio * labelCharWidth;
|
labelX = centerX + params.mLabelOffCenterRatio * labelCharWidth;
|
||||||
paint.setTextAlign(Align.LEFT);
|
paint.setTextAlign(Align.LEFT);
|
||||||
} else {
|
} else {
|
||||||
|
@ -496,11 +501,11 @@ public class KeyboardView extends View {
|
||||||
if (label == null && icon != null) {
|
if (label == null && icon != null) {
|
||||||
final int iconWidth;
|
final int iconWidth;
|
||||||
if (key.getCode() == Constants.CODE_SPACE && icon instanceof NinePatchDrawable) {
|
if (key.getCode() == Constants.CODE_SPACE && icon instanceof NinePatchDrawable) {
|
||||||
iconWidth = (int)(keyWidth * mSpacebarIconWidthRatio);
|
iconWidth = (int) (keyWidth * mSpacebarIconWidthRatio * mIconScaleFactor);
|
||||||
} else {
|
} else {
|
||||||
iconWidth = Math.min(icon.getIntrinsicWidth(), keyWidth);
|
iconWidth = (int) (Math.min(icon.getIntrinsicWidth(), keyWidth) * mIconScaleFactor);
|
||||||
}
|
}
|
||||||
final int iconHeight = icon.getIntrinsicHeight();
|
final int iconHeight = (int) (icon.getIntrinsicHeight() * mIconScaleFactor);
|
||||||
final int iconY;
|
final int iconY;
|
||||||
if (key.isAlignIconToBottom()) {
|
if (key.isAlignIconToBottom()) {
|
||||||
iconY = keyHeight - iconHeight;
|
iconY = keyHeight - iconHeight;
|
||||||
|
|
|
@ -153,7 +153,7 @@ public class DictionaryFacilitatorImpl implements DictionaryFacilitator {
|
||||||
/**
|
/**
|
||||||
* The locale associated with the dictionary group.
|
* The locale associated with the dictionary group.
|
||||||
*/
|
*/
|
||||||
@Nullable public final Locale mLocale;
|
@NonNull public final Locale mLocale;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The user account associated with the dictionary group.
|
* The user account associated with the dictionary group.
|
||||||
|
@ -202,10 +202,10 @@ public class DictionaryFacilitatorImpl implements DictionaryFacilitator {
|
||||||
new ConcurrentHashMap<>();
|
new ConcurrentHashMap<>();
|
||||||
|
|
||||||
public DictionaryGroup() {
|
public DictionaryGroup() {
|
||||||
this(null /* locale */, null /* mainDict */, null /* account */, Collections.emptyMap() /* subDicts */);
|
this(new Locale(""), null /* mainDict */, null /* account */, Collections.emptyMap() /* subDicts */);
|
||||||
}
|
}
|
||||||
|
|
||||||
public DictionaryGroup(@Nullable final Locale locale,
|
public DictionaryGroup(@NonNull final Locale locale,
|
||||||
@Nullable final Dictionary mainDict,
|
@Nullable final Dictionary mainDict,
|
||||||
@Nullable final String account,
|
@Nullable final String account,
|
||||||
@NonNull final Map<String, ExpandableBinaryDictionary> subDicts) {
|
@NonNull final Map<String, ExpandableBinaryDictionary> subDicts) {
|
||||||
|
@ -288,7 +288,7 @@ public class DictionaryFacilitatorImpl implements DictionaryFacilitator {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isActive() {
|
public boolean isActive() {
|
||||||
return mDictionaryGroups.get(0).mLocale != null;
|
return !mDictionaryGroups.get(0).mLocale.getLanguage().isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
// used in
|
// used in
|
||||||
|
@ -340,12 +340,10 @@ public class DictionaryFacilitatorImpl implements DictionaryFacilitator {
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
static DictionaryGroup findDictionaryGroupWithLocale(final List<DictionaryGroup> dictionaryGroups,
|
static DictionaryGroup findDictionaryGroupWithLocale(final List<DictionaryGroup> dictionaryGroups,
|
||||||
final Locale locale) {
|
@NonNull final Locale locale) {
|
||||||
if (dictionaryGroups == null) return null;
|
if (dictionaryGroups == null) return null;
|
||||||
for (DictionaryGroup dictionaryGroup : dictionaryGroups) {
|
for (DictionaryGroup dictionaryGroup : dictionaryGroups) {
|
||||||
if (locale == null && dictionaryGroup.mLocale == null)
|
if (locale.equals(dictionaryGroup.mLocale))
|
||||||
return dictionaryGroup;
|
|
||||||
if (locale != null && locale.equals(dictionaryGroup.mLocale))
|
|
||||||
return dictionaryGroup;
|
return dictionaryGroup;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
@ -354,7 +352,7 @@ public class DictionaryFacilitatorImpl implements DictionaryFacilitator {
|
||||||
// original
|
// original
|
||||||
public void resetDictionaries(
|
public void resetDictionaries(
|
||||||
final Context context,
|
final Context context,
|
||||||
final Locale newLocale,
|
@NonNull final Locale newLocale,
|
||||||
final boolean useContactsDict,
|
final boolean useContactsDict,
|
||||||
final boolean usePersonalizedDicts,
|
final boolean usePersonalizedDicts,
|
||||||
final boolean forceReloadMainDictionary,
|
final boolean forceReloadMainDictionary,
|
||||||
|
@ -399,10 +397,8 @@ public class DictionaryFacilitatorImpl implements DictionaryFacilitator {
|
||||||
final ArrayList<DictionaryGroup> newDictionaryGroups = new ArrayList<>(allLocales.size());
|
final ArrayList<DictionaryGroup> newDictionaryGroups = new ArrayList<>(allLocales.size());
|
||||||
for (Locale locale : allLocales) {
|
for (Locale locale : allLocales) {
|
||||||
// get existing dictionary group for new locale
|
// get existing dictionary group for new locale
|
||||||
final DictionaryGroup oldDictionaryGroupForLocale =
|
final DictionaryGroup oldDictionaryGroupForLocale = findDictionaryGroupWithLocale(mDictionaryGroups, locale);
|
||||||
findDictionaryGroupWithLocale(mDictionaryGroups, locale);
|
final ArrayList<String> dictTypesToCleanupForLocale = existingDictionariesToCleanup.get(locale);
|
||||||
final ArrayList<String> dictTypesToCleanupForLocale =
|
|
||||||
existingDictionariesToCleanup.get(locale);
|
|
||||||
final boolean noExistingDictsForThisLocale = (null == oldDictionaryGroupForLocale);
|
final boolean noExistingDictsForThisLocale = (null == oldDictionaryGroupForLocale);
|
||||||
|
|
||||||
// create new or re-use already loaded main dict
|
// create new or re-use already loaded main dict
|
||||||
|
@ -542,13 +538,13 @@ public class DictionaryFacilitatorImpl implements DictionaryFacilitator {
|
||||||
final File dictFile = dictionaryFiles.get(dictType);
|
final File dictFile = dictionaryFiles.get(dictType);
|
||||||
final ExpandableBinaryDictionary dict = getSubDict(
|
final ExpandableBinaryDictionary dict = getSubDict(
|
||||||
dictType, context, locale, dictFile, "" /* dictNamePrefix */, account);
|
dictType, context, locale, dictFile, "" /* dictNamePrefix */, account);
|
||||||
|
if (dict == null) {
|
||||||
|
throw new RuntimeException("Unknown dictionary type: " + dictType);
|
||||||
|
}
|
||||||
if (additionalDictAttributes.containsKey(dictType)) {
|
if (additionalDictAttributes.containsKey(dictType)) {
|
||||||
dict.clearAndFlushDictionaryWithAdditionalAttributes(
|
dict.clearAndFlushDictionaryWithAdditionalAttributes(
|
||||||
additionalDictAttributes.get(dictType));
|
additionalDictAttributes.get(dictType));
|
||||||
}
|
}
|
||||||
if (dict == null) {
|
|
||||||
throw new RuntimeException("Unknown dictionary type: " + dictType);
|
|
||||||
}
|
|
||||||
dict.reloadDictionaryIfRequired();
|
dict.reloadDictionaryIfRequired();
|
||||||
dict.waitAllTasksForTests();
|
dict.waitAllTasksForTests();
|
||||||
subDicts.put(dictType, dict);
|
subDicts.put(dictType, dict);
|
||||||
|
@ -939,9 +935,6 @@ public class DictionaryFacilitatorImpl implements DictionaryFacilitator {
|
||||||
if (TextUtils.isEmpty(word)) {
|
if (TextUtils.isEmpty(word)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (dictionaryGroup.mLocale == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (isBlacklisted(word)) return false;
|
if (isBlacklisted(word)) return false;
|
||||||
for (final String dictType : dictionariesToCheck) {
|
for (final String dictType : dictionariesToCheck) {
|
||||||
final Dictionary dictionary = dictionaryGroup.getDict(dictType);
|
final Dictionary dictionary = dictionaryGroup.getDict(dictType);
|
||||||
|
@ -992,7 +985,8 @@ public class DictionaryFacilitatorImpl implements DictionaryFacilitator {
|
||||||
} else isInContacts = false;
|
} else isInContacts = false;
|
||||||
|
|
||||||
// add to blacklist if in main or contacts dictionaries
|
// add to blacklist if in main or contacts dictionaries
|
||||||
if ((isInContacts || group.getDict(Dictionary.TYPE_MAIN).isValidWord(word)) && group.blacklist.add(word)) {
|
if ((isInContacts || (group.hasDict(Dictionary.TYPE_MAIN, null) && group.getDict(Dictionary.TYPE_MAIN).isValidWord(word)))
|
||||||
|
&& group.blacklist.add(word)) {
|
||||||
// write to file if word wasn't already in blacklist
|
// write to file if word wasn't already in blacklist
|
||||||
ExecutorUtils.getBackgroundExecutor(ExecutorUtils.KEYBOARD).execute(() -> {
|
ExecutorUtils.getBackgroundExecutor(ExecutorUtils.KEYBOARD).execute(() -> {
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -110,18 +110,22 @@ class KeyboardWrapperView @JvmOverloads constructor(
|
||||||
keyboardView.measuredHeight
|
keyboardView.measuredHeight
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val scale = Settings.getInstance().current.mKeyboardHeightScale
|
||||||
|
// scale one-handed mode button height if keyboard height scale is < 80%
|
||||||
|
// more relevant: also change the distance, so the buttons are actually visible
|
||||||
|
val heightScale = scale + 0.2f
|
||||||
val buttonsLeft = if (isLeftGravity) keyboardView.measuredWidth else 0
|
val buttonsLeft = if (isLeftGravity) keyboardView.measuredWidth else 0
|
||||||
stopOneHandedModeBtn.layout(
|
stopOneHandedModeBtn.layout(
|
||||||
buttonsLeft + (spareWidth - stopOneHandedModeBtn.measuredWidth) / 2,
|
buttonsLeft + (spareWidth - stopOneHandedModeBtn.measuredWidth) / 2,
|
||||||
stopOneHandedModeBtn.measuredHeight / 2,
|
(heightScale * stopOneHandedModeBtn.measuredHeight / 2).toInt(),
|
||||||
buttonsLeft + (spareWidth + stopOneHandedModeBtn.measuredWidth) / 2,
|
buttonsLeft + (spareWidth + stopOneHandedModeBtn.measuredWidth) / 2,
|
||||||
3 * stopOneHandedModeBtn.measuredHeight / 2
|
(heightScale * 3 * stopOneHandedModeBtn.measuredHeight / 2).toInt()
|
||||||
)
|
)
|
||||||
switchOneHandedModeBtn.layout(
|
switchOneHandedModeBtn.layout(
|
||||||
buttonsLeft + (spareWidth - switchOneHandedModeBtn.measuredWidth) / 2,
|
buttonsLeft + (spareWidth - switchOneHandedModeBtn.measuredWidth) / 2,
|
||||||
2 * stopOneHandedModeBtn.measuredHeight,
|
(heightScale * 2 * stopOneHandedModeBtn.measuredHeight).toInt(),
|
||||||
buttonsLeft + (spareWidth + switchOneHandedModeBtn.measuredWidth) / 2,
|
buttonsLeft + (spareWidth + switchOneHandedModeBtn.measuredWidth) / 2,
|
||||||
2 * stopOneHandedModeBtn.measuredHeight + switchOneHandedModeBtn.measuredHeight
|
(heightScale * (2 * stopOneHandedModeBtn.measuredHeight + switchOneHandedModeBtn.measuredHeight)).toInt()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -742,7 +742,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
|
||||||
* @param locale the locale
|
* @param locale the locale
|
||||||
*/
|
*/
|
||||||
// TODO: make sure the current settings always have the right locales, and read from them.
|
// TODO: make sure the current settings always have the right locales, and read from them.
|
||||||
private void resetDictionaryFacilitator(final Locale locale) {
|
private void resetDictionaryFacilitator(@NonNull final Locale locale) {
|
||||||
final SettingsValues settingsValues = mSettings.getCurrent();
|
final SettingsValues settingsValues = mSettings.getCurrent();
|
||||||
mDictionaryFacilitator.resetDictionaries(this /* context */, locale,
|
mDictionaryFacilitator.resetDictionaries(this /* context */, locale,
|
||||||
settingsValues.mUseContactsDictionary, settingsValues.mUsePersonalizedDicts,
|
settingsValues.mUseContactsDictionary, settingsValues.mUsePersonalizedDicts,
|
||||||
|
|
|
@ -269,7 +269,6 @@ public class SuggestedWords {
|
||||||
public final int mScore;
|
public final int mScore;
|
||||||
public final int mKindAndFlags;
|
public final int mKindAndFlags;
|
||||||
public final int mCodePointCount;
|
public final int mCodePointCount;
|
||||||
@Deprecated
|
|
||||||
public final Dictionary mSourceDict;
|
public final Dictionary mSourceDict;
|
||||||
// For auto-commit. This keeps track of the index inside the touch coordinates array
|
// For auto-commit. This keeps track of the index inside the touch coordinates array
|
||||||
// passed to native code to get suggestions for a gesture that corresponds to the first
|
// passed to native code to get suggestions for a gesture that corresponds to the first
|
||||||
|
|
|
@ -8,6 +8,7 @@ import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.*
|
import android.widget.*
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
import androidx.core.view.get
|
||||||
import androidx.core.view.isGone
|
import androidx.core.view.isGone
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.core.view.size
|
import androidx.core.view.size
|
||||||
|
@ -15,6 +16,8 @@ import org.dslul.openboard.inputmethod.dictionarypack.DictionaryPackConstants
|
||||||
import org.dslul.openboard.inputmethod.latin.BinaryDictionaryGetter
|
import org.dslul.openboard.inputmethod.latin.BinaryDictionaryGetter
|
||||||
import org.dslul.openboard.inputmethod.latin.R
|
import org.dslul.openboard.inputmethod.latin.R
|
||||||
import org.dslul.openboard.inputmethod.latin.common.LocaleUtils
|
import org.dslul.openboard.inputmethod.latin.common.LocaleUtils
|
||||||
|
import org.dslul.openboard.inputmethod.latin.databinding.LanguageListItemBinding
|
||||||
|
import org.dslul.openboard.inputmethod.latin.databinding.LocaleSettingsDialogBinding
|
||||||
import org.dslul.openboard.inputmethod.latin.utils.*
|
import org.dslul.openboard.inputmethod.latin.utils.*
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
@ -24,26 +27,28 @@ class LanguageSettingsDialog(
|
||||||
private val infos: MutableList<SubtypeInfo>,
|
private val infos: MutableList<SubtypeInfo>,
|
||||||
private val fragment: LanguageSettingsFragment?,
|
private val fragment: LanguageSettingsFragment?,
|
||||||
private val onlySystemLocales: Boolean,
|
private val onlySystemLocales: Boolean,
|
||||||
private val onSubtypesChanged: () -> Unit
|
private val reloadSetting: () -> Unit
|
||||||
) : AlertDialog(context), LanguageSettingsFragment.Listener {
|
) : AlertDialog(context), LanguageSettingsFragment.Listener {
|
||||||
private val prefs = DeviceProtectedUtils.getSharedPreferences(context)!!
|
private val prefs = DeviceProtectedUtils.getSharedPreferences(context)!!
|
||||||
private val view = LayoutInflater.from(context).inflate(R.layout.locale_settings_dialog, null)
|
private val binding = LocaleSettingsDialogBinding.inflate(LayoutInflater.from(context))
|
||||||
private val mainLocaleString = infos.first().subtype.locale()
|
private val mainLocaleString = infos.first().subtype.locale()
|
||||||
private val mainLocale = mainLocaleString.toLocale()
|
private val mainLocale = mainLocaleString.toLocale()
|
||||||
|
private var hasInternalDictForLanguage = false
|
||||||
|
private lateinit var userDicts: MutableSet<File>
|
||||||
|
|
||||||
init {
|
init {
|
||||||
setTitle(infos.first().displayName)
|
setTitle(infos.first().displayName)
|
||||||
setView(ScrollView(context).apply { addView(view) })
|
setView(ScrollView(context).apply { addView(binding.root) })
|
||||||
setButton(BUTTON_NEGATIVE, context.getString(R.string.dialog_close)) { _, _ ->
|
setButton(BUTTON_NEGATIVE, context.getString(R.string.dialog_close)) { _, _ ->
|
||||||
dismiss()
|
dismiss()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (onlySystemLocales)
|
if (onlySystemLocales)
|
||||||
view.findViewById<View>(R.id.subtypes).isGone = true
|
binding.subtypes.isGone = true
|
||||||
else
|
else
|
||||||
fillSubtypesView(view.findViewById(R.id.subtypes))
|
fillSubtypesView()
|
||||||
fillSecondaryLocaleView(view.findViewById(R.id.secondary_languages))
|
fillSecondaryLocaleView()
|
||||||
fillDictionariesView(view.findViewById(R.id.dictionaries))
|
fillDictionariesView()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onStart() {
|
override fun onStart() {
|
||||||
|
@ -56,9 +61,9 @@ class LanguageSettingsDialog(
|
||||||
fragment?.setListener(null)
|
fragment?.setListener(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun fillSubtypesView(subtypesView: LinearLayout) {
|
private fun fillSubtypesView() {
|
||||||
if (infos.any { it.subtype.isAsciiCapable }) { // currently can only add subtypes for latin keyboards
|
if (infos.any { it.subtype.isAsciiCapable }) { // currently can only add subtypes for latin keyboards
|
||||||
subtypesView.findViewById<ImageView>(R.id.add_subtype).setOnClickListener {
|
binding.addSubtype.setOnClickListener {
|
||||||
val layouts = context.resources.getStringArray(R.array.predefined_layouts)
|
val layouts = context.resources.getStringArray(R.array.predefined_layouts)
|
||||||
.filterNot { layoutName -> infos.any { SubtypeLocaleUtils.getKeyboardLayoutSetName(it.subtype) == layoutName } }
|
.filterNot { layoutName -> infos.any { SubtypeLocaleUtils.getKeyboardLayoutSetName(it.subtype) == layoutName } }
|
||||||
val displayNames = layouts.map { SubtypeLocaleUtils.getKeyboardLayoutSetDisplayName(it) }
|
val displayNames = layouts.map { SubtypeLocaleUtils.getKeyboardLayoutSetDisplayName(it) }
|
||||||
|
@ -70,23 +75,23 @@ class LanguageSettingsDialog(
|
||||||
val newSubtypeInfo = newSubtype.toSubtypeInfo(mainLocale, context, true, infos.first().hasDictionary) // enabled by default, because why else add them
|
val newSubtypeInfo = newSubtype.toSubtypeInfo(mainLocale, context, true, infos.first().hasDictionary) // enabled by default, because why else add them
|
||||||
addAdditionalSubtype(prefs, context.resources, newSubtype)
|
addAdditionalSubtype(prefs, context.resources, newSubtype)
|
||||||
addEnabledSubtype(prefs, newSubtype)
|
addEnabledSubtype(prefs, newSubtype)
|
||||||
addSubtypeToView(newSubtypeInfo, subtypesView)
|
addSubtypeToView(newSubtypeInfo)
|
||||||
infos.add(newSubtypeInfo)
|
infos.add(newSubtypeInfo)
|
||||||
onSubtypesChanged()
|
reloadSetting()
|
||||||
}
|
}
|
||||||
.setNegativeButton(android.R.string.cancel, null)
|
.setNegativeButton(android.R.string.cancel, null)
|
||||||
.show()
|
.show()
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
subtypesView.findViewById<View>(R.id.add_subtype).isGone = true
|
binding.addSubtype.isGone = true
|
||||||
|
|
||||||
// add subtypes
|
// add subtypes
|
||||||
infos.sortedBy { it.displayName }.forEach {
|
infos.sortedBy { it.displayName }.forEach {
|
||||||
addSubtypeToView(it, subtypesView)
|
addSubtypeToView(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun addSubtypeToView(subtype: SubtypeInfo, subtypesView: LinearLayout) {
|
private fun addSubtypeToView(subtype: SubtypeInfo) {
|
||||||
val row = LayoutInflater.from(context).inflate(R.layout.language_list_item, listView)
|
val row = LayoutInflater.from(context).inflate(R.layout.language_list_item, listView)
|
||||||
row.findViewById<TextView>(R.id.language_name).text =
|
row.findViewById<TextView>(R.id.language_name).text =
|
||||||
SubtypeLocaleUtils.getKeyboardLayoutSetDisplayName(subtype.subtype)
|
SubtypeLocaleUtils.getKeyboardLayoutSetDisplayName(subtype.subtype)
|
||||||
|
@ -104,7 +109,7 @@ class LanguageSettingsDialog(
|
||||||
else
|
else
|
||||||
removeEnabledSubtype(prefs, subtype.subtype)
|
removeEnabledSubtype(prefs, subtype.subtype)
|
||||||
subtype.isEnabled = b
|
subtype.isEnabled = b
|
||||||
onSubtypesChanged()
|
reloadSetting()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (isAdditionalSubtype(subtype.subtype)) {
|
if (isAdditionalSubtype(subtype.subtype)) {
|
||||||
|
@ -113,19 +118,19 @@ class LanguageSettingsDialog(
|
||||||
isVisible = true
|
isVisible = true
|
||||||
setOnClickListener {
|
setOnClickListener {
|
||||||
// can be re-added easily, no need for confirmation dialog
|
// can be re-added easily, no need for confirmation dialog
|
||||||
subtypesView.removeView(row)
|
binding.subtypes.removeView(row)
|
||||||
infos.remove(subtype)
|
infos.remove(subtype)
|
||||||
|
|
||||||
removeAdditionalSubtype(prefs, context.resources, subtype.subtype)
|
removeAdditionalSubtype(prefs, context.resources, subtype.subtype)
|
||||||
removeEnabledSubtype(prefs, subtype.subtype)
|
removeEnabledSubtype(prefs, subtype.subtype)
|
||||||
onSubtypesChanged()
|
reloadSetting()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
subtypesView.addView(row)
|
binding.subtypes.addView(row)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun fillSecondaryLocaleView(secondaryLocalesView: LinearLayout) {
|
private fun fillSecondaryLocaleView() {
|
||||||
// can only use multilingual typing if there is more than one dictionary available
|
// can only use multilingual typing if there is more than one dictionary available
|
||||||
val availableSecondaryLocales = getAvailableSecondaryLocales(
|
val availableSecondaryLocales = getAvailableSecondaryLocales(
|
||||||
context,
|
context,
|
||||||
|
@ -134,10 +139,10 @@ class LanguageSettingsDialog(
|
||||||
)
|
)
|
||||||
val selectedSecondaryLocales = Settings.getSecondaryLocales(prefs, mainLocaleString)
|
val selectedSecondaryLocales = Settings.getSecondaryLocales(prefs, mainLocaleString)
|
||||||
selectedSecondaryLocales.forEach {
|
selectedSecondaryLocales.forEach {
|
||||||
addSecondaryLocaleView(it, secondaryLocalesView)
|
addSecondaryLocaleView(it)
|
||||||
}
|
}
|
||||||
if (availableSecondaryLocales.isNotEmpty()) {
|
if (availableSecondaryLocales.isNotEmpty()) {
|
||||||
secondaryLocalesView.findViewById<ImageView>(R.id.add_secondary_language).apply {
|
binding.addSecondaryLanguage.apply {
|
||||||
isVisible = true
|
isVisible = true
|
||||||
setOnClickListener {
|
setOnClickListener {
|
||||||
val locales = (availableSecondaryLocales - Settings.getSecondaryLocales(prefs, mainLocaleString)).sortedBy { it.displayName }
|
val locales = (availableSecondaryLocales - Settings.getSecondaryLocales(prefs, mainLocaleString)).sortedBy { it.displayName }
|
||||||
|
@ -148,35 +153,37 @@ class LanguageSettingsDialog(
|
||||||
val locale = locales[i]
|
val locale = locales[i]
|
||||||
val localeStrings = Settings.getSecondaryLocales(prefs, mainLocaleString).map { it.toString() }
|
val localeStrings = Settings.getSecondaryLocales(prefs, mainLocaleString).map { it.toString() }
|
||||||
Settings.setSecondaryLocales(prefs, mainLocaleString, localeStrings + locale.toString())
|
Settings.setSecondaryLocales(prefs, mainLocaleString, localeStrings + locale.toString())
|
||||||
addSecondaryLocaleView(locale, secondaryLocalesView)
|
addSecondaryLocaleView(locale)
|
||||||
di.dismiss()
|
di.dismiss()
|
||||||
|
reloadSetting()
|
||||||
}
|
}
|
||||||
.setNegativeButton(android.R.string.cancel, null)
|
.setNegativeButton(android.R.string.cancel, null)
|
||||||
.show()
|
.show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (selectedSecondaryLocales.isEmpty())
|
} else if (selectedSecondaryLocales.isEmpty())
|
||||||
secondaryLocalesView.isGone = true
|
binding.secondaryLocales.isGone = true
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun addSecondaryLocaleView(locale: Locale, secondaryLocalesView: LinearLayout) {
|
private fun addSecondaryLocaleView(locale: Locale) {
|
||||||
val row = LayoutInflater.from(context).inflate(R.layout.language_list_item, listView)
|
val rowBinding = LanguageListItemBinding.inflate(LayoutInflater.from(context), listView, false)
|
||||||
row.findViewById<Switch>(R.id.language_switch).isGone = true
|
rowBinding.languageSwitch.isGone = true
|
||||||
row.findViewById<Switch>(R.id.language_details).isGone = true
|
rowBinding.languageDetails.isGone = true
|
||||||
row.findViewById<TextView>(R.id.language_name).text = locale.displayName
|
rowBinding.languageName.text = locale.displayName
|
||||||
row.findViewById<ImageView>(R.id.delete_button).apply {
|
rowBinding.deleteButton.apply {
|
||||||
isVisible = true
|
isVisible = true
|
||||||
setOnClickListener {
|
setOnClickListener {
|
||||||
val localeStrings = Settings.getSecondaryLocales(prefs, mainLocaleString).map { it.toString() }
|
val localeStrings = Settings.getSecondaryLocales(prefs, mainLocaleString).map { it.toString() }
|
||||||
Settings.setSecondaryLocales(prefs, mainLocaleString, localeStrings - locale.toString())
|
Settings.setSecondaryLocales(prefs, mainLocaleString, localeStrings - locale.toString())
|
||||||
secondaryLocalesView.removeView(row)
|
binding.secondaryLocales.removeView(rowBinding.root)
|
||||||
|
reloadSetting()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
secondaryLocalesView.addView(row)
|
binding.secondaryLocales.addView(rowBinding.root)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun fillDictionariesView(dictionariesView: LinearLayout) {
|
private fun fillDictionariesView() {
|
||||||
dictionariesView.findViewById<ImageView>(R.id.add_dictionary).setOnClickListener {
|
binding.addDictionary.setOnClickListener {
|
||||||
val link = "<a href='$DICTIONARY_URL'>" + context.getString(R.string.dictionary_link_text) + "</a>"
|
val link = "<a href='$DICTIONARY_URL'>" + context.getString(R.string.dictionary_link_text) + "</a>"
|
||||||
val message = SpannableStringUtils.fromHtml(context.getString(R.string.add_dictionary, link))
|
val message = SpannableStringUtils.fromHtml(context.getString(R.string.add_dictionary, link))
|
||||||
val dialog = Builder(context)
|
val dialog = Builder(context)
|
||||||
|
@ -188,9 +195,11 @@ class LanguageSettingsDialog(
|
||||||
dialog.show()
|
dialog.show()
|
||||||
(dialog.findViewById<View>(android.R.id.message) as? TextView)?.movementMethod = LinkMovementMethod.getInstance()
|
(dialog.findViewById<View>(android.R.id.message) as? TextView)?.movementMethod = LinkMovementMethod.getInstance()
|
||||||
}
|
}
|
||||||
val (userDicts, hasInternalDictForLanguage) = getUserAndInternalDictionaries(context, mainLocaleString)
|
val (_userDicts, _hasInternalDictForLanguage) = getUserAndInternalDictionaries(context, mainLocaleString)
|
||||||
|
userDicts = _userDicts.toMutableSet()
|
||||||
|
hasInternalDictForLanguage = _hasInternalDictForLanguage
|
||||||
if (hasInternalDictForLanguage) {
|
if (hasInternalDictForLanguage) {
|
||||||
dictionariesView.addView(TextView(context, null, R.style.PreferenceCategoryTitleText).apply {
|
binding.dictionaries.addView(TextView(context, null, R.style.PreferenceCategoryTitleText).apply {
|
||||||
setText(R.string.internal_dictionary_summary)
|
setText(R.string.internal_dictionary_summary)
|
||||||
textSize *= 0.8f
|
textSize *= 0.8f
|
||||||
setPadding((context.resources.displayMetrics.scaledDensity * 16).toInt(), 0, 0, 0)
|
setPadding((context.resources.displayMetrics.scaledDensity * 16).toInt(), 0, 0, 0)
|
||||||
|
@ -198,25 +207,31 @@ class LanguageSettingsDialog(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
userDicts.sorted().forEach {
|
userDicts.sorted().forEach {
|
||||||
addDictionaryToView(it, dictionariesView)
|
addDictionaryToView(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onNewDictionary(uri: Uri?) {
|
override fun onNewDictionary(uri: Uri?) {
|
||||||
NewDictionaryAdder(context) { replaced, dictFile ->
|
NewDictionaryAdder(context) { replaced, dictFile ->
|
||||||
if (!replaced)
|
if (!replaced) {
|
||||||
addDictionaryToView(dictFile, view.findViewById(R.id.dictionaries))
|
addDictionaryToView(dictFile)
|
||||||
|
userDicts.add(dictFile)
|
||||||
|
if (hasInternalDictForLanguage) {
|
||||||
|
binding.dictionaries[1].isEnabled =
|
||||||
|
userDicts.none { it.name == "${DictionaryInfoUtils.MAIN_DICT_PREFIX}${USER_DICTIONARY_SUFFIX}" }
|
||||||
|
}
|
||||||
|
}
|
||||||
}.addDictionary(uri, mainLocale)
|
}.addDictionary(uri, mainLocale)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun addDictionaryToView(dictFile: File, dictionariesView: LinearLayout) {
|
private fun addDictionaryToView(dictFile: File) {
|
||||||
if (!infos.first().hasDictionary) {
|
if (!infos.first().hasDictionary) {
|
||||||
infos.forEach { it.hasDictionary = true }
|
infos.forEach { it.hasDictionary = true }
|
||||||
}
|
}
|
||||||
val dictType = dictFile.name.substringBefore("_${USER_DICTIONARY_SUFFIX}")
|
val dictType = dictFile.name.substringBefore("_${USER_DICTIONARY_SUFFIX}")
|
||||||
val row = LayoutInflater.from(context).inflate(R.layout.language_list_item, listView)
|
val rowBinding = LanguageListItemBinding.inflate(LayoutInflater.from(context), listView, false)
|
||||||
row.findViewById<TextView>(R.id.language_name).text = dictType
|
rowBinding.languageName.text = dictType
|
||||||
row.findViewById<TextView>(R.id.language_details).apply {
|
rowBinding.languageDetails.apply {
|
||||||
val header = DictionaryInfoUtils.getDictionaryFileHeaderOrNull(dictFile, 0, dictFile.length())
|
val header = DictionaryInfoUtils.getDictionaryFileHeaderOrNull(dictFile, 0, dictFile.length())
|
||||||
if (header?.description == null) {
|
if (header?.description == null) {
|
||||||
isGone = true
|
isGone = true
|
||||||
|
@ -225,8 +240,8 @@ class LanguageSettingsDialog(
|
||||||
text = header.description
|
text = header.description
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
row.findViewById<Switch>(R.id.language_switch).isGone = true
|
rowBinding.languageSwitch.isGone = true
|
||||||
row.findViewById<ImageView>(R.id.delete_button).apply {
|
rowBinding.deleteButton.apply {
|
||||||
isVisible = true
|
isVisible = true
|
||||||
setOnClickListener {
|
setOnClickListener {
|
||||||
confirmDialog(context, context.getString(R.string.remove_dictionary_message, dictType), context.getString(
|
confirmDialog(context, context.getString(R.string.remove_dictionary_message, dictType), context.getString(
|
||||||
|
@ -237,15 +252,19 @@ class LanguageSettingsDialog(
|
||||||
parent.delete()
|
parent.delete()
|
||||||
val newDictBroadcast = Intent(DictionaryPackConstants.NEW_DICTIONARY_INTENT_ACTION)
|
val newDictBroadcast = Intent(DictionaryPackConstants.NEW_DICTIONARY_INTENT_ACTION)
|
||||||
fragment?.activity?.sendBroadcast(newDictBroadcast)
|
fragment?.activity?.sendBroadcast(newDictBroadcast)
|
||||||
dictionariesView.removeView(row)
|
binding.dictionaries.removeView(rowBinding.root)
|
||||||
if (dictionariesView.size < 2) { // first view is "Dictionaries"
|
if (binding.dictionaries.size < 2) { // first view is "Dictionaries"
|
||||||
infos.forEach { it.hasDictionary = false }
|
infos.forEach { it.hasDictionary = false }
|
||||||
}
|
}
|
||||||
|
userDicts.remove(dictFile)
|
||||||
|
if (hasInternalDictForLanguage) {
|
||||||
|
binding.dictionaries[1].isEnabled =
|
||||||
|
userDicts.none { it.name == "${DictionaryInfoUtils.MAIN_DICT_PREFIX}${USER_DICTIONARY_SUFFIX}" }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dictionariesView.addView(row)
|
binding.dictionaries.addView(rowBinding.root)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:showDividers="middle"
|
android:showDividers="middle"
|
||||||
android:divider="@drawable/ic_divider"
|
android:divider="@drawable/ic_divider"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:padding="10dp">
|
android:padding="10dp">
|
||||||
|
<!-- layout appears unnecessary, but more views will be added -->
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/subtypes"
|
android:id="@+id/subtypes"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
|
@ -15,7 +17,8 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:paddingHorizontal="16dp"
|
android:paddingHorizontal="16dp"
|
||||||
android:orientation="horizontal">
|
android:orientation="horizontal"
|
||||||
|
tools:ignore="UseCompoundDrawables"> <!-- view gets an onClickListener -->
|
||||||
<TextView
|
<TextView
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
@ -31,8 +34,9 @@
|
||||||
android:src="@drawable/ic_plus" />
|
android:src="@drawable/ic_plus" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
<!-- layout appears unnecessary, but more views will be added -->
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/secondary_languages"
|
android:id="@+id/secondary_locales"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content">
|
||||||
|
@ -57,6 +61,7 @@
|
||||||
android:src="@drawable/ic_plus" />
|
android:src="@drawable/ic_plus" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
<!-- layout appears unnecessary, but more views will be added -->
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/dictionaries"
|
android:id="@+id/dictionaries"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue