mirror of
https://github.com/Helium314/HeliBoard.git
synced 2025-04-23 15:49:09 +00:00
app names dictionary similar to contacts dictionary
This commit is contained in:
parent
a566e4d8a2
commit
b0827c7967
17 changed files with 270 additions and 35 deletions
|
@ -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>
|
||||||
|
|
|
@ -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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
30
app/src/main/java/helium314/keyboard/latin/AppsManager.kt
Normal file
30
app/src/main/java/helium314/keyboard/latin/AppsManager.kt
Normal 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
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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)) {
|
||||||
|
|
|
@ -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 */);
|
||||||
|
|
38
app/src/main/java/helium314/keyboard/latin/LatinTokens.kt
Normal file
38
app/src/main/java/helium314/keyboard/latin/LatinTokens.kt
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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")
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
) {
|
) {
|
||||||
|
|
|
@ -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 -->
|
||||||
|
|
Loading…
Add table
Reference in a new issue