From a566e4d8a2f37d7d05d15b15d4656a4c8c157e11 Mon Sep 17 00:00:00 2001 From: devycarol <[email protected]> Date: Fri, 14 Feb 2025 13:35:48 -0700 Subject: [PATCH 1/3] replace persistent HashSet singleton with simple switch --- .../helium314/keyboard/latin/Dictionary.java | 31 +++++++++---------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/app/src/main/java/helium314/keyboard/latin/Dictionary.java b/app/src/main/java/helium314/keyboard/latin/Dictionary.java index 4c26703e..108fdf4f 100644 --- a/app/src/main/java/helium314/keyboard/latin/Dictionary.java +++ b/app/src/main/java/helium314/keyboard/latin/Dictionary.java @@ -6,15 +6,15 @@ package helium314.keyboard.latin; +import androidx.annotation.NonNull; + +import java.util.ArrayList; +import java.util.Locale; + import helium314.keyboard.latin.SuggestedWords.SuggestedWordInfo; import helium314.keyboard.latin.common.ComposedData; import helium314.keyboard.latin.settings.SettingsValuesForSuggestion; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Locale; - /** * Abstract base class for a dictionary that can do a fuzzy search for words based on a set of key * strokes. @@ -52,21 +52,12 @@ public abstract class Dictionary { public static final String TYPE_USER = "user"; // User history dictionary internal to LatinIME. public static final String TYPE_USER_HISTORY = "history"; + @NonNull public final String mDictType; // The locale for this dictionary. May be null if unknown (phony dictionary for example). public final Locale mLocale; - /** - * Set out of the dictionary types listed above that are based on data specific to the user, - * e.g., the user's contacts. - */ - private static final HashSet sUserSpecificDictionaryTypes = new HashSet<>(Arrays.asList( - TYPE_USER_TYPED, - TYPE_USER, - TYPE_CONTACTS, - TYPE_USER_HISTORY)); - - public Dictionary(final String dictType, final Locale locale) { + public Dictionary(@NonNull final String dictType, final Locale locale) { mDictType = dictType; mLocale = locale; } @@ -178,7 +169,13 @@ public abstract class Dictionary { * @return Whether this dictionary is specific to the user. */ public boolean isUserSpecific() { - return sUserSpecificDictionaryTypes.contains(mDictType); + return switch (mDictType) { + case TYPE_USER_TYPED, + TYPE_USER, + TYPE_CONTACTS, + TYPE_USER_HISTORY -> true; + default -> false; + }; } /** From b0827c7967de2fd9907a47782b4d9c511a844859 Mon Sep 17 00:00:00 2001 From: devycarol <[email protected]> Date: Fri, 14 Feb 2025 22:45:22 -0700 Subject: [PATCH 2/3] app names dictionary similar to contacts dictionary --- app/src/main/AndroidManifest.xml | 8 +- .../keyboard/latin/AppsBinaryDictionary.java | 90 +++++++++++++++++++ .../keyboard/latin/AppsDictionaryConstants.kt | 9 ++ .../helium314/keyboard/latin/AppsManager.kt | 30 +++++++ .../helium314/keyboard/latin/Dictionary.java | 2 + .../keyboard/latin/DictionaryFacilitator.java | 4 + .../latin/DictionaryFacilitatorImpl.java | 69 ++++++++------ .../latin/DictionaryFacilitatorLruCache.java | 15 +++- .../helium314/keyboard/latin/LatinIME.java | 7 +- .../helium314/keyboard/latin/LatinTokens.kt | 38 ++++++++ .../keyboard/latin/settings/Defaults.kt | 1 + .../keyboard/latin/settings/Settings.java | 1 + .../latin/settings/SettingsValues.java | 7 +- .../AndroidSpellCheckerService.java | 13 ++- .../keyboard/settings/SettingsActivity.kt | 1 + .../settings/screens/TextCorrectionScreen.kt | 6 ++ app/src/main/res/values/strings.xml | 4 + 17 files changed, 270 insertions(+), 35 deletions(-) create mode 100644 app/src/main/java/helium314/keyboard/latin/AppsBinaryDictionary.java create mode 100644 app/src/main/java/helium314/keyboard/latin/AppsDictionaryConstants.kt create mode 100644 app/src/main/java/helium314/keyboard/latin/AppsManager.kt create mode 100644 app/src/main/java/helium314/keyboard/latin/LatinTokens.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index cecae197..49040a5e 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -98,7 +98,13 @@ SPDX-License-Identifier: Apache-2.0 AND GPL-3.0-only - + + + + + + + diff --git a/app/src/main/java/helium314/keyboard/latin/AppsBinaryDictionary.java b/app/src/main/java/helium314/keyboard/latin/AppsBinaryDictionary.java new file mode 100644 index 00000000..15b5079c --- /dev/null +++ b/app/src/main/java/helium314/keyboard/latin/AppsBinaryDictionary.java @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: GPL-3.0-only + +package helium314.keyboard.latin; + +import android.content.Context; + +import androidx.annotation.Nullable; + +import com.android.inputmethod.latin.BinaryDictionary; + +import java.io.File; +import java.util.Locale; + +import helium314.keyboard.latin.common.StringUtils; +import helium314.keyboard.latin.utils.Log; + +public class AppsBinaryDictionary extends ExpandableBinaryDictionary { + private static final String TAG = AppsBinaryDictionary.class.getSimpleName(); + private static final String NAME = "apps"; + + private static final boolean DEBUG = false; + private static final boolean DEBUG_DUMP = false; + + private final AppsManager mAppsManager; + + protected AppsBinaryDictionary(final Context ctx, final Locale locale, + final File dictFile, final String name) { + super(ctx, getDictName(name, locale, dictFile), locale, Dictionary.TYPE_APPS, dictFile); + mAppsManager = new AppsManager(ctx); + reloadDictionaryIfRequired(); + } + + public static AppsBinaryDictionary getDictionary(final Context context, final Locale locale, + final File dictFile, final String dictNamePrefix, @Nullable final String account) { + return new AppsBinaryDictionary(context, locale, dictFile, dictNamePrefix + NAME); + } + + /** + * Typically called whenever the dictionary is created for the first time or recreated when we + * think that there are updates to the dictionary. This is called asynchronously. + */ + @Override + public void loadInitialContentsLocked() { + loadDictionaryLocked(); + } + + /** + * Loads app names to the dictionary. + */ + private void loadDictionaryLocked() { + for (final String name : mAppsManager.getNames()) { + addNameLocked(name); + } + } + + /** + * Adds the words in an app label to the binary dictionary along with their n-grams. + */ + private void addNameLocked(final String appLabel) { + NgramContext ngramContext = NgramContext.getEmptyPrevWordsContext( + BinaryDictionary.MAX_PREV_WORD_COUNT_FOR_N_GRAM); + // TODO: Better tokenization for non-Latin writing systems + for (final String word : new LatinTokens(appLabel)) { + if (DEBUG_DUMP) { + Log.d(TAG, "addName word = " + word); + } + final int wordLen = StringUtils.codePointCount(word); + // Don't add single letter words, possibly confuses capitalization of i. + if (1 < wordLen && wordLen <= MAX_WORD_LENGTH) { + if (DEBUG) { + Log.d(TAG, "addName " + appLabel + ", " + word + ", " + ngramContext); + } + runGCIfRequiredLocked(true /* mindsBlockByGC */); + addUnigramLocked(word, AppsDictionaryConstantsKt.FREQUENCY_FOR_APPS, + null /* shortcut */, 0 /* shortcutFreq */, false /* isNotAWord */, + false /* isPossiblyOffensive */, + BinaryDictionary.NOT_A_VALID_TIMESTAMP); + if (ngramContext.isValid()) { + runGCIfRequiredLocked(true /* mindsBlockByGC */); + addNgramEntryLocked(ngramContext, + word, + AppsDictionaryConstantsKt.FREQUENCY_FOR_APPS_BIGRAM, + BinaryDictionary.NOT_A_VALID_TIMESTAMP); + } + ngramContext = ngramContext.getNextNgramContext( + new NgramContext.WordInfo(word)); + } + } + } +} diff --git a/app/src/main/java/helium314/keyboard/latin/AppsDictionaryConstants.kt b/app/src/main/java/helium314/keyboard/latin/AppsDictionaryConstants.kt new file mode 100644 index 00000000..5c847a82 --- /dev/null +++ b/app/src/main/java/helium314/keyboard/latin/AppsDictionaryConstants.kt @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: GPL-3.0-only + +package helium314.keyboard.latin + +/** + * Frequency for app names into the dictionary + */ +const val FREQUENCY_FOR_APPS: Int = 40 +const val FREQUENCY_FOR_APPS_BIGRAM: Int = 90 diff --git a/app/src/main/java/helium314/keyboard/latin/AppsManager.kt b/app/src/main/java/helium314/keyboard/latin/AppsManager.kt new file mode 100644 index 00000000..3c918563 --- /dev/null +++ b/app/src/main/java/helium314/keyboard/latin/AppsManager.kt @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: GPL-3.0-only + +package helium314.keyboard.latin + +import android.content.Context +import android.content.Intent +import android.content.pm.PackageManager +import android.content.pm.ResolveInfo +import java.util.HashSet + +class AppsManager(context: Context) { + private val mPackageManager: PackageManager = context.packageManager + + /** + * Returns all app labels associated with a launcher icon, sorted arbitrarily. + */ + fun getNames(): HashSet { + val filter = Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_LAUNCHER) + // activities with an entry/icon for the launcher + val launcherApps: List = mPackageManager.queryIntentActivities(filter, 0) + + val names = HashSet(launcherApps.size) + for (info in launcherApps) { + val name = info.activityInfo.loadLabel(mPackageManager).toString() + names.add(name) + } + + return names + } +} diff --git a/app/src/main/java/helium314/keyboard/latin/Dictionary.java b/app/src/main/java/helium314/keyboard/latin/Dictionary.java index 108fdf4f..88c52dbb 100644 --- a/app/src/main/java/helium314/keyboard/latin/Dictionary.java +++ b/app/src/main/java/helium314/keyboard/latin/Dictionary.java @@ -48,6 +48,7 @@ public abstract class Dictionary { // phony dictionary instances for them. public static final String TYPE_MAIN = "main"; public static final String TYPE_CONTACTS = "contacts"; + public static final String TYPE_APPS = "apps"; // User dictionary, the system-managed one. public static final String TYPE_USER = "user"; // User history dictionary internal to LatinIME. @@ -173,6 +174,7 @@ public abstract class Dictionary { case TYPE_USER_TYPED, TYPE_USER, TYPE_CONTACTS, + TYPE_APPS, TYPE_USER_HISTORY -> true; default -> false; }; diff --git a/app/src/main/java/helium314/keyboard/latin/DictionaryFacilitator.java b/app/src/main/java/helium314/keyboard/latin/DictionaryFacilitator.java index 4d8ca5f6..783235a1 100644 --- a/app/src/main/java/helium314/keyboard/latin/DictionaryFacilitator.java +++ b/app/src/main/java/helium314/keyboard/latin/DictionaryFacilitator.java @@ -32,11 +32,13 @@ public interface DictionaryFacilitator { String[] ALL_DICTIONARY_TYPES = new String[] { Dictionary.TYPE_MAIN, Dictionary.TYPE_CONTACTS, + Dictionary.TYPE_APPS, Dictionary.TYPE_USER_HISTORY, Dictionary.TYPE_USER}; String[] DYNAMIC_DICTIONARY_TYPES = new String[] { Dictionary.TYPE_CONTACTS, + Dictionary.TYPE_APPS, Dictionary.TYPE_USER_HISTORY, Dictionary.TYPE_USER}; @@ -87,6 +89,7 @@ public interface DictionaryFacilitator { boolean usesSameSettings( @NonNull final List locales, final boolean contacts, + final boolean apps, final boolean personalization, @Nullable final String account ); @@ -97,6 +100,7 @@ public interface DictionaryFacilitator { final Context context, final Locale newLocale, final boolean useContactsDict, + final boolean useAppsDict, final boolean usePersonalizedDicts, final boolean forceReloadMainDictionary, @Nullable final String account, diff --git a/app/src/main/java/helium314/keyboard/latin/DictionaryFacilitatorImpl.java b/app/src/main/java/helium314/keyboard/latin/DictionaryFacilitatorImpl.java index e075b129..f1cb8a6f 100644 --- a/app/src/main/java/helium314/keyboard/latin/DictionaryFacilitatorImpl.java +++ b/app/src/main/java/helium314/keyboard/latin/DictionaryFacilitatorImpl.java @@ -16,6 +16,23 @@ import android.view.inputmethod.InputMethodSubtype; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Scanner; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + import helium314.keyboard.keyboard.Keyboard; import helium314.keyboard.latin.NgramContext.WordInfo; import helium314.keyboard.latin.SuggestedWords.SuggestedWordInfo; @@ -35,23 +52,6 @@ import helium314.keyboard.latin.utils.SubtypeSettings; import helium314.keyboard.latin.utils.SubtypeUtilsKt; import helium314.keyboard.latin.utils.SuggestionResults; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Scanner; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; - /** * Facilitates interaction with different kinds of dictionaries. Provides APIs * to instantiate and select the correct dictionaries (based on language or account), @@ -274,6 +274,10 @@ public class DictionaryFacilitatorImpl implements DictionaryFacilitator { return mDictionaryGroups.get(0).getSubDict(Dictionary.TYPE_CONTACTS) != null; } + public boolean usesApps() { + return mDictionaryGroups.get(0).getSubDict(Dictionary.TYPE_APPS) != null; + } + public boolean usesPersonalization() { return mDictionaryGroups.get(0).getSubDict(Dictionary.TYPE_USER_HISTORY) != null; } @@ -306,6 +310,7 @@ public class DictionaryFacilitatorImpl implements DictionaryFacilitator { case Dictionary.TYPE_USER_HISTORY -> UserHistoryDictionary.getDictionary(context, locale, dictFile, dictNamePrefix, account); case Dictionary.TYPE_USER -> UserBinaryDictionary.getDictionary(context, locale, dictFile, dictNamePrefix, account); case Dictionary.TYPE_CONTACTS -> ContactsBinaryDictionary.getDictionary(context, locale, dictFile, dictNamePrefix, account); + case Dictionary.TYPE_APPS -> AppsBinaryDictionary.getDictionary(context, locale, dictFile, dictNamePrefix, account); default -> null; }; } catch (final SecurityException | IllegalArgumentException e) { @@ -332,6 +337,7 @@ public class DictionaryFacilitatorImpl implements DictionaryFacilitator { final Context context, @NonNull final Locale newLocale, final boolean useContactsDict, + final boolean useAppsDict, final boolean usePersonalizedDicts, final boolean forceReloadMainDictionary, @Nullable final String account, @@ -364,6 +370,9 @@ public class DictionaryFacilitatorImpl implements DictionaryFacilitator { && PermissionsUtil.checkAllPermissionsGranted(context, Manifest.permission.READ_CONTACTS)) { subDictTypesToUse.add(Dictionary.TYPE_CONTACTS); } + if (useAppsDict) { + subDictTypesToUse.add(Dictionary.TYPE_APPS); + } if (usePersonalizedDicts) { subDictTypesToUse.add(Dictionary.TYPE_USER_HISTORY); } @@ -958,6 +967,7 @@ public class DictionaryFacilitatorImpl implements DictionaryFacilitator { if (historyDict != null) { historyDict.removeUnigramEntryDynamically(word); } + // and from personal dictionary final ExpandableBinaryDictionary userDict = group.getSubDict(Dictionary.TYPE_USER); if (userDict != null) { @@ -965,19 +975,28 @@ public class DictionaryFacilitatorImpl implements DictionaryFacilitator { } final ExpandableBinaryDictionary contactsDict = group.getSubDict(Dictionary.TYPE_CONTACTS); - if (contactsDict != null) { - if (contactsDict.isInDictionary(word)) { - contactsDict.removeUnigramEntryDynamically(word); // will be gone until next reload of dict - addToBlacklist(word, group); - return; - } - } - if (!group.hasDict(Dictionary.TYPE_MAIN, null)) + if (contactsDict != null && contactsDict.isInDictionary(word)) { + contactsDict.removeUnigramEntryDynamically(word); // will be gone until next reload of dict + addToBlacklist(word, group); return; + } + + final ExpandableBinaryDictionary appsDict = group.getSubDict(Dictionary.TYPE_APPS); + if (appsDict != null && appsDict.isInDictionary(word)) { + appsDict.removeUnigramEntryDynamically(word); // will be gone until next reload of dict + addToBlacklist(word, group); + return; + } + + if (!group.hasDict(Dictionary.TYPE_MAIN, null)) { + return; + } + if (group.getDict(Dictionary.TYPE_MAIN).isValidWord(word)) { addToBlacklist(word, group); return; } + final String lowercase = word.toLowerCase(group.mLocale); if (group.getDict(Dictionary.TYPE_MAIN).isValidWord(lowercase)) { addToBlacklist(lowercase, group); diff --git a/app/src/main/java/helium314/keyboard/latin/DictionaryFacilitatorLruCache.java b/app/src/main/java/helium314/keyboard/latin/DictionaryFacilitatorLruCache.java index 08096413..28cd88c5 100644 --- a/app/src/main/java/helium314/keyboard/latin/DictionaryFacilitatorLruCache.java +++ b/app/src/main/java/helium314/keyboard/latin/DictionaryFacilitatorLruCache.java @@ -26,6 +26,7 @@ public class DictionaryFacilitatorLruCache { private final Object mLock = new Object(); private final DictionaryFacilitator mDictionaryFacilitator; private boolean mUseContactsDictionary; + private boolean mUseAppsDictionary; private Locale mLocale; public DictionaryFacilitatorLruCache(final Context context, final String dictionaryNamePrefix) { @@ -59,7 +60,7 @@ public class DictionaryFacilitatorLruCache { if (mLocale != null) { // Note: Given that personalized dictionaries are not used here; we can pass null account. mDictionaryFacilitator.resetDictionaries(mContext, mLocale, - mUseContactsDictionary, false /* usePersonalizedDicts */, + mUseContactsDictionary, mUseAppsDictionary, false /* usePersonalizedDicts */, false /* forceReloadMainDictionary */, null /* account */, mDictionaryNamePrefix, null /* listener */); } @@ -77,6 +78,18 @@ public class DictionaryFacilitatorLruCache { } } + public void setUseAppsDictionary(final boolean useAppsDictionary) { + synchronized (mLock) { + if (mUseAppsDictionary == useAppsDictionary) { + // The value has not been changed. + return; + } + mUseAppsDictionary = useAppsDictionary; + resetDictionariesForLocaleLocked(); + waitForLoadingMainDictionary(mDictionaryFacilitator); + } + } + public DictionaryFacilitator get(final Locale locale) { synchronized (mLock) { if (!mDictionaryFacilitator.isForLocale(locale)) { diff --git a/app/src/main/java/helium314/keyboard/latin/LatinIME.java b/app/src/main/java/helium314/keyboard/latin/LatinIME.java index d2e75a45..d9326b18 100644 --- a/app/src/main/java/helium314/keyboard/latin/LatinIME.java +++ b/app/src/main/java/helium314/keyboard/latin/LatinIME.java @@ -668,6 +668,7 @@ public class LatinIME extends InputMethodService implements if (mDictionaryFacilitator.usesSameSettings( locales, mSettings.getCurrent().mUseContactsDictionary, + mSettings.getCurrent().mUseAppsDictionary, mSettings.getCurrent().mUsePersonalizedDicts, mSettings.getCurrent().mAccount )) { @@ -686,8 +687,8 @@ public class LatinIME extends InputMethodService implements private void resetDictionaryFacilitator(@NonNull final Locale locale) { final SettingsValues settingsValues = mSettings.getCurrent(); mDictionaryFacilitator.resetDictionaries(this, locale, - settingsValues.mUseContactsDictionary, settingsValues.mUsePersonalizedDicts, - false, settingsValues.mAccount, "", this); + settingsValues.mUseContactsDictionary, settingsValues.mUseAppsDictionary, + settingsValues.mUsePersonalizedDicts, false, settingsValues.mAccount, "", this); mInputLogic.mSuggest.setAutoCorrectionThreshold(settingsValues.mAutoCorrectionThreshold); } @@ -698,7 +699,7 @@ public class LatinIME extends InputMethodService implements final SettingsValues settingsValues = mSettings.getCurrent(); mDictionaryFacilitator.resetDictionaries(this /* context */, mDictionaryFacilitator.getMainLocale(), settingsValues.mUseContactsDictionary, - settingsValues.mUsePersonalizedDicts, + settingsValues.mUseAppsDictionary, settingsValues.mUsePersonalizedDicts, true /* forceReloadMainDictionary */, settingsValues.mAccount, "" /* dictNamePrefix */, this /* DictionaryInitializationListener */); diff --git a/app/src/main/java/helium314/keyboard/latin/LatinTokens.kt b/app/src/main/java/helium314/keyboard/latin/LatinTokens.kt new file mode 100644 index 00000000..2c80e584 --- /dev/null +++ b/app/src/main/java/helium314/keyboard/latin/LatinTokens.kt @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: GPL-3.0-only + +package helium314.keyboard.latin + +/** + * Tokenizes strings by groupings of non-space characters, making them iterable. Note that letters, + * punctuations, etc. are all treated the same by this construct. + */ +class LatinTokens(phrase: String) : Iterable { + private val mPhrase = phrase + private val mLength = phrase.length + private val mStartPos = phrase.indexOfFirst { !Character.isWhitespace(it) } + // the iterator should start at the first non-whitespace character + + override fun iterator() = object : Iterator { + private var startPos = mStartPos + + override fun hasNext(): Boolean { + return startPos < mLength + } + + override fun next(): String { + var endPos = startPos + + do if (++endPos >= mLength) break + while (!Character.isWhitespace(mPhrase[endPos])) + val word = mPhrase.substring(startPos, endPos) + + if (endPos < mLength) { + do if (++endPos >= mLength) break + while (Character.isWhitespace(mPhrase[endPos])) + } + startPos = endPos + + return word + } + } +} diff --git a/app/src/main/java/helium314/keyboard/latin/settings/Defaults.kt b/app/src/main/java/helium314/keyboard/latin/settings/Defaults.kt index 0301409b..c133584a 100644 --- a/app/src/main/java/helium314/keyboard/latin/settings/Defaults.kt +++ b/app/src/main/java/helium314/keyboard/latin/settings/Defaults.kt @@ -110,6 +110,7 @@ object Defaults { const val PREF_GESTURE_TRAIL_FADEOUT_DURATION = 800 const val PREF_SHOW_SETUP_WIZARD_ICON = true const val PREF_USE_CONTACTS = false + const val PREF_USE_APPS = false const val PREFS_LONG_PRESS_SYMBOLS_FOR_NUMPAD = false const val PREF_ONE_HANDED_MODE = false @SuppressLint("RtlHardcoded") diff --git a/app/src/main/java/helium314/keyboard/latin/settings/Settings.java b/app/src/main/java/helium314/keyboard/latin/settings/Settings.java index 89e93e8d..3b77b820 100644 --- a/app/src/main/java/helium314/keyboard/latin/settings/Settings.java +++ b/app/src/main/java/helium314/keyboard/latin/settings/Settings.java @@ -114,6 +114,7 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang public static final String PREF_GESTURE_TRAIL_FADEOUT_DURATION = "gesture_trail_fadeout_duration"; public static final String PREF_SHOW_SETUP_WIZARD_ICON = "show_setup_wizard_icon"; public static final String PREF_USE_CONTACTS = "use_contacts"; + public static final String PREF_USE_APPS = "use_apps"; public static final String PREFS_LONG_PRESS_SYMBOLS_FOR_NUMPAD = "long_press_symbols_for_numpad"; // one-handed mode gravity, enablement and scale, stored separately per orientation diff --git a/app/src/main/java/helium314/keyboard/latin/settings/SettingsValues.java b/app/src/main/java/helium314/keyboard/latin/settings/SettingsValues.java index 8576bc1d..911ecf0a 100644 --- a/app/src/main/java/helium314/keyboard/latin/settings/SettingsValues.java +++ b/app/src/main/java/helium314/keyboard/latin/settings/SettingsValues.java @@ -103,6 +103,7 @@ public class SettingsValues { public final int mScreenMetrics; public final boolean mAddToPersonalDictionary; public final boolean mUseContactsDictionary; + public final boolean mUseAppsDictionary; public final boolean mCustomNavBarColor; public final float mKeyboardHeightScale; public final boolean mUrlDetectionEnabled; @@ -251,6 +252,7 @@ public class SettingsValues { mPopupKeyLabelSources = SubtypeUtilsKt.getPopupKeyLabelSources(selectedSubtype, prefs); mAddToPersonalDictionary = prefs.getBoolean(Settings.PREF_ADD_TO_PERSONAL_DICTIONARY, Defaults.PREF_ADD_TO_PERSONAL_DICTIONARY); mUseContactsDictionary = SettingsValues.readUseContactsEnabled(prefs, context); + mUseAppsDictionary = prefs.getBoolean(Settings.PREF_USE_APPS, Defaults.PREF_USE_APPS); mCustomNavBarColor = prefs.getBoolean(Settings.PREF_NAVBAR_COLOR, Defaults.PREF_NAVBAR_COLOR); mNarrowKeyGaps = prefs.getBoolean(Settings.PREF_NARROW_KEY_GAPS, Defaults.PREF_NARROW_KEY_GAPS); mSettingsValuesForSuggestion = new SettingsValuesForSuggestion( @@ -334,11 +336,12 @@ public class SettingsValues { return mDisplayOrientation == configuration.orientation; } - private static boolean readUseContactsEnabled(final SharedPreferences prefs, final Context context) { + private static boolean readUseContactsEnabled(final SharedPreferences prefs, final Context ctx) { final boolean setting = prefs.getBoolean(Settings.PREF_USE_CONTACTS, Defaults.PREF_USE_CONTACTS); if (!setting) return false; - if (PermissionsUtil.checkAllPermissionsGranted(context, Manifest.permission.READ_CONTACTS)) + if (PermissionsUtil.checkAllPermissionsGranted(ctx, Manifest.permission.READ_CONTACTS)) { return true; + } // disable if permission not granted prefs.edit().putBoolean(Settings.PREF_USE_CONTACTS, false).apply(); return false; diff --git a/app/src/main/java/helium314/keyboard/latin/spellcheck/AndroidSpellCheckerService.java b/app/src/main/java/helium314/keyboard/latin/spellcheck/AndroidSpellCheckerService.java index 80756179..73d66ad3 100644 --- a/app/src/main/java/helium314/keyboard/latin/spellcheck/AndroidSpellCheckerService.java +++ b/app/src/main/java/helium314/keyboard/latin/spellcheck/AndroidSpellCheckerService.java @@ -83,6 +83,7 @@ public final class AndroidSpellCheckerService extends SpellCheckerService final SharedPreferences prefs = KtxKt.prefs(this); prefs.registerOnSharedPreferenceChangeListener(this); onSharedPreferenceChanged(prefs, Settings.PREF_USE_CONTACTS); + onSharedPreferenceChanged(prefs, Settings.PREF_USE_APPS); final boolean blockOffensive = prefs.getBoolean(Settings.PREF_BLOCK_POTENTIALLY_OFFENSIVE, Defaults.PREF_BLOCK_POTENTIALLY_OFFENSIVE); mSettingsValuesForSuggestion = new SettingsValuesForSuggestion(blockOffensive, false); } @@ -93,13 +94,19 @@ public final class AndroidSpellCheckerService extends SpellCheckerService @Override public void onSharedPreferenceChanged(final SharedPreferences prefs, final String key) { - if (Settings.PREF_USE_CONTACTS.equals(key)) { + if (key != null) switch (key) { + case Settings.PREF_USE_CONTACTS -> { final boolean useContactsDictionary = prefs.getBoolean(Settings.PREF_USE_CONTACTS, Defaults.PREF_USE_CONTACTS); mDictionaryFacilitatorCache.setUseContactsDictionary(useContactsDictionary); - } else if (Settings.PREF_BLOCK_POTENTIALLY_OFFENSIVE.equals(key)) { + } + case Settings.PREF_USE_APPS -> { + final boolean useAppsDictionary = prefs.getBoolean(Settings.PREF_USE_APPS, Defaults.PREF_USE_APPS); + mDictionaryFacilitatorCache.setUseAppsDictionary(useAppsDictionary); + } + case Settings.PREF_BLOCK_POTENTIALLY_OFFENSIVE -> { final boolean blockOffensive = prefs.getBoolean(Settings.PREF_BLOCK_POTENTIALLY_OFFENSIVE, Defaults.PREF_BLOCK_POTENTIALLY_OFFENSIVE); mSettingsValuesForSuggestion = new SettingsValuesForSuggestion(blockOffensive, false); - } + }} } @Override diff --git a/app/src/main/java/helium314/keyboard/settings/SettingsActivity.kt b/app/src/main/java/helium314/keyboard/settings/SettingsActivity.kt index 498b7293..387eb3bb 100644 --- a/app/src/main/java/helium314/keyboard/settings/SettingsActivity.kt +++ b/app/src/main/java/helium314/keyboard/settings/SettingsActivity.kt @@ -99,6 +99,7 @@ class SettingsActivity : ComponentActivity(), SharedPreferences.OnSharedPreferen if (spellchecker) Column { // lazy way of implementing spell checker settings settingsContainer[Settings.PREF_USE_CONTACTS]!!.Preference() + settingsContainer[Settings.PREF_USE_APPS]!!.Preference() settingsContainer[Settings.PREF_BLOCK_POTENTIALLY_OFFENSIVE]!!.Preference() } else diff --git a/app/src/main/java/helium314/keyboard/settings/screens/TextCorrectionScreen.kt b/app/src/main/java/helium314/keyboard/settings/screens/TextCorrectionScreen.kt index 97d64188..3d726cbe 100644 --- a/app/src/main/java/helium314/keyboard/settings/screens/TextCorrectionScreen.kt +++ b/app/src/main/java/helium314/keyboard/settings/screens/TextCorrectionScreen.kt @@ -68,6 +68,7 @@ fun TextCorrectionScreen( Settings.PREF_BIGRAM_PREDICTIONS, Settings.PREF_SUGGEST_CLIPBOARD_CONTENT, Settings.PREF_USE_CONTACTS, + Settings.PREF_USE_APPS, if (prefs.getBoolean(Settings.PREF_KEY_USE_PERSONALIZED_DICTS, Defaults.PREF_KEY_USE_PERSONALIZED_DICTS)) Settings.PREF_ADD_TO_PERSONAL_DICTIONARY else null ) @@ -195,6 +196,11 @@ fun createCorrectionSettings(context: Context) = listOf( } ) }, + Setting(context, Settings.PREF_USE_APPS, + R.string.use_apps_dict, R.string.use_apps_dict_summary + ) { setting -> + SwitchPreference(setting, Defaults.PREF_USE_APPS) + }, Setting(context, Settings.PREF_ADD_TO_PERSONAL_DICTIONARY, R.string.add_to_personal_dictionary, R.string.add_to_personal_dictionary_summary ) { diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 903b3cbd..425a70b6 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -76,6 +76,10 @@ Look up contact names Use names from Contacts for suggestions and corrections + + Look up app names + + Use names of installed apps for suggestions and corrections Personalized suggestions From 00f508866cdd02158c03c50bf87b57308c671a19 Mon Sep 17 00:00:00 2001 From: devycarol <[email protected]> Date: Tue, 11 Mar 2025 18:07:35 -0600 Subject: [PATCH 3/3] fix build? --- .../helium314/keyboard/latin/DictionaryFacilitatorImpl.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/helium314/keyboard/latin/DictionaryFacilitatorImpl.java b/app/src/main/java/helium314/keyboard/latin/DictionaryFacilitatorImpl.java index f1cb8a6f..aa96e5b7 100644 --- a/app/src/main/java/helium314/keyboard/latin/DictionaryFacilitatorImpl.java +++ b/app/src/main/java/helium314/keyboard/latin/DictionaryFacilitatorImpl.java @@ -289,8 +289,9 @@ public class DictionaryFacilitatorImpl implements DictionaryFacilitator { @Override public boolean usesSameSettings(@NonNull final List locales, final boolean contacts, - final boolean personalization, @Nullable final String account) { - final boolean first = usesContacts() == contacts && usesPersonalization() == personalization + final boolean apps, final boolean personalization, @Nullable final String account) { + final boolean first = usesContacts() == contacts && usesApps() == apps + && usesPersonalization() == personalization && TextUtils.equals(mDictionaryGroups.get(0).mAccount, account) && locales.size() == mDictionaryGroups.size(); if (!first) return false;