diff options
author | 2024-12-16 21:45:41 -0500 | |
---|---|---|
committer | 2025-01-11 14:17:35 -0500 | |
commit | e9a0e66716dab4dd3184d009d8920de1961efdfa (patch) | |
tree | 02dcc096643d74645bf28459c2834c3d4a2ad7f2 /java/src/org/kelar/inputmethod/latin/ContactsBinaryDictionary.java | |
parent | fb3b9360d70596d7e921de8bf7d3ca99564a077e (diff) | |
download | latinime-e9a0e66716dab4dd3184d009d8920de1961efdfa.tar.gz latinime-e9a0e66716dab4dd3184d009d8920de1961efdfa.tar.xz latinime-e9a0e66716dab4dd3184d009d8920de1961efdfa.zip |
Rename to Kelar Keyboard (org.kelar.inputmethod.latin)
Diffstat (limited to 'java/src/org/kelar/inputmethod/latin/ContactsBinaryDictionary.java')
-rw-r--r-- | java/src/org/kelar/inputmethod/latin/ContactsBinaryDictionary.java | 176 |
1 files changed, 176 insertions, 0 deletions
diff --git a/java/src/org/kelar/inputmethod/latin/ContactsBinaryDictionary.java b/java/src/org/kelar/inputmethod/latin/ContactsBinaryDictionary.java new file mode 100644 index 000000000..97f465095 --- /dev/null +++ b/java/src/org/kelar/inputmethod/latin/ContactsBinaryDictionary.java @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.kelar.inputmethod.latin; + +import android.Manifest; +import android.content.Context; +import android.net.Uri; +import android.provider.ContactsContract; +import android.provider.ContactsContract.Contacts; +import android.util.Log; + +import org.kelar.inputmethod.annotations.ExternallyReferenced; +import org.kelar.inputmethod.latin.ContactsManager.ContactsChangedListener; +import org.kelar.inputmethod.latin.common.StringUtils; +import org.kelar.inputmethod.latin.permissions.PermissionsUtil; +import org.kelar.inputmethod.latin.personalization.AccountUtils; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +import javax.annotation.Nullable; + +public class ContactsBinaryDictionary extends ExpandableBinaryDictionary + implements ContactsChangedListener { + private static final String TAG = ContactsBinaryDictionary.class.getSimpleName(); + private static final String NAME = "contacts"; + + private static final boolean DEBUG = false; + private static final boolean DEBUG_DUMP = false; + + /** + * Whether to use "firstname lastname" in bigram predictions. + */ + private final boolean mUseFirstLastBigrams; + private final ContactsManager mContactsManager; + + protected ContactsBinaryDictionary(final Context context, final Locale locale, + final File dictFile, final String name) { + super(context, getDictName(name, locale, dictFile), locale, Dictionary.TYPE_CONTACTS, + dictFile); + mUseFirstLastBigrams = ContactsDictionaryUtils.useFirstLastBigramsForLocale(locale); + mContactsManager = new ContactsManager(context); + mContactsManager.registerForUpdates(this /* listener */); + reloadDictionaryIfRequired(); + } + + // Note: This method is called by {@link DictionaryFacilitator} using Java reflection. + @ExternallyReferenced + public static ContactsBinaryDictionary getDictionary(final Context context, final Locale locale, + final File dictFile, final String dictNamePrefix, @Nullable final String account) { + return new ContactsBinaryDictionary(context, locale, dictFile, dictNamePrefix + NAME); + } + + @Override + public synchronized void close() { + mContactsManager.close(); + super.close(); + } + + /** + * 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() { + loadDeviceAccountsEmailAddressesLocked(); + loadDictionaryForUriLocked(ContactsContract.Profile.CONTENT_URI); + // TODO: Switch this URL to the newer ContactsContract too + loadDictionaryForUriLocked(Contacts.CONTENT_URI); + } + + /** + * Loads device accounts to the dictionary. + */ + private void loadDeviceAccountsEmailAddressesLocked() { + final List<String> accountVocabulary = + AccountUtils.getDeviceAccountsEmailAddresses(mContext); + if (accountVocabulary == null || accountVocabulary.isEmpty()) { + return; + } + for (String word : accountVocabulary) { + if (DEBUG) { + Log.d(TAG, "loadAccountVocabulary: " + word); + } + runGCIfRequiredLocked(true /* mindsBlockByGC */); + addUnigramLocked(word, ContactsDictionaryConstants.FREQUENCY_FOR_CONTACTS, + false /* isNotAWord */, false /* isPossiblyOffensive */, + BinaryDictionary.NOT_A_VALID_TIMESTAMP); + } + } + + /** + * Loads data within content providers to the dictionary. + */ + private void loadDictionaryForUriLocked(final Uri uri) { + if (!PermissionsUtil.checkAllPermissionsGranted( + mContext, Manifest.permission.READ_CONTACTS)) { + Log.i(TAG, "No permission to read contacts. Not loading the Dictionary."); + } + + final ArrayList<String> validNames = mContactsManager.getValidNames(uri); + for (final String name : validNames) { + addNameLocked(name); + } + if (uri.equals(Contacts.CONTENT_URI)) { + // Since we were able to add content successfully, update the local + // state of the manager. + mContactsManager.updateLocalState(validNames); + } + } + + /** + * Adds the words in a name (e.g., firstname/lastname) to the binary dictionary along with their + * bigrams depending on locale. + */ + private void addNameLocked(final String name) { + int len = StringUtils.codePointCount(name); + NgramContext ngramContext = NgramContext.getEmptyPrevWordsContext( + BinaryDictionary.MAX_PREV_WORD_COUNT_FOR_N_GRAM); + // TODO: Better tokenization for non-Latin writing systems + for (int i = 0; i < len; i++) { + if (Character.isLetter(name.codePointAt(i))) { + int end = ContactsDictionaryUtils.getWordEndPosition(name, len, i); + String word = name.substring(i, end); + if (DEBUG_DUMP) { + Log.d(TAG, "addName word = " + word); + } + i = end - 1; + // Don't add single letter words, possibly confuses + // capitalization of i. + final int wordLen = StringUtils.codePointCount(word); + if (wordLen <= MAX_WORD_LENGTH && wordLen > 1) { + if (DEBUG) { + Log.d(TAG, "addName " + name + ", " + word + ", " + ngramContext); + } + runGCIfRequiredLocked(true /* mindsBlockByGC */); + addUnigramLocked(word, + ContactsDictionaryConstants.FREQUENCY_FOR_CONTACTS, false /* isNotAWord */, + false /* isPossiblyOffensive */, + BinaryDictionary.NOT_A_VALID_TIMESTAMP); + if (ngramContext.isValid() && mUseFirstLastBigrams) { + runGCIfRequiredLocked(true /* mindsBlockByGC */); + addNgramEntryLocked(ngramContext, + word, + ContactsDictionaryConstants.FREQUENCY_FOR_CONTACTS_BIGRAM, + BinaryDictionary.NOT_A_VALID_TIMESTAMP); + } + ngramContext = ngramContext.getNextNgramContext( + new NgramContext.WordInfo(word)); + } + } + } + } + + @Override + public void onContactsChange() { + setNeedsToRecreate(); + } +} |