app names dictionary similar to contacts dictionary

This commit is contained in:
devycarol 2025-02-14 22:45:22 -07:00
parent a566e4d8a2
commit b0827c7967
17 changed files with 270 additions and 35 deletions

View file

@ -100,5 +100,11 @@ SPDX-License-Identifier: Apache-2.0 AND GPL-3.0-only
<intent> <intent>
<action android:name="android.view.InputMethod" /> <action android:name="android.view.InputMethod" />
</intent> </intent>
<!-- To detect names of installed apps -->
<intent>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent>
</queries> </queries>
</manifest> </manifest>

View file

@ -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));
}
}
}
}

View file

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

View file

@ -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<String> {
val filter = Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_LAUNCHER)
// activities with an entry/icon for the launcher
val launcherApps: List<ResolveInfo> = mPackageManager.queryIntentActivities(filter, 0)
val names = HashSet<String>(launcherApps.size)
for (info in launcherApps) {
val name = info.activityInfo.loadLabel(mPackageManager).toString()
names.add(name)
}
return names
}
}

View file

@ -48,6 +48,7 @@ public abstract class Dictionary {
// phony dictionary instances for them. // phony dictionary instances for them.
public static final String TYPE_MAIN = "main"; public static final String TYPE_MAIN = "main";
public static final String TYPE_CONTACTS = "contacts"; public static final String TYPE_CONTACTS = "contacts";
public static final String TYPE_APPS = "apps";
// User dictionary, the system-managed one. // User dictionary, the system-managed one.
public static final String TYPE_USER = "user"; public static final String TYPE_USER = "user";
// User history dictionary internal to LatinIME. // User history dictionary internal to LatinIME.
@ -173,6 +174,7 @@ public abstract class Dictionary {
case TYPE_USER_TYPED, case TYPE_USER_TYPED,
TYPE_USER, TYPE_USER,
TYPE_CONTACTS, TYPE_CONTACTS,
TYPE_APPS,
TYPE_USER_HISTORY -> true; TYPE_USER_HISTORY -> true;
default -> false; default -> false;
}; };

View file

@ -32,11 +32,13 @@ public interface DictionaryFacilitator {
String[] ALL_DICTIONARY_TYPES = new String[] { String[] ALL_DICTIONARY_TYPES = new String[] {
Dictionary.TYPE_MAIN, Dictionary.TYPE_MAIN,
Dictionary.TYPE_CONTACTS, Dictionary.TYPE_CONTACTS,
Dictionary.TYPE_APPS,
Dictionary.TYPE_USER_HISTORY, Dictionary.TYPE_USER_HISTORY,
Dictionary.TYPE_USER}; Dictionary.TYPE_USER};
String[] DYNAMIC_DICTIONARY_TYPES = new String[] { String[] DYNAMIC_DICTIONARY_TYPES = new String[] {
Dictionary.TYPE_CONTACTS, Dictionary.TYPE_CONTACTS,
Dictionary.TYPE_APPS,
Dictionary.TYPE_USER_HISTORY, Dictionary.TYPE_USER_HISTORY,
Dictionary.TYPE_USER}; Dictionary.TYPE_USER};
@ -87,6 +89,7 @@ public interface DictionaryFacilitator {
boolean usesSameSettings( boolean usesSameSettings(
@NonNull final List<Locale> locales, @NonNull final List<Locale> locales,
final boolean contacts, final boolean contacts,
final boolean apps,
final boolean personalization, final boolean personalization,
@Nullable final String account @Nullable final String account
); );
@ -97,6 +100,7 @@ public interface DictionaryFacilitator {
final Context context, final Context context,
final Locale newLocale, final Locale newLocale,
final boolean useContactsDict, final boolean useContactsDict,
final boolean useAppsDict,
final boolean usePersonalizedDicts, final boolean usePersonalizedDicts,
final boolean forceReloadMainDictionary, final boolean forceReloadMainDictionary,
@Nullable final String account, @Nullable final String account,

View file

@ -16,6 +16,23 @@ import android.view.inputmethod.InputMethodSubtype;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; 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.keyboard.Keyboard;
import helium314.keyboard.latin.NgramContext.WordInfo; import helium314.keyboard.latin.NgramContext.WordInfo;
import helium314.keyboard.latin.SuggestedWords.SuggestedWordInfo; 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.SubtypeUtilsKt;
import helium314.keyboard.latin.utils.SuggestionResults; 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 * Facilitates interaction with different kinds of dictionaries. Provides APIs
* to instantiate and select the correct dictionaries (based on language or account), * 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; return mDictionaryGroups.get(0).getSubDict(Dictionary.TYPE_CONTACTS) != null;
} }
public boolean usesApps() {
return mDictionaryGroups.get(0).getSubDict(Dictionary.TYPE_APPS) != null;
}
public boolean usesPersonalization() { public boolean usesPersonalization() {
return mDictionaryGroups.get(0).getSubDict(Dictionary.TYPE_USER_HISTORY) != null; 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_HISTORY -> UserHistoryDictionary.getDictionary(context, locale, dictFile, dictNamePrefix, account);
case Dictionary.TYPE_USER -> UserBinaryDictionary.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_CONTACTS -> ContactsBinaryDictionary.getDictionary(context, locale, dictFile, dictNamePrefix, account);
case Dictionary.TYPE_APPS -> AppsBinaryDictionary.getDictionary(context, locale, dictFile, dictNamePrefix, account);
default -> null; default -> null;
}; };
} catch (final SecurityException | IllegalArgumentException e) { } catch (final SecurityException | IllegalArgumentException e) {
@ -332,6 +337,7 @@ public class DictionaryFacilitatorImpl implements DictionaryFacilitator {
final Context context, final Context context,
@NonNull final Locale newLocale, @NonNull final Locale newLocale,
final boolean useContactsDict, final boolean useContactsDict,
final boolean useAppsDict,
final boolean usePersonalizedDicts, final boolean usePersonalizedDicts,
final boolean forceReloadMainDictionary, final boolean forceReloadMainDictionary,
@Nullable final String account, @Nullable final String account,
@ -364,6 +370,9 @@ public class DictionaryFacilitatorImpl implements DictionaryFacilitator {
&& PermissionsUtil.checkAllPermissionsGranted(context, Manifest.permission.READ_CONTACTS)) { && PermissionsUtil.checkAllPermissionsGranted(context, Manifest.permission.READ_CONTACTS)) {
subDictTypesToUse.add(Dictionary.TYPE_CONTACTS); subDictTypesToUse.add(Dictionary.TYPE_CONTACTS);
} }
if (useAppsDict) {
subDictTypesToUse.add(Dictionary.TYPE_APPS);
}
if (usePersonalizedDicts) { if (usePersonalizedDicts) {
subDictTypesToUse.add(Dictionary.TYPE_USER_HISTORY); subDictTypesToUse.add(Dictionary.TYPE_USER_HISTORY);
} }
@ -958,6 +967,7 @@ public class DictionaryFacilitatorImpl implements DictionaryFacilitator {
if (historyDict != null) { if (historyDict != null) {
historyDict.removeUnigramEntryDynamically(word); historyDict.removeUnigramEntryDynamically(word);
} }
// and from personal dictionary // and from personal dictionary
final ExpandableBinaryDictionary userDict = group.getSubDict(Dictionary.TYPE_USER); final ExpandableBinaryDictionary userDict = group.getSubDict(Dictionary.TYPE_USER);
if (userDict != null) { if (userDict != null) {
@ -965,19 +975,28 @@ public class DictionaryFacilitatorImpl implements DictionaryFacilitator {
} }
final ExpandableBinaryDictionary contactsDict = group.getSubDict(Dictionary.TYPE_CONTACTS); final ExpandableBinaryDictionary contactsDict = group.getSubDict(Dictionary.TYPE_CONTACTS);
if (contactsDict != null) { if (contactsDict != null && contactsDict.isInDictionary(word)) {
if (contactsDict.isInDictionary(word)) {
contactsDict.removeUnigramEntryDynamically(word); // will be gone until next reload of dict contactsDict.removeUnigramEntryDynamically(word); // will be gone until next reload of dict
addToBlacklist(word, group); addToBlacklist(word, group);
return; return;
} }
}
if (!group.hasDict(Dictionary.TYPE_MAIN, null)) 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; return;
}
if (!group.hasDict(Dictionary.TYPE_MAIN, null)) {
return;
}
if (group.getDict(Dictionary.TYPE_MAIN).isValidWord(word)) { if (group.getDict(Dictionary.TYPE_MAIN).isValidWord(word)) {
addToBlacklist(word, group); addToBlacklist(word, group);
return; return;
} }
final String lowercase = word.toLowerCase(group.mLocale); final String lowercase = word.toLowerCase(group.mLocale);
if (group.getDict(Dictionary.TYPE_MAIN).isValidWord(lowercase)) { if (group.getDict(Dictionary.TYPE_MAIN).isValidWord(lowercase)) {
addToBlacklist(lowercase, group); addToBlacklist(lowercase, group);

View file

@ -26,6 +26,7 @@ public class DictionaryFacilitatorLruCache {
private final Object mLock = new Object(); private final Object mLock = new Object();
private final DictionaryFacilitator mDictionaryFacilitator; private final DictionaryFacilitator mDictionaryFacilitator;
private boolean mUseContactsDictionary; private boolean mUseContactsDictionary;
private boolean mUseAppsDictionary;
private Locale mLocale; private Locale mLocale;
public DictionaryFacilitatorLruCache(final Context context, final String dictionaryNamePrefix) { public DictionaryFacilitatorLruCache(final Context context, final String dictionaryNamePrefix) {
@ -59,7 +60,7 @@ public class DictionaryFacilitatorLruCache {
if (mLocale != null) { if (mLocale != null) {
// Note: Given that personalized dictionaries are not used here; we can pass null account. // Note: Given that personalized dictionaries are not used here; we can pass null account.
mDictionaryFacilitator.resetDictionaries(mContext, mLocale, mDictionaryFacilitator.resetDictionaries(mContext, mLocale,
mUseContactsDictionary, false /* usePersonalizedDicts */, mUseContactsDictionary, mUseAppsDictionary, false /* usePersonalizedDicts */,
false /* forceReloadMainDictionary */, null /* account */, false /* forceReloadMainDictionary */, null /* account */,
mDictionaryNamePrefix, null /* listener */); 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) { public DictionaryFacilitator get(final Locale locale) {
synchronized (mLock) { synchronized (mLock) {
if (!mDictionaryFacilitator.isForLocale(locale)) { if (!mDictionaryFacilitator.isForLocale(locale)) {

View file

@ -668,6 +668,7 @@ public class LatinIME extends InputMethodService implements
if (mDictionaryFacilitator.usesSameSettings( if (mDictionaryFacilitator.usesSameSettings(
locales, locales,
mSettings.getCurrent().mUseContactsDictionary, mSettings.getCurrent().mUseContactsDictionary,
mSettings.getCurrent().mUseAppsDictionary,
mSettings.getCurrent().mUsePersonalizedDicts, mSettings.getCurrent().mUsePersonalizedDicts,
mSettings.getCurrent().mAccount mSettings.getCurrent().mAccount
)) { )) {
@ -686,8 +687,8 @@ public class LatinIME extends InputMethodService implements
private void resetDictionaryFacilitator(@NonNull final Locale locale) { private void resetDictionaryFacilitator(@NonNull final Locale locale) {
final SettingsValues settingsValues = mSettings.getCurrent(); final SettingsValues settingsValues = mSettings.getCurrent();
mDictionaryFacilitator.resetDictionaries(this, locale, mDictionaryFacilitator.resetDictionaries(this, locale,
settingsValues.mUseContactsDictionary, settingsValues.mUsePersonalizedDicts, settingsValues.mUseContactsDictionary, settingsValues.mUseAppsDictionary,
false, settingsValues.mAccount, "", this); settingsValues.mUsePersonalizedDicts, false, settingsValues.mAccount, "", this);
mInputLogic.mSuggest.setAutoCorrectionThreshold(settingsValues.mAutoCorrectionThreshold); mInputLogic.mSuggest.setAutoCorrectionThreshold(settingsValues.mAutoCorrectionThreshold);
} }
@ -698,7 +699,7 @@ public class LatinIME extends InputMethodService implements
final SettingsValues settingsValues = mSettings.getCurrent(); final SettingsValues settingsValues = mSettings.getCurrent();
mDictionaryFacilitator.resetDictionaries(this /* context */, mDictionaryFacilitator.resetDictionaries(this /* context */,
mDictionaryFacilitator.getMainLocale(), settingsValues.mUseContactsDictionary, mDictionaryFacilitator.getMainLocale(), settingsValues.mUseContactsDictionary,
settingsValues.mUsePersonalizedDicts, settingsValues.mUseAppsDictionary, settingsValues.mUsePersonalizedDicts,
true /* forceReloadMainDictionary */, true /* forceReloadMainDictionary */,
settingsValues.mAccount, "" /* dictNamePrefix */, settingsValues.mAccount, "" /* dictNamePrefix */,
this /* DictionaryInitializationListener */); this /* DictionaryInitializationListener */);

View file

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

View file

@ -110,6 +110,7 @@ object Defaults {
const val PREF_GESTURE_TRAIL_FADEOUT_DURATION = 800 const val PREF_GESTURE_TRAIL_FADEOUT_DURATION = 800
const val PREF_SHOW_SETUP_WIZARD_ICON = true const val PREF_SHOW_SETUP_WIZARD_ICON = true
const val PREF_USE_CONTACTS = false const val PREF_USE_CONTACTS = false
const val PREF_USE_APPS = false
const val PREFS_LONG_PRESS_SYMBOLS_FOR_NUMPAD = false const val PREFS_LONG_PRESS_SYMBOLS_FOR_NUMPAD = false
const val PREF_ONE_HANDED_MODE = false const val PREF_ONE_HANDED_MODE = false
@SuppressLint("RtlHardcoded") @SuppressLint("RtlHardcoded")

View file

@ -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_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_SHOW_SETUP_WIZARD_ICON = "show_setup_wizard_icon";
public static final String PREF_USE_CONTACTS = "use_contacts"; 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"; 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 // one-handed mode gravity, enablement and scale, stored separately per orientation

View file

@ -103,6 +103,7 @@ public class SettingsValues {
public final int mScreenMetrics; public final int mScreenMetrics;
public final boolean mAddToPersonalDictionary; public final boolean mAddToPersonalDictionary;
public final boolean mUseContactsDictionary; public final boolean mUseContactsDictionary;
public final boolean mUseAppsDictionary;
public final boolean mCustomNavBarColor; public final boolean mCustomNavBarColor;
public final float mKeyboardHeightScale; public final float mKeyboardHeightScale;
public final boolean mUrlDetectionEnabled; public final boolean mUrlDetectionEnabled;
@ -251,6 +252,7 @@ public class SettingsValues {
mPopupKeyLabelSources = SubtypeUtilsKt.getPopupKeyLabelSources(selectedSubtype, prefs); mPopupKeyLabelSources = SubtypeUtilsKt.getPopupKeyLabelSources(selectedSubtype, prefs);
mAddToPersonalDictionary = prefs.getBoolean(Settings.PREF_ADD_TO_PERSONAL_DICTIONARY, Defaults.PREF_ADD_TO_PERSONAL_DICTIONARY); mAddToPersonalDictionary = prefs.getBoolean(Settings.PREF_ADD_TO_PERSONAL_DICTIONARY, Defaults.PREF_ADD_TO_PERSONAL_DICTIONARY);
mUseContactsDictionary = SettingsValues.readUseContactsEnabled(prefs, context); 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); mCustomNavBarColor = prefs.getBoolean(Settings.PREF_NAVBAR_COLOR, Defaults.PREF_NAVBAR_COLOR);
mNarrowKeyGaps = prefs.getBoolean(Settings.PREF_NARROW_KEY_GAPS, Defaults.PREF_NARROW_KEY_GAPS); mNarrowKeyGaps = prefs.getBoolean(Settings.PREF_NARROW_KEY_GAPS, Defaults.PREF_NARROW_KEY_GAPS);
mSettingsValuesForSuggestion = new SettingsValuesForSuggestion( mSettingsValuesForSuggestion = new SettingsValuesForSuggestion(
@ -334,11 +336,12 @@ public class SettingsValues {
return mDisplayOrientation == configuration.orientation; 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); final boolean setting = prefs.getBoolean(Settings.PREF_USE_CONTACTS, Defaults.PREF_USE_CONTACTS);
if (!setting) return false; if (!setting) return false;
if (PermissionsUtil.checkAllPermissionsGranted(context, Manifest.permission.READ_CONTACTS)) if (PermissionsUtil.checkAllPermissionsGranted(ctx, Manifest.permission.READ_CONTACTS)) {
return true; return true;
}
// disable if permission not granted // disable if permission not granted
prefs.edit().putBoolean(Settings.PREF_USE_CONTACTS, false).apply(); prefs.edit().putBoolean(Settings.PREF_USE_CONTACTS, false).apply();
return false; return false;

View file

@ -83,6 +83,7 @@ public final class AndroidSpellCheckerService extends SpellCheckerService
final SharedPreferences prefs = KtxKt.prefs(this); final SharedPreferences prefs = KtxKt.prefs(this);
prefs.registerOnSharedPreferenceChangeListener(this); prefs.registerOnSharedPreferenceChangeListener(this);
onSharedPreferenceChanged(prefs, Settings.PREF_USE_CONTACTS); 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); final boolean blockOffensive = prefs.getBoolean(Settings.PREF_BLOCK_POTENTIALLY_OFFENSIVE, Defaults.PREF_BLOCK_POTENTIALLY_OFFENSIVE);
mSettingsValuesForSuggestion = new SettingsValuesForSuggestion(blockOffensive, false); mSettingsValuesForSuggestion = new SettingsValuesForSuggestion(blockOffensive, false);
} }
@ -93,13 +94,19 @@ public final class AndroidSpellCheckerService extends SpellCheckerService
@Override @Override
public void onSharedPreferenceChanged(final SharedPreferences prefs, final String key) { 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); final boolean useContactsDictionary = prefs.getBoolean(Settings.PREF_USE_CONTACTS, Defaults.PREF_USE_CONTACTS);
mDictionaryFacilitatorCache.setUseContactsDictionary(useContactsDictionary); 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); final boolean blockOffensive = prefs.getBoolean(Settings.PREF_BLOCK_POTENTIALLY_OFFENSIVE, Defaults.PREF_BLOCK_POTENTIALLY_OFFENSIVE);
mSettingsValuesForSuggestion = new SettingsValuesForSuggestion(blockOffensive, false); mSettingsValuesForSuggestion = new SettingsValuesForSuggestion(blockOffensive, false);
} }}
} }
@Override @Override

View file

@ -99,6 +99,7 @@ class SettingsActivity : ComponentActivity(), SharedPreferences.OnSharedPreferen
if (spellchecker) if (spellchecker)
Column { // lazy way of implementing spell checker settings Column { // lazy way of implementing spell checker settings
settingsContainer[Settings.PREF_USE_CONTACTS]!!.Preference() settingsContainer[Settings.PREF_USE_CONTACTS]!!.Preference()
settingsContainer[Settings.PREF_USE_APPS]!!.Preference()
settingsContainer[Settings.PREF_BLOCK_POTENTIALLY_OFFENSIVE]!!.Preference() settingsContainer[Settings.PREF_BLOCK_POTENTIALLY_OFFENSIVE]!!.Preference()
} }
else else

View file

@ -68,6 +68,7 @@ fun TextCorrectionScreen(
Settings.PREF_BIGRAM_PREDICTIONS, Settings.PREF_BIGRAM_PREDICTIONS,
Settings.PREF_SUGGEST_CLIPBOARD_CONTENT, Settings.PREF_SUGGEST_CLIPBOARD_CONTENT,
Settings.PREF_USE_CONTACTS, Settings.PREF_USE_CONTACTS,
Settings.PREF_USE_APPS,
if (prefs.getBoolean(Settings.PREF_KEY_USE_PERSONALIZED_DICTS, Defaults.PREF_KEY_USE_PERSONALIZED_DICTS)) if (prefs.getBoolean(Settings.PREF_KEY_USE_PERSONALIZED_DICTS, Defaults.PREF_KEY_USE_PERSONALIZED_DICTS))
Settings.PREF_ADD_TO_PERSONAL_DICTIONARY else null 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, Setting(context, Settings.PREF_ADD_TO_PERSONAL_DICTIONARY,
R.string.add_to_personal_dictionary, R.string.add_to_personal_dictionary_summary R.string.add_to_personal_dictionary, R.string.add_to_personal_dictionary_summary
) { ) {

View file

@ -76,6 +76,10 @@
<string name="use_contacts_dict">Look up contact names</string> <string name="use_contacts_dict">Look up contact names</string>
<!-- Description for option enabling or disabling the use of names of people in Contacts for suggestion and correction --> <!-- Description for option enabling or disabling the use of names of people in Contacts for suggestion and correction -->
<string name="use_contacts_dict_summary">Use names from Contacts for suggestions and corrections</string> <string name="use_contacts_dict_summary">Use names from Contacts for suggestions and corrections</string>
<!-- Option name for enabling or disabling the use of installed app names for suggestion and correction -->
<string name="use_apps_dict">Look up app names</string>
<!-- Description for option enabling or disabling the use of installed app names for suggestion and correction -->
<string name="use_apps_dict_summary">Use names of installed apps for suggestions and corrections</string>
<!-- Option name for enabling the use by the keyboards of sent/received messages, e-mail and typing history to improve suggestion accuracy --> <!-- Option name for enabling the use by the keyboards of sent/received messages, e-mail and typing history to improve suggestion accuracy -->
<string name="use_personalized_dicts">Personalized suggestions</string> <string name="use_personalized_dicts">Personalized suggestions</string>
<!-- Dialog message informing that dictionaries will be deleted on changing the setting --> <!-- Dialog message informing that dictionaries will be deleted on changing the setting -->