diff --git a/README.md b/README.md index 89d2f43ea..297d534a4 100644 --- a/README.md +++ b/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") * 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) - * can follow the system's day/night setting -* Split keyboard + * can follow the system's day/night setting on Android 10+ (and on some versions of Android 9) +* Split keyboard (if the screen is large enough) * Number row * Number pad * 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//shared_prefs/`, though this may depend on the device and Android version + ## Important differences and changes to OpenBoard * Debug version can be installed along OpenBoard * Allow users to add and replace built-in dictionaries diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/KeyboardView.java b/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/KeyboardView.java index f4b5630a1..49db5520b 100644 --- a/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/KeyboardView.java +++ b/app/src/main/java/org/dslul/openboard/inputmethod/keyboard/KeyboardView.java @@ -124,6 +124,8 @@ public class KeyboardView extends View { private Bitmap mOffscreenBuffer; /** Flag for whether the key hints should be displayed */ 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 */ @NonNull private final Canvas mOffscreenCanvas = new Canvas(); @@ -301,6 +303,8 @@ public class KeyboardView extends View { } 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 Drawable background = getBackground(); // Calculate clip region and set. @@ -370,8 +374,8 @@ public class KeyboardView extends View { if (key.needsToKeepBackgroundAspectRatio(mDefaultKeyLabelFlags) // HACK: To disable expanding normal/functional key background. && !key.hasCustomActionLabel()) { - bgWidth = background.getIntrinsicWidth(); - bgHeight = background.getIntrinsicHeight(); + bgWidth = (int) (background.getIntrinsicWidth() * mIconScaleFactor); + bgHeight = (int) (background.getIntrinsicHeight() * mIconScaleFactor); bgX = (keyWidth - bgWidth) / 2; bgY = (keyHeight - bgHeight) / 2; } else { @@ -412,8 +416,9 @@ public class KeyboardView extends View { labelBaseline = centerY + labelCharHeight / 2.0f; // Horizontal label text alignment - if (key.isAlignLabelOffCenter()) { - // The label is placed off center of the key. Used mainly on "phone number" layout. + if (key.isAlignLabelOffCenter() && mShowsHints) { + // 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; paint.setTextAlign(Align.LEFT); } else { @@ -496,11 +501,11 @@ public class KeyboardView extends View { if (label == null && icon != null) { final int iconWidth; if (key.getCode() == Constants.CODE_SPACE && icon instanceof NinePatchDrawable) { - iconWidth = (int)(keyWidth * mSpacebarIconWidthRatio); + iconWidth = (int) (keyWidth * mSpacebarIconWidthRatio * mIconScaleFactor); } 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; if (key.isAlignIconToBottom()) { iconY = keyHeight - iconHeight; diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/latin/DictionaryFacilitatorImpl.java b/app/src/main/java/org/dslul/openboard/inputmethod/latin/DictionaryFacilitatorImpl.java index 3479f705e..1fe989102 100644 --- a/app/src/main/java/org/dslul/openboard/inputmethod/latin/DictionaryFacilitatorImpl.java +++ b/app/src/main/java/org/dslul/openboard/inputmethod/latin/DictionaryFacilitatorImpl.java @@ -153,7 +153,7 @@ public class DictionaryFacilitatorImpl implements DictionaryFacilitator { /** * 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. @@ -202,10 +202,10 @@ public class DictionaryFacilitatorImpl implements DictionaryFacilitator { new ConcurrentHashMap<>(); 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 String account, @NonNull final Map subDicts) { @@ -288,7 +288,7 @@ public class DictionaryFacilitatorImpl implements DictionaryFacilitator { @Override public boolean isActive() { - return mDictionaryGroups.get(0).mLocale != null; + return !mDictionaryGroups.get(0).mLocale.getLanguage().isEmpty(); } // used in @@ -340,12 +340,10 @@ public class DictionaryFacilitatorImpl implements DictionaryFacilitator { @Nullable static DictionaryGroup findDictionaryGroupWithLocale(final List dictionaryGroups, - final Locale locale) { + @NonNull final Locale locale) { if (dictionaryGroups == null) return null; for (DictionaryGroup dictionaryGroup : dictionaryGroups) { - if (locale == null && dictionaryGroup.mLocale == null) - return dictionaryGroup; - if (locale != null && locale.equals(dictionaryGroup.mLocale)) + if (locale.equals(dictionaryGroup.mLocale)) return dictionaryGroup; } return null; @@ -354,7 +352,7 @@ public class DictionaryFacilitatorImpl implements DictionaryFacilitator { // original public void resetDictionaries( final Context context, - final Locale newLocale, + @NonNull final Locale newLocale, final boolean useContactsDict, final boolean usePersonalizedDicts, final boolean forceReloadMainDictionary, @@ -399,10 +397,8 @@ public class DictionaryFacilitatorImpl implements DictionaryFacilitator { final ArrayList newDictionaryGroups = new ArrayList<>(allLocales.size()); for (Locale locale : allLocales) { // get existing dictionary group for new locale - final DictionaryGroup oldDictionaryGroupForLocale = - findDictionaryGroupWithLocale(mDictionaryGroups, locale); - final ArrayList dictTypesToCleanupForLocale = - existingDictionariesToCleanup.get(locale); + final DictionaryGroup oldDictionaryGroupForLocale = findDictionaryGroupWithLocale(mDictionaryGroups, locale); + final ArrayList dictTypesToCleanupForLocale = existingDictionariesToCleanup.get(locale); final boolean noExistingDictsForThisLocale = (null == oldDictionaryGroupForLocale); // 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 ExpandableBinaryDictionary dict = getSubDict( dictType, context, locale, dictFile, "" /* dictNamePrefix */, account); + if (dict == null) { + throw new RuntimeException("Unknown dictionary type: " + dictType); + } if (additionalDictAttributes.containsKey(dictType)) { dict.clearAndFlushDictionaryWithAdditionalAttributes( additionalDictAttributes.get(dictType)); } - if (dict == null) { - throw new RuntimeException("Unknown dictionary type: " + dictType); - } dict.reloadDictionaryIfRequired(); dict.waitAllTasksForTests(); subDicts.put(dictType, dict); @@ -939,9 +935,6 @@ public class DictionaryFacilitatorImpl implements DictionaryFacilitator { if (TextUtils.isEmpty(word)) { return false; } - if (dictionaryGroup.mLocale == null) { - return false; - } if (isBlacklisted(word)) return false; for (final String dictType : dictionariesToCheck) { final Dictionary dictionary = dictionaryGroup.getDict(dictType); @@ -992,7 +985,8 @@ public class DictionaryFacilitatorImpl implements DictionaryFacilitator { } else isInContacts = false; // 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 ExecutorUtils.getBackgroundExecutor(ExecutorUtils.KEYBOARD).execute(() -> { try { diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/latin/KeyboardWrapperView.kt b/app/src/main/java/org/dslul/openboard/inputmethod/latin/KeyboardWrapperView.kt index e0d6e257d..74cab47cb 100644 --- a/app/src/main/java/org/dslul/openboard/inputmethod/latin/KeyboardWrapperView.kt +++ b/app/src/main/java/org/dslul/openboard/inputmethod/latin/KeyboardWrapperView.kt @@ -110,18 +110,22 @@ class KeyboardWrapperView @JvmOverloads constructor( 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 stopOneHandedModeBtn.layout( buttonsLeft + (spareWidth - stopOneHandedModeBtn.measuredWidth) / 2, - stopOneHandedModeBtn.measuredHeight / 2, + (heightScale * stopOneHandedModeBtn.measuredHeight / 2).toInt(), buttonsLeft + (spareWidth + stopOneHandedModeBtn.measuredWidth) / 2, - 3 * stopOneHandedModeBtn.measuredHeight / 2 + (heightScale * 3 * stopOneHandedModeBtn.measuredHeight / 2).toInt() ) switchOneHandedModeBtn.layout( buttonsLeft + (spareWidth - switchOneHandedModeBtn.measuredWidth) / 2, - 2 * stopOneHandedModeBtn.measuredHeight, + (heightScale * 2 * stopOneHandedModeBtn.measuredHeight).toInt(), buttonsLeft + (spareWidth + switchOneHandedModeBtn.measuredWidth) / 2, - 2 * stopOneHandedModeBtn.measuredHeight + switchOneHandedModeBtn.measuredHeight + (heightScale * (2 * stopOneHandedModeBtn.measuredHeight + switchOneHandedModeBtn.measuredHeight)).toInt() ) } diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/latin/LatinIME.java b/app/src/main/java/org/dslul/openboard/inputmethod/latin/LatinIME.java index e80c19cb5..402eaa21e 100644 --- a/app/src/main/java/org/dslul/openboard/inputmethod/latin/LatinIME.java +++ b/app/src/main/java/org/dslul/openboard/inputmethod/latin/LatinIME.java @@ -742,7 +742,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen * @param locale the locale */ // 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(); mDictionaryFacilitator.resetDictionaries(this /* context */, locale, settingsValues.mUseContactsDictionary, settingsValues.mUsePersonalizedDicts, diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/latin/SuggestedWords.java b/app/src/main/java/org/dslul/openboard/inputmethod/latin/SuggestedWords.java index a03dc0fc3..3f54c2d2e 100644 --- a/app/src/main/java/org/dslul/openboard/inputmethod/latin/SuggestedWords.java +++ b/app/src/main/java/org/dslul/openboard/inputmethod/latin/SuggestedWords.java @@ -269,7 +269,6 @@ public class SuggestedWords { public final int mScore; public final int mKindAndFlags; public final int mCodePointCount; - @Deprecated public final Dictionary mSourceDict; // 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 diff --git a/app/src/main/java/org/dslul/openboard/inputmethod/latin/settings/LanguageSettingsDialog.kt b/app/src/main/java/org/dslul/openboard/inputmethod/latin/settings/LanguageSettingsDialog.kt index c16257928..ec25651c0 100644 --- a/app/src/main/java/org/dslul/openboard/inputmethod/latin/settings/LanguageSettingsDialog.kt +++ b/app/src/main/java/org/dslul/openboard/inputmethod/latin/settings/LanguageSettingsDialog.kt @@ -8,6 +8,7 @@ import android.view.LayoutInflater import android.view.View import android.widget.* import androidx.appcompat.app.AlertDialog +import androidx.core.view.get import androidx.core.view.isGone import androidx.core.view.isVisible 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.R 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 java.io.File import java.util.* @@ -24,26 +27,28 @@ class LanguageSettingsDialog( private val infos: MutableList, private val fragment: LanguageSettingsFragment?, private val onlySystemLocales: Boolean, - private val onSubtypesChanged: () -> Unit + private val reloadSetting: () -> Unit ) : AlertDialog(context), LanguageSettingsFragment.Listener { 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 mainLocale = mainLocaleString.toLocale() + private var hasInternalDictForLanguage = false + private lateinit var userDicts: MutableSet init { 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)) { _, _ -> dismiss() } if (onlySystemLocales) - view.findViewById(R.id.subtypes).isGone = true + binding.subtypes.isGone = true else - fillSubtypesView(view.findViewById(R.id.subtypes)) - fillSecondaryLocaleView(view.findViewById(R.id.secondary_languages)) - fillDictionariesView(view.findViewById(R.id.dictionaries)) + fillSubtypesView() + fillSecondaryLocaleView() + fillDictionariesView() } override fun onStart() { @@ -56,9 +61,9 @@ class LanguageSettingsDialog( 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 - subtypesView.findViewById(R.id.add_subtype).setOnClickListener { + binding.addSubtype.setOnClickListener { val layouts = context.resources.getStringArray(R.array.predefined_layouts) .filterNot { layoutName -> infos.any { SubtypeLocaleUtils.getKeyboardLayoutSetName(it.subtype) == layoutName } } 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 addAdditionalSubtype(prefs, context.resources, newSubtype) addEnabledSubtype(prefs, newSubtype) - addSubtypeToView(newSubtypeInfo, subtypesView) + addSubtypeToView(newSubtypeInfo) infos.add(newSubtypeInfo) - onSubtypesChanged() + reloadSetting() } .setNegativeButton(android.R.string.cancel, null) .show() } } else - subtypesView.findViewById(R.id.add_subtype).isGone = true + binding.addSubtype.isGone = true // add subtypes 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) row.findViewById(R.id.language_name).text = SubtypeLocaleUtils.getKeyboardLayoutSetDisplayName(subtype.subtype) @@ -104,7 +109,7 @@ class LanguageSettingsDialog( else removeEnabledSubtype(prefs, subtype.subtype) subtype.isEnabled = b - onSubtypesChanged() + reloadSetting() } } if (isAdditionalSubtype(subtype.subtype)) { @@ -113,19 +118,19 @@ class LanguageSettingsDialog( isVisible = true setOnClickListener { // can be re-added easily, no need for confirmation dialog - subtypesView.removeView(row) + binding.subtypes.removeView(row) infos.remove(subtype) removeAdditionalSubtype(prefs, context.resources, 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 val availableSecondaryLocales = getAvailableSecondaryLocales( context, @@ -134,10 +139,10 @@ class LanguageSettingsDialog( ) val selectedSecondaryLocales = Settings.getSecondaryLocales(prefs, mainLocaleString) selectedSecondaryLocales.forEach { - addSecondaryLocaleView(it, secondaryLocalesView) + addSecondaryLocaleView(it) } if (availableSecondaryLocales.isNotEmpty()) { - secondaryLocalesView.findViewById(R.id.add_secondary_language).apply { + binding.addSecondaryLanguage.apply { isVisible = true setOnClickListener { val locales = (availableSecondaryLocales - Settings.getSecondaryLocales(prefs, mainLocaleString)).sortedBy { it.displayName } @@ -148,35 +153,37 @@ class LanguageSettingsDialog( val locale = locales[i] val localeStrings = Settings.getSecondaryLocales(prefs, mainLocaleString).map { it.toString() } Settings.setSecondaryLocales(prefs, mainLocaleString, localeStrings + locale.toString()) - addSecondaryLocaleView(locale, secondaryLocalesView) + addSecondaryLocaleView(locale) di.dismiss() + reloadSetting() } .setNegativeButton(android.R.string.cancel, null) .show() } } } else if (selectedSecondaryLocales.isEmpty()) - secondaryLocalesView.isGone = true + binding.secondaryLocales.isGone = true } - private fun addSecondaryLocaleView(locale: Locale, secondaryLocalesView: LinearLayout) { - val row = LayoutInflater.from(context).inflate(R.layout.language_list_item, listView) - row.findViewById(R.id.language_switch).isGone = true - row.findViewById(R.id.language_details).isGone = true - row.findViewById(R.id.language_name).text = locale.displayName - row.findViewById(R.id.delete_button).apply { + private fun addSecondaryLocaleView(locale: Locale) { + val rowBinding = LanguageListItemBinding.inflate(LayoutInflater.from(context), listView, false) + rowBinding.languageSwitch.isGone = true + rowBinding.languageDetails.isGone = true + rowBinding.languageName.text = locale.displayName + rowBinding.deleteButton.apply { isVisible = true setOnClickListener { val localeStrings = Settings.getSecondaryLocales(prefs, mainLocaleString).map { it.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) { - dictionariesView.findViewById(R.id.add_dictionary).setOnClickListener { + private fun fillDictionariesView() { + binding.addDictionary.setOnClickListener { val link = "" + context.getString(R.string.dictionary_link_text) + "" val message = SpannableStringUtils.fromHtml(context.getString(R.string.add_dictionary, link)) val dialog = Builder(context) @@ -188,9 +195,11 @@ class LanguageSettingsDialog( dialog.show() (dialog.findViewById(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) { - 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) textSize *= 0.8f setPadding((context.resources.displayMetrics.scaledDensity * 16).toInt(), 0, 0, 0) @@ -198,25 +207,31 @@ class LanguageSettingsDialog( }) } userDicts.sorted().forEach { - addDictionaryToView(it, dictionariesView) + addDictionaryToView(it) } } override fun onNewDictionary(uri: Uri?) { NewDictionaryAdder(context) { replaced, dictFile -> - if (!replaced) - addDictionaryToView(dictFile, view.findViewById(R.id.dictionaries)) + if (!replaced) { + 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) } - private fun addDictionaryToView(dictFile: File, dictionariesView: LinearLayout) { + private fun addDictionaryToView(dictFile: File) { if (!infos.first().hasDictionary) { infos.forEach { it.hasDictionary = true } } val dictType = dictFile.name.substringBefore("_${USER_DICTIONARY_SUFFIX}") - val row = LayoutInflater.from(context).inflate(R.layout.language_list_item, listView) - row.findViewById(R.id.language_name).text = dictType - row.findViewById(R.id.language_details).apply { + val rowBinding = LanguageListItemBinding.inflate(LayoutInflater.from(context), listView, false) + rowBinding.languageName.text = dictType + rowBinding.languageDetails.apply { val header = DictionaryInfoUtils.getDictionaryFileHeaderOrNull(dictFile, 0, dictFile.length()) if (header?.description == null) { isGone = true @@ -225,8 +240,8 @@ class LanguageSettingsDialog( text = header.description } } - row.findViewById(R.id.language_switch).isGone = true - row.findViewById(R.id.delete_button).apply { + rowBinding.languageSwitch.isGone = true + rowBinding.deleteButton.apply { isVisible = true setOnClickListener { confirmDialog(context, context.getString(R.string.remove_dictionary_message, dictType), context.getString( @@ -237,15 +252,19 @@ class LanguageSettingsDialog( parent.delete() val newDictBroadcast = Intent(DictionaryPackConstants.NEW_DICTIONARY_INTENT_ACTION) fragment?.activity?.sendBroadcast(newDictBroadcast) - dictionariesView.removeView(row) - if (dictionariesView.size < 2) { // first view is "Dictionaries" + binding.dictionaries.removeView(rowBinding.root) + if (binding.dictionaries.size < 2) { // first view is "Dictionaries" 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) } } diff --git a/app/src/main/res/layout/locale_settings_dialog.xml b/app/src/main/res/layout/locale_settings_dialog.xml index 94a0dba1d..e4908a067 100644 --- a/app/src/main/res/layout/locale_settings_dialog.xml +++ b/app/src/main/res/layout/locale_settings_dialog.xml @@ -1,11 +1,13 @@ + + android:orientation="horizontal" + tools:ignore="UseCompoundDrawables"> + @@ -57,6 +61,7 @@ android:src="@drawable/ic_plus" /> +