diff options
73 files changed, 1274 insertions, 1091 deletions
diff --git a/java/res/values/attrs.xml b/java/res/values/attrs.xml index 478a5c020..80bf704db 100644 --- a/java/res/values/attrs.xml +++ b/java/res/values/attrs.xml @@ -161,7 +161,7 @@ <declare-styleable name="SuggestionStripView"> <attr name="suggestionStripOption" format="integer"> - <!-- This should be aligned with SuggestionStripViewParams.AUTO_CORRECT_* and etc. --> + <!-- This should be aligned with SuggestionStripLayoutHelper.AUTO_CORRECT_* and etc. --> <flag name="autoCorrectBold" value="0x01" /> <flag name="autoCorrectUnderline" value="0x02" /> <flag name="validTypedWordBold" value="0x04" /> diff --git a/java/res/values/keypress-vibration-durations.xml b/java/res/values/keypress-vibration-durations.xml index ad6beadb6..0474b1c92 100644 --- a/java/res/values/keypress-vibration-durations.xml +++ b/java/res/values/keypress-vibration-durations.xml @@ -39,10 +39,19 @@ <item>MODEL=(SAMSUNG-)?SCH-(J021|R530|I535|I939):MANUFACTURER=samsung,8</item> <item>MODEL=(SAMSUNG-)?(SCL21|SC-06D|SC-03E):MANUFACTURER=samsung,8</item> <item>MODEL=(SAMSUNG-)?(SHV-210[KLS]?|SPH-L710):MANUFACTURER=samsung,8</item> + <!-- Samsung Galaxy S4 --> + <item>MODEL=(SAMSUNG-)?GT-I(950[0258][G]?):MANUFACTURER=samsung,7</item> + <item>MODEL=(SAMSUNG-)?SGH-(I337|M919|N045):MANUFACTURER=samsung,7</item> + <item>MODEL=(SAMSUNG-)?SCH-(I545|I959|R970):MANUFACTURER=samsung,7</item> + <item>MODEL=(SAMSUNG-)?SPH-(L720):MANUFACTURER=samsung,7</item> + <item>MODEL=(SAMSUNG-)?(SC-04E):MANUFACTURER=samsung,7</item> + <item>MODEL=(SAMSUNG-)?(SHV-E300[KLS]?):MANUFACTURER=samsung,7</item> <!-- LG Optimus G --> <item>MODEL=LG-E97[013]|LS970|L-01E:MANUFACTURER=LGE,15</item> <!-- HTC One X --> <item>MODEL=HTC One X:MANUFACTURER=HTC,20</item> + <!-- HTC One --> + <item>MODEL=HTC One:MANUFACTURER=HTC,15</item> <!-- Motorola Razor M --> <item>MODEL=XT907:MANUFACTURER=motorola,30</item> <!-- Sony Xperia Z --> diff --git a/java/src/com/android/inputmethod/dictionarypack/ButtonSwitcher.java b/java/src/com/android/inputmethod/dictionarypack/ButtonSwitcher.java index 5ab94a429..c5aca174a 100644 --- a/java/src/com/android/inputmethod/dictionarypack/ButtonSwitcher.java +++ b/java/src/com/android/inputmethod/dictionarypack/ButtonSwitcher.java @@ -57,6 +57,11 @@ public class ButtonSwitcher extends FrameLayout { super(context, attrs, defStyle); } + public void reset() { + mStatus = NOT_INITIALIZED; + mAnimateToStatus = NOT_INITIALIZED; + } + @Override protected void onLayout(final boolean changed, final int left, final int top, final int right, final int bottom) { @@ -64,9 +69,7 @@ public class ButtonSwitcher extends FrameLayout { mInstallButton = (Button)findViewById(R.id.dict_install_button); mCancelButton = (Button)findViewById(R.id.dict_cancel_button); mDeleteButton = (Button)findViewById(R.id.dict_delete_button); - mInstallButton.setOnClickListener(mOnClickListener); - mCancelButton.setOnClickListener(mOnClickListener); - mDeleteButton.setOnClickListener(mOnClickListener); + setInternalOnClickListener(mOnClickListener); setButtonPositionWithoutAnimation(mStatus); if (mAnimateToStatus != NOT_INITIALIZED) { // We have been asked to animate before we were ready, so we took a note of it. @@ -139,6 +142,12 @@ public class ButtonSwitcher extends FrameLayout { public void setInternalOnClickListener(final OnClickListener listener) { mOnClickListener = listener; + if (null != mInstallButton) { + // Already laid out : do it now + mInstallButton.setOnClickListener(mOnClickListener); + mCancelButton.setOnClickListener(mOnClickListener); + mDeleteButton.setOnClickListener(mOnClickListener); + } } private ViewPropertyAnimator animateButton(final View button, final int direction) { diff --git a/java/src/com/android/inputmethod/dictionarypack/DictionaryListInterfaceState.java b/java/src/com/android/inputmethod/dictionarypack/DictionaryListInterfaceState.java index de3711c27..5ad5900d4 100644 --- a/java/src/com/android/inputmethod/dictionarypack/DictionaryListInterfaceState.java +++ b/java/src/com/android/inputmethod/dictionarypack/DictionaryListInterfaceState.java @@ -16,8 +16,11 @@ package com.android.inputmethod.dictionarypack; +import android.view.View; + import com.android.inputmethod.latin.CollectionUtils; +import java.util.ArrayList; import java.util.HashMap; /** @@ -37,6 +40,7 @@ public class DictionaryListInterfaceState { } private HashMap<String, State> mWordlistToState = CollectionUtils.newHashMap(); + private ArrayList<View> mViewCache = CollectionUtils.newArrayList(); public boolean isOpen(final String wordlistId) { final State state = mWordlistToState.get(wordlistId); @@ -64,4 +68,16 @@ public class DictionaryListInterfaceState { state.mOpen = false; } } + + public View findFirstOrphanedView() { + for (final View v : mViewCache) { + if (null == v.getParent()) return v; + } + return null; + } + + public View addToCacheAndReturnView(final View view) { + mViewCache.add(view); + return view; + } } diff --git a/java/src/com/android/inputmethod/dictionarypack/DictionarySettingsFragment.java b/java/src/com/android/inputmethod/dictionarypack/DictionarySettingsFragment.java index 618322357..4b89d20bb 100644 --- a/java/src/com/android/inputmethod/dictionarypack/DictionarySettingsFragment.java +++ b/java/src/com/android/inputmethod/dictionarypack/DictionarySettingsFragment.java @@ -222,7 +222,9 @@ public final class DictionarySettingsFragment extends PreferenceFragment refreshNetworkState(); removeAnyDictSettings(prefScreen); + int i = 0; for (Preference preference : prefList) { + preference.setOrder(i++); prefScreen.addPreference(preference); } } @@ -302,7 +304,7 @@ public final class DictionarySettingsFragment extends PreferenceFragment // the description. final String key = matchLevelString + "." + description + "." + wordlistId; final WordListPreference existingPref = prefMap.get(key); - if (null == existingPref || hasPriority(status, existingPref.mStatus)) { + if (null == existingPref || existingPref.hasPriorityOver(status)) { final WordListPreference oldPreference = mCurrentPreferenceMap.get(key); final WordListPreference pref; if (null != oldPreference @@ -313,7 +315,7 @@ public final class DictionarySettingsFragment extends PreferenceFragment // need to be the same, others have been tested through the key of the // map. Also, status may differ so we don't want to use #equals() here. pref = oldPreference; - pref.mStatus = status; + pref.setStatus(status); } else { // Otherwise, discard it and create a new one instead. pref = new WordListPreference(activity, mDictionaryListInterfaceState, @@ -329,18 +331,6 @@ public final class DictionarySettingsFragment extends PreferenceFragment } } - /** - * Finds out if a given status has priority over another for display order. - * - * @param newStatus - * @param oldStatus - * @return whether newStatus has priority over oldStatus. - */ - private static boolean hasPriority(final int newStatus, final int oldStatus) { - // Both of these should be one of MetadataDbHelper.STATUS_* - return newStatus > oldStatus; - } - @Override public boolean onOptionsItemSelected(final MenuItem item) { switch (item.getItemId()) { diff --git a/java/src/com/android/inputmethod/dictionarypack/MetadataDbHelper.java b/java/src/com/android/inputmethod/dictionarypack/MetadataDbHelper.java index 03ed267c3..1511dbcfe 100644 --- a/java/src/com/android/inputmethod/dictionarypack/MetadataDbHelper.java +++ b/java/src/com/android/inputmethod/dictionarypack/MetadataDbHelper.java @@ -572,7 +572,8 @@ public class MetadataDbHelper extends SQLiteOpenHelper { * If several clients use the same metadata URL, we know to only download it once, and * dispatch the update process across all relevant clients when the download ends. This means * several clients may share a single download ID if they share a metadata URI. - * The dispatching is done in {@link UpdateHandler#downloadFinished(Context, Intent)}, which + * The dispatching is done in + * {@link UpdateHandler#downloadFinished(Context, android.content.Intent)}, which * finds out about the list of relevant clients by calling this method. * * @param context a context instance to open the databases @@ -863,17 +864,20 @@ public class MetadataDbHelper extends SQLiteOpenHelper { r.getAsString(WORDLISTID_COLUMN), Integer.toString(STATUS_INSTALLED) }, null, null, null); - if (c.moveToFirst()) { - // There should never be more than one file, but if there are, it's a bug - // and we should remove them all. I think it might happen if the power of the - // phone is suddenly cut during an update. - final int filenameIndex = c.getColumnIndex(LOCAL_FILENAME_COLUMN); - do { - Utils.l("Setting for removal", c.getString(filenameIndex)); - filenames.add(c.getString(filenameIndex)); - } while (c.moveToNext()); + try { + if (c.moveToFirst()) { + // There should never be more than one file, but if there are, it's a bug + // and we should remove them all. I think it might happen if the power of + // the phone is suddenly cut during an update. + final int filenameIndex = c.getColumnIndex(LOCAL_FILENAME_COLUMN); + do { + Utils.l("Setting for removal", c.getString(filenameIndex)); + filenames.add(c.getString(filenameIndex)); + } while (c.moveToNext()); + } + } finally { + c.close(); } - r.put(STATUS_COLUMN, STATUS_INSTALLED); db.beginTransactionNonExclusive(); // Delete all old entries. There should never be any stalled entries, but if diff --git a/java/src/com/android/inputmethod/dictionarypack/WordListPreference.java b/java/src/com/android/inputmethod/dictionarypack/WordListPreference.java index 451a0fb82..7ec7e9c13 100644 --- a/java/src/com/android/inputmethod/dictionarypack/WordListPreference.java +++ b/java/src/com/android/inputmethod/dictionarypack/WordListPreference.java @@ -61,7 +61,7 @@ public final class WordListPreference extends Preference { public final Locale mLocale; public final String mDescription; // The status - public int mStatus; + private int mStatus; // The size of the dictionary file private final int mFilesize; @@ -92,12 +92,25 @@ public final class WordListPreference extends Preference { setKey(wordlistId); } - private void setStatus(final int status) { + public void setStatus(final int status) { if (status == mStatus) return; mStatus = status; setSummary(getSummary(status)); } + @Override + public View onCreateView(final ViewGroup parent) { + final View orphanedView = mInterfaceState.findFirstOrphanedView(); + if (null != orphanedView) return orphanedView; // Will be sent to onBindView + final View newView = super.onCreateView(parent); + return mInterfaceState.addToCacheAndReturnView(newView); + } + + public boolean hasPriorityOver(final int otherPrefStatus) { + // Both of these should be one of MetadataDbHelper.STATUS_* + return mStatus > otherPrefStatus; + } + private String getSummary(final int status) { switch (status) { // If we are deleting the word list, for the user it's like it's already deleted. @@ -209,6 +222,9 @@ public final class WordListPreference extends Preference { final ButtonSwitcher buttonSwitcher = (ButtonSwitcher)view.findViewById(R.id.wordlist_button_switcher); + // We need to clear the state of the button switcher, because we reuse views; if we didn't + // reset it would animate from whatever its old state was. + buttonSwitcher.reset(); if (mInterfaceState.isOpen(mWordlistId)) { // The button is open. final int previousStatus = mInterfaceState.getStatus(mWordlistId); diff --git a/java/src/com/android/inputmethod/latin/AssetFileAddress.java b/java/src/com/android/inputmethod/latin/AssetFileAddress.java index 47c750f54..875192554 100644 --- a/java/src/com/android/inputmethod/latin/AssetFileAddress.java +++ b/java/src/com/android/inputmethod/latin/AssetFileAddress.java @@ -24,7 +24,7 @@ import java.io.File; * the package file. Open it correctly thus requires the name of the package it is in, but * also the offset in the file and the length of this data. This class encapsulates these three. */ -final class AssetFileAddress { +public final class AssetFileAddress { public final String mFilename; public final long mOffset; public final long mLength; diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java index a9b58de44..c038db87c 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java @@ -32,6 +32,7 @@ import com.android.inputmethod.latin.DictionaryInfoUtils.DictionaryInfo; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; +import java.io.Closeable; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; @@ -319,20 +320,12 @@ public final class BinaryDictionaryFileDumper { // Try the next method. } finally { // Ignore exceptions while closing files. - try { - if (null != afd) afd.close(); - if (null != inputStream) inputStream.close(); - if (null != uncompressedStream) uncompressedStream.close(); - if (null != decryptedStream) decryptedStream.close(); - if (null != bufferedInputStream) bufferedInputStream.close(); - } catch (Exception e) { - Log.e(TAG, "Exception while closing a file descriptor", e); - } - try { - if (null != bufferedOutputStream) bufferedOutputStream.close(); - } catch (Exception e) { - Log.e(TAG, "Exception while closing a file", e); - } + closeAssetFileDescriptorAndReportAnyException(afd); + closeCloseableAndReportAnyException(inputStream); + closeCloseableAndReportAnyException(uncompressedStream); + closeCloseableAndReportAnyException(decryptedStream); + closeCloseableAndReportAnyException(bufferedInputStream); + closeCloseableAndReportAnyException(bufferedOutputStream); } } @@ -352,6 +345,26 @@ public final class BinaryDictionaryFileDumper { } } + // Ideally the two following methods should be merged, but AssetFileDescriptor does not + // implement Closeable although it does implement #close(), and Java does not have + // structural typing. + private static void closeAssetFileDescriptorAndReportAnyException( + final AssetFileDescriptor file) { + try { + if (null != file) file.close(); + } catch (Exception e) { + Log.e(TAG, "Exception while closing a file", e); + } + } + + private static void closeCloseableAndReportAnyException(final Closeable file) { + try { + if (null != file) file.close(); + } catch (Exception e) { + Log.e(TAG, "Exception while closing a file", e); + } + } + /** * Queries a content provider for word list data for some locale and cache the returned files * @@ -363,8 +376,14 @@ public final class BinaryDictionaryFileDumper { */ public static void cacheWordListsFromContentProvider(final Locale locale, final Context context, final boolean hasDefaultWordList) { - final ContentProviderClient providerClient = context.getContentResolver(). + final ContentProviderClient providerClient; + try { + providerClient = context.getContentResolver(). acquireContentProviderClient(getProviderUriBuilder("").build()); + } catch (final SecurityException e) { + Log.e(TAG, "No permission to communicate with the dictionary provider", e); + return; + } if (null == providerClient) { Log.e(TAG, "Can't establish communication with the dictionary provider"); return; diff --git a/java/src/com/android/inputmethod/latin/DictionaryFactory.java b/java/src/com/android/inputmethod/latin/DictionaryFactory.java index 40e51672a..4514ec2ec 100644 --- a/java/src/com/android/inputmethod/latin/DictionaryFactory.java +++ b/java/src/com/android/inputmethod/latin/DictionaryFactory.java @@ -21,6 +21,8 @@ import android.content.res.AssetFileDescriptor; import android.content.res.Resources; import android.util.Log; +import com.android.inputmethod.annotations.UsedForTesting; + import java.io.File; import java.util.ArrayList; import java.util.LinkedList; @@ -126,21 +128,22 @@ public final class DictionaryFactory { /** * Create a dictionary from passed data. This is intended for unit tests only. - * @param dictionary the file to read - * @param startOffset the offset in the file where the data starts - * @param length the length of the data + * @param dictionaryList the list of files to read, with their offsets and lengths * @param useFullEditDistance whether to use the full edit distance in suggestions * @return the created dictionary, or null. */ - public static Dictionary createDictionaryForTest(File dictionary, long startOffset, long length, + @UsedForTesting + public static Dictionary createDictionaryForTest(final AssetFileAddress[] dictionaryList, final boolean useFullEditDistance, Locale locale) { - if (dictionary.isFile()) { - return new BinaryDictionary(dictionary.getAbsolutePath(), startOffset, length, - useFullEditDistance, locale, Dictionary.TYPE_MAIN); - } else { - Log.e(TAG, "Could not find the file. path=" + dictionary.getAbsolutePath()); - return null; + final DictionaryCollection dictionaryCollection = + new DictionaryCollection(Dictionary.TYPE_MAIN); + for (final AssetFileAddress address : dictionaryList) { + final BinaryDictionary binaryDictionary = new BinaryDictionary(address.mFilename, + address.mOffset, address.mLength, useFullEditDistance, locale, + Dictionary.TYPE_MAIN); + dictionaryCollection.addDictionary(binaryDictionary); } + return dictionaryCollection; } /** diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index cebc93c18..c9a42a3a4 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -855,6 +855,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } // Remove pending messages related to update suggestions mHandler.cancelUpdateSuggestionStrip(); + if (mWordComposer.isComposingWord()) mConnection.finishComposingText(); resetComposingState(true /* alsoResetLastComposedWord */); // Notify ResearchLogger if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java index 5d580f29b..e783e6d51 100644 --- a/java/src/com/android/inputmethod/latin/Suggest.java +++ b/java/src/com/android/inputmethod/latin/Suggest.java @@ -23,7 +23,6 @@ import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.keyboard.ProximityInfo; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; -import java.io.File; import java.util.ArrayList; import java.util.Comparator; import java.util.HashSet; @@ -77,9 +76,9 @@ public final class Suggest { } @UsedForTesting - Suggest(final File dictionary, final long startOffset, final long length, final Locale locale) { - final Dictionary mainDict = DictionaryFactory.createDictionaryForTest(dictionary, - startOffset, length /* useFullEditDistance */, false, locale); + Suggest(final AssetFileAddress[] dictionaryList, final Locale locale) { + final Dictionary mainDict = DictionaryFactory.createDictionaryForTest(dictionaryList, + false /* useFullEditDistance */, locale); mLocale = locale; mMainDictionary = mainDict; addOrReplaceDictionary(mDictionaries, Dictionary.TYPE_MAIN, mainDict); diff --git a/java/src/com/android/inputmethod/latin/SuggestedWords.java b/java/src/com/android/inputmethod/latin/SuggestedWords.java index 1f453273b..7a16595a7 100644 --- a/java/src/com/android/inputmethod/latin/SuggestedWords.java +++ b/java/src/com/android/inputmethod/latin/SuggestedWords.java @@ -24,6 +24,9 @@ import java.util.Arrays; import java.util.HashSet; public final class SuggestedWords { + public static final int INDEX_OF_TYPED_WORD = 0; + public static final int INDEX_OF_AUTO_CORRECTION = 1; + private static final ArrayList<SuggestedWordInfo> EMPTY_WORD_INFO_LIST = CollectionUtils.newArrayList(0); public static final SuggestedWords EMPTY = new SuggestedWords( @@ -61,12 +64,12 @@ public final class SuggestedWords { return mSuggestedWordInfoList.size(); } - public String getWord(int pos) { - return mSuggestedWordInfoList.get(pos).mWord; + public String getWord(final int index) { + return mSuggestedWordInfoList.get(index).mWord; } - public SuggestedWordInfo getInfo(int pos) { - return mSuggestedWordInfoList.get(pos); + public SuggestedWordInfo getInfo(final int index) { + return mSuggestedWordInfoList.get(index); } public boolean willAutoCorrect() { @@ -108,8 +111,8 @@ public final class SuggestedWords { SuggestedWordInfo.KIND_TYPED, Dictionary.TYPE_USER_TYPED)); alreadySeen.add(typedWord.toString()); final int previousSize = previousSuggestions.size(); - for (int pos = 1; pos < previousSize; pos++) { - final SuggestedWordInfo prevWordInfo = previousSuggestions.getInfo(pos); + for (int index = 1; index < previousSize; index++) { + final SuggestedWordInfo prevWordInfo = previousSuggestions.getInfo(index); final String prevWord = prevWordInfo.mWord; // Filter out duplicate suggestion. if (!alreadySeen.contains(prevWord)) { diff --git a/java/src/com/android/inputmethod/latin/Utils.java b/java/src/com/android/inputmethod/latin/Utils.java index 0f96c54dc..949720fda 100644 --- a/java/src/com/android/inputmethod/latin/Utils.java +++ b/java/src/com/android/inputmethod/latin/Utils.java @@ -64,7 +64,8 @@ public final class Utils { * task should be interrupted; otherwise, in-progress tasks are allowed * to complete. */ - public static void cancelTask(AsyncTask<?, ?, ?> task, boolean mayInterruptIfRunning) { + public static void cancelTask(final AsyncTask<?, ?, ?> task, + final boolean mayInterruptIfRunning) { if (task != null && task.getStatus() != AsyncTask.Status.FINISHED) { task.cancel(mayInterruptIfRunning); } @@ -86,26 +87,34 @@ public final class Utils { private RingCharBuffer() { // Intentional empty constructor for singleton. } + @UsedForTesting public static RingCharBuffer getInstance() { return sRingCharBuffer; } - public static RingCharBuffer init(InputMethodService context, boolean enabled, - boolean usabilityStudy) { - if (!(enabled || usabilityStudy)) return null; + + public static RingCharBuffer init(final InputMethodService context, final boolean enabled, + final boolean usabilityStudy) { + if (!(enabled || usabilityStudy)) { + return null; + } sRingCharBuffer.mContext = context; sRingCharBuffer.mEnabled = true; UsabilityStudyLogUtils.getInstance().init(context); return sRingCharBuffer; } - private static int normalize(int in) { + + private static int normalize(final int in) { int ret = in % BUFSIZE; return ret < 0 ? ret + BUFSIZE : ret; } + // TODO: accept code points @UsedForTesting - public void push(char c, int x, int y) { - if (!mEnabled) return; + public void push(final char c, final int x, final int y) { + if (!mEnabled) { + return; + } mCharBuf[mEnd] = c; mXBuf[mEnd] = x; mYBuf[mEnd] = y; @@ -114,52 +123,54 @@ public final class Utils { ++mLength; } } + public char pop() { if (mLength < 1) { return PLACEHOLDER_DELIMITER_CHAR; - } else { - mEnd = normalize(mEnd - 1); - --mLength; - return mCharBuf[mEnd]; } + mEnd = normalize(mEnd - 1); + --mLength; + return mCharBuf[mEnd]; } - public char getBackwardNthChar(int n) { + + public char getBackwardNthChar(final int n) { if (mLength <= n || n < 0) { return PLACEHOLDER_DELIMITER_CHAR; - } else { - return mCharBuf[normalize(mEnd - n - 1)]; } + return mCharBuf[normalize(mEnd - n - 1)]; } - public int getPreviousX(char c, int back) { - int index = normalize(mEnd - 2 - back); + + public int getPreviousX(final char c, final int back) { + final int index = normalize(mEnd - 2 - back); if (mLength <= back || Character.toLowerCase(c) != Character.toLowerCase(mCharBuf[index])) { return INVALID_COORDINATE; - } else { - return mXBuf[index]; } + return mXBuf[index]; } - public int getPreviousY(char c, int back) { + + public int getPreviousY(final char c, final int back) { int index = normalize(mEnd - 2 - back); if (mLength <= back || Character.toLowerCase(c) != Character.toLowerCase(mCharBuf[index])) { return INVALID_COORDINATE; - } else { - return mYBuf[index]; } + return mYBuf[index]; } - public String getLastWord(int ignoreCharCount) { - StringBuilder sb = new StringBuilder(); + + public String getLastWord(final int ignoreCharCount) { + final StringBuilder sb = new StringBuilder(); + final LatinIME latinIme = (LatinIME)mContext; int i = ignoreCharCount; for (; i < mLength; ++i) { - char c = mCharBuf[normalize(mEnd - 1 - i)]; - if (!((LatinIME)mContext).isWordSeparator(c)) { + final char c = mCharBuf[normalize(mEnd - 1 - i)]; + if (!latinIme.isWordSeparator(c)) { break; } } for (; i < mLength; ++i) { char c = mCharBuf[normalize(mEnd - 1 - i)]; - if (!((LatinIME)mContext).isWordSeparator(c)) { + if (!latinIme.isWordSeparator(c)) { sb.append(c); } else { break; @@ -167,6 +178,7 @@ public final class Utils { } return sb.reverse().toString(); } + public void reset() { mLength = 0; } @@ -174,11 +186,11 @@ public final class Utils { // Get the current stack trace public static String getStackTrace(final int limit) { - StringBuilder sb = new StringBuilder(); + final StringBuilder sb = new StringBuilder(); try { throw new RuntimeException(); - } catch (RuntimeException e) { - StackTraceElement[] frames = e.getStackTrace(); + } catch (final RuntimeException e) { + final StackTraceElement[] frames = e.getStackTrace(); // Start at 1 because the first frame is here and we don't care about it for (int j = 1; j < frames.length && j < limit + 1; ++j) { sb.append(frames[j].toString() + "\n"); @@ -222,7 +234,7 @@ public final class Utils { return OnDemandInitializationHolder.sInstance; } - public void init(InputMethodService ims) { + public void init(final InputMethodService ims) { mIms = ims; mDirectory = ims.getFilesDir(); } @@ -232,17 +244,17 @@ public final class Utils { && (mDirectory != null && mDirectory.exists())) { try { mWriter = getPrintWriter(mDirectory, FILENAME, false); - } catch (IOException e) { + } catch (final IOException e) { Log.e(USABILITY_TAG, "Can't create log file."); } } } - public static void writeBackSpace(int x, int y) { + public static void writeBackSpace(final int x, final int y) { UsabilityStudyLogUtils.getInstance().write("<backspace>\t" + x + "\t" + y); } - public void writeChar(char c, int x, int y) { + public static void writeChar(final char c, final int x, final int y) { String inputChar = String.valueOf(c); switch (c) { case '\n': @@ -279,15 +291,15 @@ public final class Utils { private synchronized String getBufferedLogs() { mWriter.flush(); - StringBuilder sb = new StringBuilder(); - BufferedReader br = getBufferedReader(); + final StringBuilder sb = new StringBuilder(); + final BufferedReader br = getBufferedReader(); String line; try { while ((line = br.readLine()) != null) { sb.append('\n'); sb.append(line); } - } catch (IOException e) { + } catch (final IOException e) { Log.e(USABILITY_TAG, "Can't read log file."); } finally { if (LatinImeLogger.sDBG) { @@ -295,7 +307,7 @@ public final class Utils { } try { br.close(); - } catch (IOException e) { + } catch (final IOException e) { // ignore. } } @@ -334,10 +346,10 @@ public final class Utils { srcStream.close(); dest.close(); destStream.close(); - } catch (FileNotFoundException e1) { + } catch (final FileNotFoundException e1) { Log.w(USABILITY_TAG, e1); return; - } catch (IOException e2) { + } catch (final IOException e2) { Log.w(USABILITY_TAG, e2); return; } @@ -387,13 +399,13 @@ public final class Utils { createLogFileIfNotExist(); try { return new BufferedReader(new FileReader(mFile)); - } catch (FileNotFoundException e) { + } catch (final FileNotFoundException e) { return null; } } - private PrintWriter getPrintWriter( - File dir, String filename, boolean renew) throws IOException { + private PrintWriter getPrintWriter(final File dir, final String filename, + final boolean renew) throws IOException { mFile = new File(dir, filename); if (mFile.exists()) { if (renew) { @@ -405,8 +417,7 @@ public final class Utils { } public static final class Stats { - public static void onNonSeparator(final char code, final int x, - final int y) { + public static void onNonSeparator(final char code, final int x, final int y) { RingCharBuffer.getInstance().push(code, x, y); LatinImeLogger.logOnInputChar(); } @@ -430,7 +441,9 @@ public final class Utils { public static void onAutoCorrection(final String typedWord, final String correctedWord, final String separatorString, final WordComposer wordComposer) { final boolean isBatchMode = wordComposer.isBatchMode(); - if (!isBatchMode && TextUtils.isEmpty(typedWord)) return; + if (!isBatchMode && TextUtils.isEmpty(typedWord)) { + return; + } // TODO: this fails when the separator is more than 1 code point long, but // the backend can't handle it yet. The only case when this happens is with // smileys and other multi-character keys. @@ -454,36 +467,43 @@ public final class Utils { } public static String getDebugInfo(final SuggestedWords suggestions, final int pos) { - if (!LatinImeLogger.sDBG) return null; + if (!LatinImeLogger.sDBG) { + return null; + } final SuggestedWordInfo wordInfo = suggestions.getInfo(pos); - if (wordInfo == null) return null; + if (wordInfo == null) { + return null; + } final String info = wordInfo.getDebugString(); - if (TextUtils.isEmpty(info)) return null; + if (TextUtils.isEmpty(info)) { + return null; + } return info; } - public static int getAcitivityTitleResId(Context context, Class<? extends Activity> cls) { + public static int getAcitivityTitleResId(final Context context, + final Class<? extends Activity> cls) { final ComponentName cn = new ComponentName(context, cls); try { final ActivityInfo ai = context.getPackageManager().getActivityInfo(cn, 0); if (ai != null) { return ai.labelRes; } - } catch (NameNotFoundException e) { + } catch (final NameNotFoundException e) { Log.e(TAG, "Failed to get settings activity title res id.", e); } return 0; } - public static String getVersionName(Context context) { + public static String getVersionName(final Context context) { try { if (context == null) { return ""; } final String packageName = context.getPackageName(); - PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0); + final PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0); return info.versionName; - } catch (NameNotFoundException e) { + } catch (final NameNotFoundException e) { Log.e(TAG, "Could not find version info.", e); } return ""; diff --git a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java index 09f81d4c7..322ae5b0f 100644 --- a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java +++ b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java @@ -61,7 +61,7 @@ public final class MoreSuggestions extends Keyboard { super(); } - public int layout(final SuggestedWords suggestedWords, final int fromPos, + public int layout(final SuggestedWords suggestedWords, final int fromIndex, final int maxWidth, final int minWidth, final int maxRow, final Paint paint, final Resources res) { clearKeys(); @@ -70,53 +70,54 @@ public final class MoreSuggestions extends Keyboard { final float padding = res.getDimension(R.dimen.more_suggestions_key_horizontal_padding); int row = 0; - int pos = fromPos, rowStartPos = fromPos; + int index = fromIndex; + int rowStartIndex = fromIndex; final int size = Math.min(suggestedWords.size(), SuggestionStripView.MAX_SUGGESTIONS); - while (pos < size) { - final String word = suggestedWords.getWord(pos); + while (index < size) { + final String word = suggestedWords.getWord(index); // TODO: Should take care of text x-scaling. - mWidths[pos] = (int)(TypefaceUtils.getLabelWidth(word, paint) + padding); - final int numColumn = pos - rowStartPos + 1; + mWidths[index] = (int)(TypefaceUtils.getLabelWidth(word, paint) + padding); + final int numColumn = index - rowStartIndex + 1; final int columnWidth = (maxWidth - mDividerWidth * (numColumn - 1)) / numColumn; if (numColumn > MAX_COLUMNS_IN_ROW - || !fitInWidth(rowStartPos, pos + 1, columnWidth)) { + || !fitInWidth(rowStartIndex, index + 1, columnWidth)) { if ((row + 1) >= maxRow) { break; } - mNumColumnsInRow[row] = pos - rowStartPos; - rowStartPos = pos; + mNumColumnsInRow[row] = index - rowStartIndex; + rowStartIndex = index; row++; } - mColumnOrders[pos] = pos - rowStartPos; - mRowNumbers[pos] = row; - pos++; + mColumnOrders[index] = index - rowStartIndex; + mRowNumbers[index] = row; + index++; } - mNumColumnsInRow[row] = pos - rowStartPos; + mNumColumnsInRow[row] = index - rowStartIndex; mNumRows = row + 1; mBaseWidth = mOccupiedWidth = Math.max( - minWidth, calcurateMaxRowWidth(fromPos, pos)); + minWidth, calcurateMaxRowWidth(fromIndex, index)); mBaseHeight = mOccupiedHeight = mNumRows * mDefaultRowHeight + mVerticalGap; - return pos - fromPos; + return index - fromIndex; } - private boolean fitInWidth(final int startPos, final int endPos, final int width) { - for (int pos = startPos; pos < endPos; pos++) { - if (mWidths[pos] > width) + private boolean fitInWidth(final int startIndex, final int endIndex, final int width) { + for (int index = startIndex; index < endIndex; index++) { + if (mWidths[index] > width) return false; } return true; } - private int calcurateMaxRowWidth(final int startPos, final int endPos) { + private int calcurateMaxRowWidth(final int startIndex, final int endIndex) { int maxRowWidth = 0; - int pos = startPos; + int index = startIndex; for (int row = 0; row < mNumRows; row++) { final int numColumnInRow = mNumColumnsInRow[row]; int maxKeyWidth = 0; - while (pos < endPos && mRowNumbers[pos] == row) { - maxKeyWidth = Math.max(maxKeyWidth, mWidths[pos]); - pos++; + while (index < endIndex && mRowNumbers[index] == row) { + maxKeyWidth = Math.max(maxKeyWidth, mWidths[index]); + index++; } maxRowWidth = Math.max(maxRowWidth, maxKeyWidth * numColumnInRow + mDividerWidth * (numColumnInRow - 1)); @@ -130,40 +131,40 @@ public final class MoreSuggestions extends Keyboard { { 2, 0, 1}, }; - public int getNumColumnInRow(final int pos) { - return mNumColumnsInRow[mRowNumbers[pos]]; + public int getNumColumnInRow(final int index) { + return mNumColumnsInRow[mRowNumbers[index]]; } - public int getColumnNumber(final int pos) { - final int columnOrder = mColumnOrders[pos]; - final int numColumn = getNumColumnInRow(pos); + public int getColumnNumber(final int index) { + final int columnOrder = mColumnOrders[index]; + final int numColumn = getNumColumnInRow(index); return COLUMN_ORDER_TO_NUMBER[numColumn - 1][columnOrder]; } - public int getX(final int pos) { - final int columnNumber = getColumnNumber(pos); - return columnNumber * (getWidth(pos) + mDividerWidth); + public int getX(final int index) { + final int columnNumber = getColumnNumber(index); + return columnNumber * (getWidth(index) + mDividerWidth); } - public int getY(final int pos) { - final int row = mRowNumbers[pos]; + public int getY(final int index) { + final int row = mRowNumbers[index]; return (mNumRows -1 - row) * mDefaultRowHeight + mTopPadding; } - public int getWidth(final int pos) { - final int numColumnInRow = getNumColumnInRow(pos); + public int getWidth(final int index) { + final int numColumnInRow = getNumColumnInRow(index); return (mOccupiedWidth - mDividerWidth * (numColumnInRow - 1)) / numColumnInRow; } - public void markAsEdgeKey(final Key key, final int pos) { - final int row = mRowNumbers[pos]; + public void markAsEdgeKey(final Key key, final int index) { + final int row = mRowNumbers[index]; if (row == 0) key.markAsBottomEdge(this); if (row == mNumRows - 1) key.markAsTopEdge(this); final int numColumnInRow = mNumColumnsInRow[row]; - final int column = getColumnNumber(pos); + final int column = getColumnNumber(index); if (column == 0) key.markAsLeftEdge(this); if (column == numColumnInRow - 1) @@ -174,15 +175,15 @@ public final class MoreSuggestions extends Keyboard { public static final class Builder extends KeyboardBuilder<MoreSuggestionsParam> { private final MoreSuggestionsView mPaneView; private SuggestedWords mSuggestedWords; - private int mFromPos; - private int mToPos; + private int mFromIndex; + private int mToIndex; public Builder(final Context context, final MoreSuggestionsView paneView) { super(context, new MoreSuggestionsParam()); mPaneView = paneView; } - public Builder layout(final SuggestedWords suggestedWords, final int fromPos, + public Builder layout(final SuggestedWords suggestedWords, final int fromIndex, final int maxWidth, final int minWidth, final int maxRow, final Keyboard parentKeyboard) { final int xmlId = R.xml.kbd_suggestions_pane_template; @@ -190,10 +191,10 @@ public final class MoreSuggestions extends Keyboard { mParams.mVerticalGap = mParams.mTopPadding = parentKeyboard.mVerticalGap / 2; mPaneView.updateKeyboardGeometry(mParams.mDefaultRowHeight); - final int count = mParams.layout(suggestedWords, fromPos, maxWidth, minWidth, maxRow, + final int count = mParams.layout(suggestedWords, fromIndex, maxWidth, minWidth, maxRow, mPaneView.newLabelPaint(null /* key */), mResources); - mFromPos = fromPos; - mToPos = fromPos + count; + mFromIndex = fromIndex; + mToIndex = fromIndex + count; mSuggestedWords = suggestedWords; return this; } @@ -201,20 +202,20 @@ public final class MoreSuggestions extends Keyboard { @Override public MoreSuggestions build() { final MoreSuggestionsParam params = mParams; - for (int pos = mFromPos; pos < mToPos; pos++) { - final int x = params.getX(pos); - final int y = params.getY(pos); - final int width = params.getWidth(pos); - final String word = mSuggestedWords.getWord(pos); - final String info = Utils.getDebugInfo(mSuggestedWords, pos); - final int index = pos + SUGGESTION_CODE_BASE; + for (int index = mFromIndex; index < mToIndex; index++) { + final int x = params.getX(index); + final int y = params.getY(index); + final int width = params.getWidth(index); + final String word = mSuggestedWords.getWord(index); + final String info = Utils.getDebugInfo(mSuggestedWords, index); + final int indexInMoreSuggestions = index + SUGGESTION_CODE_BASE; final Key key = new Key( - params, word, info, KeyboardIconsSet.ICON_UNDEFINED, index, null, x, y, - width, params.mDefaultRowHeight, 0); - params.markAsEdgeKey(key, pos); + params, word, info, KeyboardIconsSet.ICON_UNDEFINED, indexInMoreSuggestions, + null, x, y, width, params.mDefaultRowHeight, 0); + params.markAsEdgeKey(key, index); params.onAddKey(key); - final int columnNumber = params.getColumnNumber(pos); - final int numColumnInRow = params.getNumColumnInRow(pos); + final int columnNumber = params.getColumnNumber(index); + final int numColumnInRow = params.getNumColumnInRow(index); if (columnNumber < numColumnInRow - 1) { final Divider divider = new Divider(params, params.mDivider, x + width, y, params.mDividerWidth, params.mDefaultRowHeight); diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java new file mode 100644 index 000000000..2f2ec3560 --- /dev/null +++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java @@ -0,0 +1,561 @@ +/* + * Copyright (C) 2013 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 com.android.inputmethod.latin.suggestions; + +import android.content.Context; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Paint.Align; +import android.graphics.Rect; +import android.graphics.Typeface; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.text.Spannable; +import android.text.SpannableString; +import android.text.Spanned; +import android.text.TextPaint; +import android.text.TextUtils; +import android.text.style.CharacterStyle; +import android.text.style.StyleSpan; +import android.text.style.UnderlineSpan; +import android.util.AttributeSet; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.ViewGroup; +import android.widget.LinearLayout; +import android.widget.TextView; + +import com.android.inputmethod.keyboard.ViewLayoutUtils; +import com.android.inputmethod.latin.AutoCorrection; +import com.android.inputmethod.latin.LatinImeLogger; +import com.android.inputmethod.latin.R; +import com.android.inputmethod.latin.ResourceUtils; +import com.android.inputmethod.latin.SuggestedWords; +import com.android.inputmethod.latin.Utils; + +import java.util.ArrayList; + +final class SuggestionStripLayoutHelper { + private static final int DEFAULT_SUGGESTIONS_COUNT_IN_STRIP = 3; + private static final float DEFAULT_CENTER_SUGGESTION_PERCENTILE = 0.40f; + private static final int DEFAULT_MAX_MORE_SUGGESTIONS_ROW = 2; + private static final int PUNCTUATIONS_IN_STRIP = 5; + private static final float MIN_TEXT_XSCALE = 0.70f; + + public final int mPadding; + public final int mDividerWidth; + public final int mSuggestionsStripHeight; + public final int mSuggestionsCountInStrip; + public final int mMoreSuggestionsRowHeight; + private int mMaxMoreSuggestionsRow; + public final float mMinMoreSuggestionsWidth; + public final int mMoreSuggestionsBottomGap; + public boolean mMoreSuggestionsAvailable; + + // The index of these {@link ArrayList} is the position in the suggestion strip. The indices + // increase towards the right for LTR scripts and the left for RTL scripts, starting with 0. + // The position of the most important suggestion is in {@link #mCenterPositionInStrip} + private final ArrayList<TextView> mWordViews; + private final ArrayList<View> mDividerViews; + private final ArrayList<TextView> mDebugInfoViews; + + private final int mColorValidTypedWord; + private final int mColorTypedWord; + private final int mColorAutoCorrect; + private final int mColorSuggested; + private final float mAlphaObsoleted; + private final float mCenterSuggestionWeight; + private final int mCenterPositionInStrip; + private final Drawable mMoreSuggestionsHint; + private static final String MORE_SUGGESTIONS_HINT = "\u2026"; + private static final String LEFTWARDS_ARROW = "\u2190"; + + private static final CharacterStyle BOLD_SPAN = new StyleSpan(Typeface.BOLD); + private static final CharacterStyle UNDERLINE_SPAN = new UnderlineSpan(); + + private final int mSuggestionStripOption; + // These constants are the flag values of + // {@link R.styleable#SuggestionStripView_suggestionStripOption} attribute. + private static final int AUTO_CORRECT_BOLD = 0x01; + private static final int AUTO_CORRECT_UNDERLINE = 0x02; + private static final int VALID_TYPED_WORD_BOLD = 0x04; + + private final TextView mWordToSaveView; + private final TextView mLeftwardsArrowView; + private final TextView mHintToSaveView; + + public SuggestionStripLayoutHelper(final Context context, final AttributeSet attrs, + final int defStyle, final ArrayList<TextView> wordViews, + final ArrayList<View> dividerViews, final ArrayList<TextView> debugInfoViews) { + mWordViews = wordViews; + mDividerViews = dividerViews; + mDebugInfoViews = debugInfoViews; + + final TextView wordView = wordViews.get(0); + final View dividerView = dividerViews.get(0); + mPadding = wordView.getCompoundPaddingLeft() + wordView.getCompoundPaddingRight(); + dividerView.measure( + ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); + mDividerWidth = dividerView.getMeasuredWidth(); + + final Resources res = wordView.getResources(); + mSuggestionsStripHeight = res.getDimensionPixelSize(R.dimen.suggestions_strip_height); + + final TypedArray a = context.obtainStyledAttributes(attrs, + R.styleable.SuggestionStripView, defStyle, R.style.SuggestionStripViewStyle); + mSuggestionStripOption = a.getInt( + R.styleable.SuggestionStripView_suggestionStripOption, 0); + final float alphaValidTypedWord = ResourceUtils.getFraction(a, + R.styleable.SuggestionStripView_alphaValidTypedWord, 1.0f); + final float alphaTypedWord = ResourceUtils.getFraction(a, + R.styleable.SuggestionStripView_alphaTypedWord, 1.0f); + final float alphaAutoCorrect = ResourceUtils.getFraction(a, + R.styleable.SuggestionStripView_alphaAutoCorrect, 1.0f); + final float alphaSuggested = ResourceUtils.getFraction(a, + R.styleable.SuggestionStripView_alphaSuggested, 1.0f); + mAlphaObsoleted = ResourceUtils.getFraction(a, + R.styleable.SuggestionStripView_alphaSuggested, 1.0f); + mColorValidTypedWord = applyAlpha(a.getColor( + R.styleable.SuggestionStripView_colorValidTypedWord, 0), alphaValidTypedWord); + mColorTypedWord = applyAlpha(a.getColor( + R.styleable.SuggestionStripView_colorTypedWord, 0), alphaTypedWord); + mColorAutoCorrect = applyAlpha(a.getColor( + R.styleable.SuggestionStripView_colorAutoCorrect, 0), alphaAutoCorrect); + mColorSuggested = applyAlpha(a.getColor( + R.styleable.SuggestionStripView_colorSuggested, 0), alphaSuggested); + mSuggestionsCountInStrip = a.getInt( + R.styleable.SuggestionStripView_suggestionsCountInStrip, + DEFAULT_SUGGESTIONS_COUNT_IN_STRIP); + mCenterSuggestionWeight = ResourceUtils.getFraction(a, + R.styleable.SuggestionStripView_centerSuggestionPercentile, + DEFAULT_CENTER_SUGGESTION_PERCENTILE); + mMaxMoreSuggestionsRow = a.getInt( + R.styleable.SuggestionStripView_maxMoreSuggestionsRow, + DEFAULT_MAX_MORE_SUGGESTIONS_ROW); + mMinMoreSuggestionsWidth = ResourceUtils.getFraction(a, + R.styleable.SuggestionStripView_minMoreSuggestionsWidth, 1.0f); + a.recycle(); + + mMoreSuggestionsHint = getMoreSuggestionsHint(res, + res.getDimension(R.dimen.more_suggestions_hint_text_size), mColorAutoCorrect); + mCenterPositionInStrip = mSuggestionsCountInStrip / 2; + mMoreSuggestionsBottomGap = res.getDimensionPixelOffset( + R.dimen.more_suggestions_bottom_gap); + mMoreSuggestionsRowHeight = res.getDimensionPixelSize(R.dimen.more_suggestions_row_height); + + final LayoutInflater inflater = LayoutInflater.from(context); + mWordToSaveView = (TextView)inflater.inflate(R.layout.suggestion_word, null); + mLeftwardsArrowView = (TextView)inflater.inflate(R.layout.hint_add_to_dictionary, null); + mHintToSaveView = (TextView)inflater.inflate(R.layout.hint_add_to_dictionary, null); + } + + public int getMaxMoreSuggestionsRow() { + return mMaxMoreSuggestionsRow; + } + + private int getMoreSuggestionsHeight() { + return mMaxMoreSuggestionsRow * mMoreSuggestionsRowHeight + mMoreSuggestionsBottomGap; + } + + public int setMoreSuggestionsHeight(final int remainingHeight) { + final int currentHeight = getMoreSuggestionsHeight(); + if (currentHeight <= remainingHeight) { + return currentHeight; + } + + mMaxMoreSuggestionsRow = (remainingHeight - mMoreSuggestionsBottomGap) + / mMoreSuggestionsRowHeight; + final int newHeight = getMoreSuggestionsHeight(); + return newHeight; + } + + private static Drawable getMoreSuggestionsHint(final Resources res, final float textSize, + final int color) { + final Paint paint = new Paint(); + paint.setAntiAlias(true); + paint.setTextAlign(Align.CENTER); + paint.setTextSize(textSize); + paint.setColor(color); + final Rect bounds = new Rect(); + paint.getTextBounds(MORE_SUGGESTIONS_HINT, 0, MORE_SUGGESTIONS_HINT.length(), bounds); + final int width = Math.round(bounds.width() + 0.5f); + final int height = Math.round(bounds.height() + 0.5f); + final Bitmap buffer = Bitmap.createBitmap(width, (height * 3 / 2), Bitmap.Config.ARGB_8888); + final Canvas canvas = new Canvas(buffer); + canvas.drawText(MORE_SUGGESTIONS_HINT, width / 2, height, paint); + return new BitmapDrawable(res, buffer); + } + + private CharSequence getStyledSuggestedWord(final SuggestedWords suggestedWords, + final int indexInSuggestedWords) { + final String word = suggestedWords.getWord(indexInSuggestedWords); + final boolean isAutoCorrect = indexInSuggestedWords == 1 + && suggestedWords.willAutoCorrect(); + final boolean isTypedWordValid = indexInSuggestedWords == 0 + && suggestedWords.mTypedWordValid; + if (!isAutoCorrect && !isTypedWordValid) { + return word; + } + + final int len = word.length(); + final Spannable spannedWord = new SpannableString(word); + final int option = mSuggestionStripOption; + if ((isAutoCorrect && (option & AUTO_CORRECT_BOLD) != 0) + || (isTypedWordValid && (option & VALID_TYPED_WORD_BOLD) != 0)) { + spannedWord.setSpan(BOLD_SPAN, 0, len, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); + } + if (isAutoCorrect && (option & AUTO_CORRECT_UNDERLINE) != 0) { + spannedWord.setSpan(UNDERLINE_SPAN, 0, len, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); + } + return spannedWord; + } + + private int getIndexInSuggestedWords(final int positionInStrip, + final SuggestedWords suggestedWords) { + // TODO: This works for 3 suggestions. Revisit this algorithm when there are 5 or more + // suggestions. + final int mostImportantIndexInSuggestedWords = suggestedWords.willAutoCorrect() + ? SuggestedWords.INDEX_OF_AUTO_CORRECTION : SuggestedWords.INDEX_OF_TYPED_WORD; + if (positionInStrip == mCenterPositionInStrip) { + return mostImportantIndexInSuggestedWords; + } + if (positionInStrip == mostImportantIndexInSuggestedWords) { + return mCenterPositionInStrip; + } + return positionInStrip; + } + + private int getSuggestionTextColor(final int positionInStrip, + final SuggestedWords suggestedWords) { + final int indexInSuggestedWords = getIndexInSuggestedWords(positionInStrip, suggestedWords); + // TODO: Need to revisit this logic with bigram suggestions + final boolean isSuggested = (indexInSuggestedWords != SuggestedWords.INDEX_OF_TYPED_WORD); + + final int color; + if (positionInStrip == mCenterPositionInStrip && suggestedWords.willAutoCorrect()) { + color = mColorAutoCorrect; + } else if (positionInStrip == mCenterPositionInStrip && suggestedWords.mTypedWordValid) { + color = mColorValidTypedWord; + } else if (isSuggested) { + color = mColorSuggested; + } else { + color = mColorTypedWord; + } + if (LatinImeLogger.sDBG && suggestedWords.size() > 1) { + // If we auto-correct, then the autocorrection is in slot 0 and the typed word + // is in slot 1. + if (positionInStrip == mCenterPositionInStrip + && AutoCorrection.shouldBlockAutoCorrectionBySafetyNet( + suggestedWords.getWord(SuggestedWords.INDEX_OF_AUTO_CORRECTION), + suggestedWords.getWord(SuggestedWords.INDEX_OF_TYPED_WORD))) { + return 0xFFFF0000; + } + } + + if (suggestedWords.mIsObsoleteSuggestions && isSuggested) { + return applyAlpha(color, mAlphaObsoleted); + } + return color; + } + + private static int applyAlpha(final int color, final float alpha) { + final int newAlpha = (int)(Color.alpha(color) * alpha); + return Color.argb(newAlpha, Color.red(color), Color.green(color), Color.blue(color)); + } + + private static void addDivider(final ViewGroup stripView, final View dividerView) { + stripView.addView(dividerView); + final LinearLayout.LayoutParams params = + (LinearLayout.LayoutParams)dividerView.getLayoutParams(); + params.gravity = Gravity.CENTER; + } + + public void layout(final SuggestedWords suggestedWords, final ViewGroup stripView, + final ViewGroup placerView) { + if (suggestedWords.mIsPunctuationSuggestions) { + layoutPunctuationSuggestions(suggestedWords, stripView); + return; + } + + final int countInStrip = mSuggestionsCountInStrip; + setupWordViewsTextAndColor(suggestedWords, countInStrip); + mMoreSuggestionsAvailable = (suggestedWords.size() > countInStrip); + int x = 0; + for (int positionInStrip = 0; positionInStrip < countInStrip; positionInStrip++) { + if (positionInStrip != 0) { + final View divider = mDividerViews.get(positionInStrip); + // Add divider if this isn't the left most suggestion in suggestions strip. + addDivider(stripView, divider); + x += divider.getMeasuredWidth(); + } + + final int width = getSuggestionWidth(positionInStrip, placerView.getWidth()); + final TextView wordView = layoutWord(positionInStrip, width); + stripView.addView(wordView); + setLayoutWeight(wordView, getSuggestionWeight(positionInStrip), + ViewGroup.LayoutParams.MATCH_PARENT); + x += wordView.getMeasuredWidth(); + + if (SuggestionStripView.DBG) { + layoutDebugInfo(positionInStrip, placerView, x); + } + } + } + + /** + * Format appropriately the suggested word in {@link #mWordViews} specified by + * <code>positionInStrip</code>. When the suggested word doesn't exist, the corresponding + * {@link TextView} will be disabled and never respond to user interaction. The suggested word + * may be shrunk or ellipsized to fit in the specified width. + * + * The <code>positionInStrip</code> argument is the index in the suggestion strip. The indices + * increase towards the right for LTR scripts and the left for RTL scripts, starting with 0. + * The position of the most important suggestion is in {@link #mCenterPositionInStrip}. This + * usually doesn't match the index in <code>suggedtedWords</code> -- see + * {@link #getIndexInSuggestedWords(int,SuggestedWords)}. + * + * @param positionInStrip the position in the suggestion strip. + * @param width the maximum width for layout in pixels. + * @return the {@link TextView} containing the suggested word appropriately formatted. + */ + private TextView layoutWord(final int positionInStrip, final int width) { + final TextView wordView = mWordViews.get(positionInStrip); + final CharSequence word = wordView.getText(); + if (positionInStrip == mCenterPositionInStrip && mMoreSuggestionsAvailable) { + // TODO: This "more suggestions hint" should have a nicely designed icon. + wordView.setCompoundDrawablesWithIntrinsicBounds( + null, null, null, mMoreSuggestionsHint); + // HACK: Align with other TextViews that have no compound drawables. + wordView.setCompoundDrawablePadding(-mMoreSuggestionsHint.getIntrinsicHeight()); + } else { + wordView.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null); + } + + // Disable this suggestion if the suggestion is null or empty. + wordView.setEnabled(!TextUtils.isEmpty(word)); + final CharSequence text = getEllipsizedText(word, width, wordView.getPaint()); + final float scaleX = wordView.getTextScaleX(); + wordView.setText(text); // TextView.setText() resets text scale x to 1.0. + wordView.setTextScaleX(scaleX); + return wordView; + } + + private void layoutDebugInfo(final int positionInStrip, final ViewGroup placerView, + final int x) { + final TextView debugInfoView = mDebugInfoViews.get(positionInStrip); + final CharSequence debugInfo = debugInfoView.getText(); + if (debugInfo == null) { + return; + } + placerView.addView(debugInfoView); + debugInfoView.measure( + ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); + final int infoWidth = debugInfoView.getMeasuredWidth(); + final int y = debugInfoView.getMeasuredHeight(); + ViewLayoutUtils.placeViewAt( + debugInfoView, x - infoWidth, y, infoWidth, debugInfoView.getMeasuredHeight()); + } + + private int getSuggestionWidth(final int positionInStrip, final int maxWidth) { + final int paddings = mPadding * mSuggestionsCountInStrip; + final int dividers = mDividerWidth * (mSuggestionsCountInStrip - 1); + final int availableWidth = maxWidth - paddings - dividers; + return (int)(availableWidth * getSuggestionWeight(positionInStrip)); + } + + private float getSuggestionWeight(final int positionInStrip) { + if (positionInStrip == mCenterPositionInStrip) { + return mCenterSuggestionWeight; + } + // TODO: Revisit this for cases of 5 or more suggestions + return (1.0f - mCenterSuggestionWeight) / (mSuggestionsCountInStrip - 1); + } + + private void setupWordViewsTextAndColor(final SuggestedWords suggestedWords, + final int countInStrip) { + final int count = Math.min(suggestedWords.size(), countInStrip); + for (int positionInStrip = 0; positionInStrip < count; positionInStrip++) { + final int indexInSuggestedWords = + getIndexInSuggestedWords(positionInStrip, suggestedWords); + final TextView wordView = mWordViews.get(positionInStrip); + // {@link TextView#getTag()} is used to get the index in suggestedWords at + // {@link SuggestionStripView#onClick(View)}. + wordView.setTag(indexInSuggestedWords); + wordView.setText(getStyledSuggestedWord(suggestedWords, indexInSuggestedWords)); + wordView.setTextColor(getSuggestionTextColor(positionInStrip, suggestedWords)); + if (SuggestionStripView.DBG) { + mDebugInfoViews.get(positionInStrip).setText( + Utils.getDebugInfo(suggestedWords, indexInSuggestedWords)); + } + } + for (int positionInStrip = count; positionInStrip < countInStrip; positionInStrip++) { + mWordViews.get(positionInStrip).setText(null); + // Make this inactive for touches in {@link #layoutWord(int,int)}. + if (SuggestionStripView.DBG) { + mDebugInfoViews.get(positionInStrip).setText(null); + } + } + } + + private void layoutPunctuationSuggestions(final SuggestedWords suggestedWords, + final ViewGroup stripView) { + final int countInStrip = Math.min(suggestedWords.size(), PUNCTUATIONS_IN_STRIP); + for (int positionInStrip = 0; positionInStrip < countInStrip; positionInStrip++) { + if (positionInStrip != 0) { + // Add divider if this isn't the left most suggestion in suggestions strip. + addDivider(stripView, mDividerViews.get(positionInStrip)); + } + + final TextView wordView = mWordViews.get(positionInStrip); + wordView.setEnabled(true); + wordView.setTextColor(mColorAutoCorrect); + final String punctuation = suggestedWords.getWord(positionInStrip); + wordView.setText(punctuation); + wordView.setTextScaleX(1.0f); + wordView.setCompoundDrawables(null, null, null, null); + stripView.addView(wordView); + setLayoutWeight(wordView, 1.0f, mSuggestionsStripHeight); + } + mMoreSuggestionsAvailable = (suggestedWords.size() > countInStrip); + } + + public void layoutAddToDictionaryHint(final String word, final ViewGroup stripView, + final int stripWidth, final CharSequence hintText, final OnClickListener listener) { + final int width = stripWidth - mDividerWidth - mPadding * 2; + + final TextView wordView = mWordToSaveView; + wordView.setTextColor(mColorTypedWord); + final int wordWidth = (int)(width * mCenterSuggestionWeight); + final CharSequence text = getEllipsizedText(word, wordWidth, wordView.getPaint()); + final float wordScaleX = wordView.getTextScaleX(); + wordView.setTag(word); + wordView.setText(text); + wordView.setTextScaleX(wordScaleX); + stripView.addView(wordView); + setLayoutWeight(wordView, mCenterSuggestionWeight, ViewGroup.LayoutParams.MATCH_PARENT); + + stripView.addView(mDividerViews.get(0)); + + final TextView leftArrowView = mLeftwardsArrowView; + leftArrowView.setTextColor(mColorAutoCorrect); + leftArrowView.setText(LEFTWARDS_ARROW); + stripView.addView(leftArrowView); + + final TextView hintView = mHintToSaveView; + hintView.setGravity(Gravity.LEFT | Gravity.CENTER_VERTICAL); + hintView.setTextColor(mColorAutoCorrect); + final int hintWidth = width - wordWidth - leftArrowView.getWidth(); + final float hintScaleX = getTextScaleX(hintText, hintWidth, hintView.getPaint()); + hintView.setText(hintText); + hintView.setTextScaleX(hintScaleX); + stripView.addView(hintView); + setLayoutWeight( + hintView, 1.0f - mCenterSuggestionWeight, ViewGroup.LayoutParams.MATCH_PARENT); + + wordView.setOnClickListener(listener); + leftArrowView.setOnClickListener(listener); + hintView.setOnClickListener(listener); + } + + public CharSequence getAddToDictionaryWord() { + return (CharSequence)mWordToSaveView.getTag(); + } + + public boolean isAddToDictionaryShowing(final View v) { + return v == mWordToSaveView || v == mHintToSaveView || v == mLeftwardsArrowView; + } + + private static void setLayoutWeight(final View v, final float weight, final int height) { + final ViewGroup.LayoutParams lp = v.getLayoutParams(); + if (lp instanceof LinearLayout.LayoutParams) { + final LinearLayout.LayoutParams llp = (LinearLayout.LayoutParams)lp; + llp.weight = weight; + llp.width = 0; + llp.height = height; + } + } + + private static float getTextScaleX(final CharSequence text, final int maxWidth, + final TextPaint paint) { + paint.setTextScaleX(1.0f); + final int width = getTextWidth(text, paint); + if (width <= maxWidth) { + return 1.0f; + } + return maxWidth / (float)width; + } + + private static CharSequence getEllipsizedText(final CharSequence text, final int maxWidth, + final TextPaint paint) { + if (text == null) { + return null; + } + final float scaleX = getTextScaleX(text, maxWidth, paint); + if (scaleX >= MIN_TEXT_XSCALE) { + paint.setTextScaleX(scaleX); + return text; + } + + // Note that TextUtils.ellipsize() use text-x-scale as 1.0 if ellipsize is needed. To + // get squeezed and ellipsized text, passes enlarged width (maxWidth / MIN_TEXT_XSCALE). + final CharSequence ellipsized = TextUtils.ellipsize( + text, paint, maxWidth / MIN_TEXT_XSCALE, TextUtils.TruncateAt.MIDDLE); + paint.setTextScaleX(MIN_TEXT_XSCALE); + return ellipsized; + } + + private static int getTextWidth(final CharSequence text, final TextPaint paint) { + if (TextUtils.isEmpty(text)) { + return 0; + } + final Typeface savedTypeface = paint.getTypeface(); + paint.setTypeface(getTextTypeface(text)); + final int len = text.length(); + final float[] widths = new float[len]; + final int count = paint.getTextWidths(text, 0, len, widths); + int width = 0; + for (int i = 0; i < count; i++) { + width += Math.round(widths[i] + 0.5f); + } + paint.setTypeface(savedTypeface); + return width; + } + + private static Typeface getTextTypeface(final CharSequence text) { + if (!(text instanceof SpannableString)) { + return Typeface.DEFAULT; + } + + final SpannableString ss = (SpannableString)text; + final StyleSpan[] styles = ss.getSpans(0, text.length(), StyleSpan.class); + if (styles.length == 0) { + return Typeface.DEFAULT; + } + + if (styles[0].getStyle() == Typeface.BOLD) { + return Typeface.DEFAULT_BOLD; + } + // TODO: BOLD_ITALIC, ITALIC case? + return Typeface.DEFAULT; + } +} diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java index 9764610b3..b2b9427af 100644 --- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java +++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java @@ -18,34 +18,14 @@ package com.android.inputmethod.latin.suggestions; import android.content.Context; import android.content.res.Resources; -import android.content.res.TypedArray; -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.graphics.Paint.Align; -import android.graphics.Rect; -import android.graphics.Typeface; -import android.graphics.drawable.BitmapDrawable; -import android.graphics.drawable.Drawable; -import android.text.Spannable; -import android.text.SpannableString; -import android.text.Spanned; -import android.text.TextPaint; -import android.text.TextUtils; -import android.text.style.CharacterStyle; -import android.text.style.StyleSpan; -import android.text.style.UnderlineSpan; import android.util.AttributeSet; import android.view.GestureDetector; -import android.view.Gravity; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.View.OnClickListener; import android.view.View.OnLongClickListener; import android.view.ViewGroup; -import android.widget.LinearLayout; import android.widget.RelativeLayout; import android.widget.TextView; @@ -53,17 +33,13 @@ import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.keyboard.KeyboardSwitcher; import com.android.inputmethod.keyboard.MainKeyboardView; import com.android.inputmethod.keyboard.MoreKeysPanel; -import com.android.inputmethod.keyboard.ViewLayoutUtils; import com.android.inputmethod.latin.AudioAndHapticFeedbackManager; -import com.android.inputmethod.latin.AutoCorrection; import com.android.inputmethod.latin.CollectionUtils; import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.LatinImeLogger; import com.android.inputmethod.latin.R; -import com.android.inputmethod.latin.ResourceUtils; import com.android.inputmethod.latin.SuggestedWords; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; -import com.android.inputmethod.latin.Utils; import com.android.inputmethod.latin.define.ProductionFlag; import com.android.inputmethod.latin.suggestions.MoreSuggestions.MoreSuggestionsListener; import com.android.inputmethod.research.ResearchLogger; @@ -89,516 +65,14 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick private final MoreSuggestionsView mMoreSuggestionsView; private final MoreSuggestions.Builder mMoreSuggestionsBuilder; - private final ArrayList<TextView> mWords = CollectionUtils.newArrayList(); - private final ArrayList<TextView> mInfos = CollectionUtils.newArrayList(); - private final ArrayList<View> mDividers = CollectionUtils.newArrayList(); + private final ArrayList<TextView> mWordViews = CollectionUtils.newArrayList(); + private final ArrayList<TextView> mDebugInfoViews = CollectionUtils.newArrayList(); + private final ArrayList<View> mDividerViews = CollectionUtils.newArrayList(); Listener mListener; private SuggestedWords mSuggestedWords = SuggestedWords.EMPTY; - private final SuggestionStripViewParams mParams; - private static final float MIN_TEXT_XSCALE = 0.70f; - - private static final class SuggestionStripViewParams { - private static final int DEFAULT_SUGGESTIONS_COUNT_IN_STRIP = 3; - private static final float DEFAULT_CENTER_SUGGESTION_PERCENTILE = 0.40f; - private static final int DEFAULT_MAX_MORE_SUGGESTIONS_ROW = 2; - private static final int PUNCTUATIONS_IN_STRIP = 5; - - public final int mPadding; - public final int mDividerWidth; - public final int mSuggestionsStripHeight; - public final int mSuggestionsCountInStrip; - public final int mMoreSuggestionsRowHeight; - private int mMaxMoreSuggestionsRow; - public final float mMinMoreSuggestionsWidth; - public final int mMoreSuggestionsBottomGap; - - private final ArrayList<TextView> mWords; - private final ArrayList<View> mDividers; - private final ArrayList<TextView> mInfos; - - private final int mColorValidTypedWord; - private final int mColorTypedWord; - private final int mColorAutoCorrect; - private final int mColorSuggested; - private final float mAlphaObsoleted; - private final float mCenterSuggestionWeight; - private final int mCenterSuggestionIndex; - private final Drawable mMoreSuggestionsHint; - private static final String MORE_SUGGESTIONS_HINT = "\u2026"; - private static final String LEFTWARDS_ARROW = "\u2190"; - - private static final CharacterStyle BOLD_SPAN = new StyleSpan(Typeface.BOLD); - private static final CharacterStyle UNDERLINE_SPAN = new UnderlineSpan(); - private static final int AUTO_CORRECT_BOLD = 0x01; - private static final int AUTO_CORRECT_UNDERLINE = 0x02; - private static final int VALID_TYPED_WORD_BOLD = 0x04; - - private final int mSuggestionStripOption; - - private final ArrayList<CharSequence> mTexts = CollectionUtils.newArrayList(); - - public boolean mMoreSuggestionsAvailable; - - private final TextView mWordToSaveView; - private final TextView mLeftwardsArrowView; - private final TextView mHintToSaveView; - - public SuggestionStripViewParams(final Context context, final AttributeSet attrs, - final int defStyle, final ArrayList<TextView> words, final ArrayList<View> dividers, - final ArrayList<TextView> infos) { - mWords = words; - mDividers = dividers; - mInfos = infos; - - final TextView word = words.get(0); - final View divider = dividers.get(0); - mPadding = word.getCompoundPaddingLeft() + word.getCompoundPaddingRight(); - divider.measure( - ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); - mDividerWidth = divider.getMeasuredWidth(); - - final Resources res = word.getResources(); - mSuggestionsStripHeight = res.getDimensionPixelSize(R.dimen.suggestions_strip_height); - - final TypedArray a = context.obtainStyledAttributes(attrs, - R.styleable.SuggestionStripView, defStyle, R.style.SuggestionStripViewStyle); - mSuggestionStripOption = a.getInt( - R.styleable.SuggestionStripView_suggestionStripOption, 0); - final float alphaValidTypedWord = ResourceUtils.getFraction(a, - R.styleable.SuggestionStripView_alphaValidTypedWord, 1.0f); - final float alphaTypedWord = ResourceUtils.getFraction(a, - R.styleable.SuggestionStripView_alphaTypedWord, 1.0f); - final float alphaAutoCorrect = ResourceUtils.getFraction(a, - R.styleable.SuggestionStripView_alphaAutoCorrect, 1.0f); - final float alphaSuggested = ResourceUtils.getFraction(a, - R.styleable.SuggestionStripView_alphaSuggested, 1.0f); - mAlphaObsoleted = ResourceUtils.getFraction(a, - R.styleable.SuggestionStripView_alphaSuggested, 1.0f); - mColorValidTypedWord = applyAlpha(a.getColor( - R.styleable.SuggestionStripView_colorValidTypedWord, 0), alphaValidTypedWord); - mColorTypedWord = applyAlpha(a.getColor( - R.styleable.SuggestionStripView_colorTypedWord, 0), alphaTypedWord); - mColorAutoCorrect = applyAlpha(a.getColor( - R.styleable.SuggestionStripView_colorAutoCorrect, 0), alphaAutoCorrect); - mColorSuggested = applyAlpha(a.getColor( - R.styleable.SuggestionStripView_colorSuggested, 0), alphaSuggested); - mSuggestionsCountInStrip = a.getInt( - R.styleable.SuggestionStripView_suggestionsCountInStrip, - DEFAULT_SUGGESTIONS_COUNT_IN_STRIP); - mCenterSuggestionWeight = ResourceUtils.getFraction(a, - R.styleable.SuggestionStripView_centerSuggestionPercentile, - DEFAULT_CENTER_SUGGESTION_PERCENTILE); - mMaxMoreSuggestionsRow = a.getInt( - R.styleable.SuggestionStripView_maxMoreSuggestionsRow, - DEFAULT_MAX_MORE_SUGGESTIONS_ROW); - mMinMoreSuggestionsWidth = ResourceUtils.getFraction(a, - R.styleable.SuggestionStripView_minMoreSuggestionsWidth, 1.0f); - a.recycle(); - - mMoreSuggestionsHint = getMoreSuggestionsHint(res, - res.getDimension(R.dimen.more_suggestions_hint_text_size), mColorAutoCorrect); - mCenterSuggestionIndex = mSuggestionsCountInStrip / 2; - mMoreSuggestionsBottomGap = res.getDimensionPixelOffset( - R.dimen.more_suggestions_bottom_gap); - mMoreSuggestionsRowHeight = res.getDimensionPixelSize( - R.dimen.more_suggestions_row_height); - - final LayoutInflater inflater = LayoutInflater.from(context); - mWordToSaveView = (TextView)inflater.inflate(R.layout.suggestion_word, null); - mLeftwardsArrowView = (TextView)inflater.inflate(R.layout.hint_add_to_dictionary, null); - mHintToSaveView = (TextView)inflater.inflate(R.layout.hint_add_to_dictionary, null); - } - - public int getMaxMoreSuggestionsRow() { - return mMaxMoreSuggestionsRow; - } - - private int getMoreSuggestionsHeight() { - return mMaxMoreSuggestionsRow * mMoreSuggestionsRowHeight + mMoreSuggestionsBottomGap; - } - - public int setMoreSuggestionsHeight(final int remainingHeight) { - final int currentHeight = getMoreSuggestionsHeight(); - if (currentHeight <= remainingHeight) { - return currentHeight; - } - - mMaxMoreSuggestionsRow = (remainingHeight - mMoreSuggestionsBottomGap) - / mMoreSuggestionsRowHeight; - final int newHeight = getMoreSuggestionsHeight(); - return newHeight; - } - - private static Drawable getMoreSuggestionsHint(final Resources res, final float textSize, - final int color) { - final Paint paint = new Paint(); - paint.setAntiAlias(true); - paint.setTextAlign(Align.CENTER); - paint.setTextSize(textSize); - paint.setColor(color); - final Rect bounds = new Rect(); - paint.getTextBounds(MORE_SUGGESTIONS_HINT, 0, MORE_SUGGESTIONS_HINT.length(), bounds); - final int width = Math.round(bounds.width() + 0.5f); - final int height = Math.round(bounds.height() + 0.5f); - final Bitmap buffer = Bitmap.createBitmap( - width, (height * 3 / 2), Bitmap.Config.ARGB_8888); - final Canvas canvas = new Canvas(buffer); - canvas.drawText(MORE_SUGGESTIONS_HINT, width / 2, height, paint); - return new BitmapDrawable(res, buffer); - } - - private CharSequence getStyledSuggestionWord(final SuggestedWords suggestedWords, - final int indexInSuggestedWords) { - final String word = suggestedWords.getWord(indexInSuggestedWords); - final boolean isAutoCorrect = indexInSuggestedWords == 1 - && suggestedWords.willAutoCorrect(); - final boolean isTypedWordValid = indexInSuggestedWords == 0 - && suggestedWords.mTypedWordValid; - if (!isAutoCorrect && !isTypedWordValid) - return word; - - final int len = word.length(); - final Spannable spannedWord = new SpannableString(word); - final int option = mSuggestionStripOption; - if ((isAutoCorrect && (option & AUTO_CORRECT_BOLD) != 0) - || (isTypedWordValid && (option & VALID_TYPED_WORD_BOLD) != 0)) { - spannedWord.setSpan(BOLD_SPAN, 0, len, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); - } - if (isAutoCorrect && (option & AUTO_CORRECT_UNDERLINE) != 0) { - spannedWord.setSpan(UNDERLINE_SPAN, 0, len, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); - } - return spannedWord; - } - - private int getIndexInSuggestedWords(final int indexInStrip, - final SuggestedWords suggestedWords) { - // TODO: This works for 3 suggestions. Revisit this algorithm when there are 5 or more - // suggestions. - final int mostImportantIndexInSuggestedWords = suggestedWords.willAutoCorrect() ? 1 : 0; - if (indexInStrip == mCenterSuggestionIndex) { - return mostImportantIndexInSuggestedWords; - } else if (indexInStrip == mostImportantIndexInSuggestedWords) { - return mCenterSuggestionIndex; - } else { - return indexInStrip; - } - } - - private int getSuggestionTextColor(final int indexInStrip, - final SuggestedWords suggestedWords) { - final int indexInSuggestedWords = getIndexInSuggestedWords( - indexInStrip, suggestedWords); - // TODO: Need to revisit this logic with bigram suggestions - final boolean isSuggested = (indexInSuggestedWords != 0); - - final int color; - if (indexInStrip == mCenterSuggestionIndex && suggestedWords.willAutoCorrect()) { - color = mColorAutoCorrect; - } else if (indexInStrip == mCenterSuggestionIndex && suggestedWords.mTypedWordValid) { - color = mColorValidTypedWord; - } else if (isSuggested) { - color = mColorSuggested; - } else { - color = mColorTypedWord; - } - if (LatinImeLogger.sDBG && suggestedWords.size() > 1) { - // If we auto-correct, then the autocorrection is in slot 0 and the typed word - // is in slot 1. - if (indexInStrip == mCenterSuggestionIndex - && AutoCorrection.shouldBlockAutoCorrectionBySafetyNet( - suggestedWords.getWord(1), suggestedWords.getWord(0))) { - return 0xFFFF0000; - } - } - - if (suggestedWords.mIsObsoleteSuggestions && isSuggested) { - return applyAlpha(color, mAlphaObsoleted); - } else { - return color; - } - } - - private static int applyAlpha(final int color, final float alpha) { - final int newAlpha = (int)(Color.alpha(color) * alpha); - return Color.argb(newAlpha, Color.red(color), Color.green(color), Color.blue(color)); - } - - private static void addDivider(final ViewGroup stripView, final View divider) { - stripView.addView(divider); - final LinearLayout.LayoutParams params = - (LinearLayout.LayoutParams)divider.getLayoutParams(); - params.gravity = Gravity.CENTER; - } - - public void layout(final SuggestedWords suggestedWords, final ViewGroup stripView, - final ViewGroup placer, final int stripWidth) { - if (suggestedWords.mIsPunctuationSuggestions) { - layoutPunctuationSuggestions(suggestedWords, stripView); - return; - } - - final int countInStrip = mSuggestionsCountInStrip; - setupTexts(suggestedWords, countInStrip); - mMoreSuggestionsAvailable = (suggestedWords.size() > countInStrip); - int x = 0; - for (int indexInStrip = 0; indexInStrip < countInStrip; indexInStrip++) { - if (indexInStrip != 0) { - final View divider = mDividers.get(indexInStrip); - // Add divider if this isn't the left most suggestion in suggestions strip. - addDivider(stripView, divider); - x += divider.getMeasuredWidth(); - } - - final int width = getSuggestionWidth(indexInStrip, stripWidth); - final TextView word = layoutWord(suggestedWords, indexInStrip, width); - stripView.addView(word); - setLayoutWeight(word, getSuggestionWeight(indexInStrip), - ViewGroup.LayoutParams.MATCH_PARENT); - x += word.getMeasuredWidth(); - - if (DBG) { - layoutDebugInfo(suggestedWords, indexInStrip, placer, x); - } - } - } - - /** - * Format appropriately the suggested word indirectly specified by - * <code>indexInStrip</code> as text in a corresponding {@link TextView}. When the - * suggested word doesn't exist, the corresponding {@link TextView} will be disabled - * and never respond to user interaction. The suggested word may be shrunk or ellipsized to - * fit in the specified width. - * - * The <code>indexInStrip</code> argument is the index in the suggestion strip. The indices - * increase towards the right for LTR scripts and the left for RTL scripts, starting with 0. - * The index of the most important suggestion is in {@link #mCenterSuggestionIndex}. This - * usually doesn't match the index in <code>suggedtedWords</code> -- see - * {@link #getIndexInSuggestedWords(int,SuggestedWords)}. - * - * @param suggestedWords the list of suggestions. - * @param indexInStrip the in the suggestion strip. - * @param width the maximum width for layout in pixels. - * @return the {@link TextView} containing the suggested word appropriately formatted. - */ - private TextView layoutWord(final SuggestedWords suggestedWords, final int indexInStrip, - final int width) { - final int indexInSuggestedWords = getIndexInSuggestedWords( - indexInStrip, suggestedWords); - final CharSequence styled = mTexts.get(indexInSuggestedWords); - final TextView word = mWords.get(indexInSuggestedWords); - if (indexInStrip == mCenterSuggestionIndex && mMoreSuggestionsAvailable) { - // TODO: This "more suggestions hint" should have a nicely designed icon. - word.setCompoundDrawablesWithIntrinsicBounds( - null, null, null, mMoreSuggestionsHint); - // HACK: Align with other TextViews that have no compound drawables. - word.setCompoundDrawablePadding(-mMoreSuggestionsHint.getIntrinsicHeight()); - } else { - word.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null); - } - - // Disable this suggestion if the suggestion is null or empty. - word.setEnabled(!TextUtils.isEmpty(styled)); - word.setTextColor(getSuggestionTextColor(indexInStrip, suggestedWords)); - final CharSequence text = getEllipsizedText(styled, width, word.getPaint()); - final float scaleX = word.getTextScaleX(); - word.setText(text); // TextView.setText() resets text scale x to 1.0. - word.setTextScaleX(scaleX); - return word; - } - - private void layoutDebugInfo(final SuggestedWords suggestedWords, final int indexInStrip, - final ViewGroup placer, final int x) { - final int indexInSuggestedWords = getIndexInSuggestedWords( - indexInStrip, suggestedWords); - if (indexInSuggestedWords >= suggestedWords.size()) { - return; - } - final String debugInfo = Utils.getDebugInfo(suggestedWords, indexInSuggestedWords); - if (debugInfo == null) { - return; - } - final TextView info = mInfos.get(indexInSuggestedWords); - info.setText(debugInfo); - placer.addView(info); - info.measure(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); - final int infoWidth = info.getMeasuredWidth(); - final int y = info.getMeasuredHeight(); - ViewLayoutUtils.placeViewAt( - info, x - infoWidth, y, infoWidth, info.getMeasuredHeight()); - } - - private int getSuggestionWidth(final int indexInStrip, final int maxWidth) { - final int paddings = mPadding * mSuggestionsCountInStrip; - final int dividers = mDividerWidth * (mSuggestionsCountInStrip - 1); - final int availableWidth = maxWidth - paddings - dividers; - return (int)(availableWidth * getSuggestionWeight(indexInStrip)); - } - - private float getSuggestionWeight(final int indexInStrip) { - if (indexInStrip == mCenterSuggestionIndex) { - return mCenterSuggestionWeight; - } else { - // TODO: Revisit this for cases of 5 or more suggestions - return (1.0f - mCenterSuggestionWeight) / (mSuggestionsCountInStrip - 1); - } - } - - private void setupTexts(final SuggestedWords suggestedWords, final int countInStrip) { - mTexts.clear(); - final int count = Math.min(suggestedWords.size(), countInStrip); - for (int pos = 0; pos < count; pos++) { - final CharSequence styled = getStyledSuggestionWord(suggestedWords, pos); - mTexts.add(styled); - } - for (int pos = count; pos < countInStrip; pos++) { - // Make this inactive for touches in layout(). - mTexts.add(null); - } - } - - private void layoutPunctuationSuggestions(final SuggestedWords suggestedWords, - final ViewGroup stripView) { - final int countInStrip = Math.min(suggestedWords.size(), PUNCTUATIONS_IN_STRIP); - for (int indexInStrip = 0; indexInStrip < countInStrip; indexInStrip++) { - if (indexInStrip != 0) { - // Add divider if this isn't the left most suggestion in suggestions strip. - addDivider(stripView, mDividers.get(indexInStrip)); - } - - final TextView word = mWords.get(indexInStrip); - word.setEnabled(true); - word.setTextColor(mColorAutoCorrect); - final String text = suggestedWords.getWord(indexInStrip); - word.setText(text); - word.setTextScaleX(1.0f); - word.setCompoundDrawables(null, null, null, null); - stripView.addView(word); - setLayoutWeight(word, 1.0f, mSuggestionsStripHeight); - } - mMoreSuggestionsAvailable = false; - } - - public void layoutAddToDictionaryHint(final String word, final ViewGroup stripView, - final int stripWidth, final CharSequence hintText, final OnClickListener listener) { - final int width = stripWidth - mDividerWidth - mPadding * 2; - - final TextView wordView = mWordToSaveView; - wordView.setTextColor(mColorTypedWord); - final int wordWidth = (int)(width * mCenterSuggestionWeight); - final CharSequence text = getEllipsizedText(word, wordWidth, wordView.getPaint()); - final float wordScaleX = wordView.getTextScaleX(); - wordView.setTag(word); - wordView.setText(text); - wordView.setTextScaleX(wordScaleX); - stripView.addView(wordView); - setLayoutWeight(wordView, mCenterSuggestionWeight, ViewGroup.LayoutParams.MATCH_PARENT); - - stripView.addView(mDividers.get(0)); - - final TextView leftArrowView = mLeftwardsArrowView; - leftArrowView.setTextColor(mColorAutoCorrect); - leftArrowView.setText(LEFTWARDS_ARROW); - stripView.addView(leftArrowView); - - final TextView hintView = mHintToSaveView; - hintView.setGravity(Gravity.LEFT | Gravity.CENTER_VERTICAL); - hintView.setTextColor(mColorAutoCorrect); - final int hintWidth = width - wordWidth - leftArrowView.getWidth(); - final float hintScaleX = getTextScaleX(hintText, hintWidth, hintView.getPaint()); - hintView.setText(hintText); - hintView.setTextScaleX(hintScaleX); - stripView.addView(hintView); - setLayoutWeight( - hintView, 1.0f - mCenterSuggestionWeight, ViewGroup.LayoutParams.MATCH_PARENT); - - wordView.setOnClickListener(listener); - leftArrowView.setOnClickListener(listener); - hintView.setOnClickListener(listener); - } - - public CharSequence getAddToDictionaryWord() { - return (CharSequence)mWordToSaveView.getTag(); - } - - public boolean isAddToDictionaryShowing(final View v) { - return v == mWordToSaveView || v == mHintToSaveView || v == mLeftwardsArrowView; - } - - private static void setLayoutWeight(final View v, final float weight, final int height) { - final ViewGroup.LayoutParams lp = v.getLayoutParams(); - if (lp instanceof LinearLayout.LayoutParams) { - final LinearLayout.LayoutParams llp = (LinearLayout.LayoutParams)lp; - llp.weight = weight; - llp.width = 0; - llp.height = height; - } - } - - private static float getTextScaleX(final CharSequence text, final int maxWidth, - final TextPaint paint) { - paint.setTextScaleX(1.0f); - final int width = getTextWidth(text, paint); - if (width <= maxWidth) { - return 1.0f; - } - return maxWidth / (float)width; - } - - private static CharSequence getEllipsizedText(final CharSequence text, final int maxWidth, - final TextPaint paint) { - if (text == null) return null; - paint.setTextScaleX(1.0f); - final int width = getTextWidth(text, paint); - if (width <= maxWidth) { - return text; - } - final float scaleX = maxWidth / (float)width; - if (scaleX >= MIN_TEXT_XSCALE) { - paint.setTextScaleX(scaleX); - return text; - } - - // Note that TextUtils.ellipsize() use text-x-scale as 1.0 if ellipsize is needed. To - // get squeezed and ellipsized text, passes enlarged width (maxWidth / MIN_TEXT_XSCALE). - final CharSequence ellipsized = TextUtils.ellipsize( - text, paint, maxWidth / MIN_TEXT_XSCALE, TextUtils.TruncateAt.MIDDLE); - paint.setTextScaleX(MIN_TEXT_XSCALE); - return ellipsized; - } - - private static int getTextWidth(final CharSequence text, final TextPaint paint) { - if (TextUtils.isEmpty(text)) return 0; - final Typeface savedTypeface = paint.getTypeface(); - paint.setTypeface(getTextTypeface(text)); - final int len = text.length(); - final float[] widths = new float[len]; - final int count = paint.getTextWidths(text, 0, len, widths); - int width = 0; - for (int i = 0; i < count; i++) { - width += Math.round(widths[i] + 0.5f); - } - paint.setTypeface(savedTypeface); - return width; - } - - private static Typeface getTextTypeface(final CharSequence text) { - if (!(text instanceof SpannableString)) - return Typeface.DEFAULT; - - final SpannableString ss = (SpannableString)text; - final StyleSpan[] styles = ss.getSpans(0, text.length(), StyleSpan.class); - if (styles.length == 0) - return Typeface.DEFAULT; - - switch (styles[0].getStyle()) { - case Typeface.BOLD: return Typeface.DEFAULT_BOLD; - // TODO: BOLD_ITALIC, ITALIC case? - default: return Typeface.DEFAULT; - } - } - } + private final SuggestionStripLayoutHelper mLayoutHelper; /** * Construct a {@link SuggestionStripView} for showing suggestions to be picked by the user. @@ -619,19 +93,17 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick mSuggestionsStrip = (ViewGroup)findViewById(R.id.suggestions_strip); for (int pos = 0; pos < MAX_SUGGESTIONS; pos++) { final TextView word = (TextView)inflater.inflate(R.layout.suggestion_word, null); - word.setTag(pos); word.setOnClickListener(this); word.setOnLongClickListener(this); - mWords.add(word); + mWordViews.add(word); final View divider = inflater.inflate(R.layout.suggestion_divider, null); - divider.setTag(pos); divider.setOnClickListener(this); - mDividers.add(divider); - mInfos.add((TextView)inflater.inflate(R.layout.suggestion_info, null)); + mDividerViews.add(divider); + mDebugInfoViews.add((TextView)inflater.inflate(R.layout.suggestion_info, null)); } - mParams = new SuggestionStripViewParams( - context, attrs, defStyle, mWords, mDividers, mInfos); + mLayoutHelper = new SuggestionStripLayoutHelper( + context, attrs, defStyle, mWordViews, mDividerViews, mDebugInfoViews); mMoreSuggestionsContainer = inflater.inflate(R.layout.more_suggestions, null); mMoreSuggestionsView = (MoreSuggestionsView)mMoreSuggestionsContainer @@ -657,24 +129,25 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick public void setSuggestions(final SuggestedWords suggestedWords) { clear(); mSuggestedWords = suggestedWords; - mParams.layout(mSuggestedWords, mSuggestionsStrip, this, getWidth()); + mLayoutHelper.layout(mSuggestedWords, mSuggestionsStrip, this); if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { ResearchLogger.suggestionStripView_setSuggestions(mSuggestedWords); } } public int setMoreSuggestionsHeight(final int remainingHeight) { - return mParams.setMoreSuggestionsHeight(remainingHeight); + return mLayoutHelper.setMoreSuggestionsHeight(remainingHeight); } public boolean isShowingAddToDictionaryHint() { return mSuggestionsStrip.getChildCount() > 0 - && mParams.isAddToDictionaryShowing(mSuggestionsStrip.getChildAt(0)); + && mLayoutHelper.isAddToDictionaryShowing(mSuggestionsStrip.getChildAt(0)); } public void showAddToDictionaryHint(final String word, final CharSequence hintText) { clear(); - mParams.layoutAddToDictionaryHint(word, mSuggestionsStrip, getWidth(), hintText, this); + mLayoutHelper.layoutAddToDictionaryHint( + word, mSuggestionsStrip, getWidth(), hintText, this); } public boolean dismissAddToDictionaryHint() { @@ -739,30 +212,30 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick if (parentKeyboard == null) { return false; } - final SuggestionStripViewParams params = mParams; - if (!params.mMoreSuggestionsAvailable) { + final SuggestionStripLayoutHelper layoutHelper = mLayoutHelper; + if (!layoutHelper.mMoreSuggestionsAvailable) { return false; } final int stripWidth = getWidth(); final View container = mMoreSuggestionsContainer; final int maxWidth = stripWidth - container.getPaddingLeft() - container.getPaddingRight(); final MoreSuggestions.Builder builder = mMoreSuggestionsBuilder; - builder.layout(mSuggestedWords, params.mSuggestionsCountInStrip, maxWidth, - (int)(maxWidth * params.mMinMoreSuggestionsWidth), - params.getMaxMoreSuggestionsRow(), parentKeyboard); + builder.layout(mSuggestedWords, layoutHelper.mSuggestionsCountInStrip, maxWidth, + (int)(maxWidth * layoutHelper.mMinMoreSuggestionsWidth), + layoutHelper.getMaxMoreSuggestionsRow(), parentKeyboard); mMoreSuggestionsView.setKeyboard(builder.build()); container.measure(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); final MoreKeysPanel moreKeysPanel = mMoreSuggestionsView; final int pointX = stripWidth / 2; - final int pointY = -params.mMoreSuggestionsBottomGap; + final int pointY = -layoutHelper.mMoreSuggestionsBottomGap; moreKeysPanel.showMoreKeysPanel(this, mMoreSuggestionsController, pointX, pointY, mMoreSuggestionsListener); mMoreSuggestionsMode = MORE_SUGGESTIONS_CHECKING_MODAL_OR_SLIDING; mOriginX = mLastX; mOriginY = mLastY; - for (int i = 0; i < params.mSuggestionsCountInStrip; i++) { - mWords.get(i).setPressed(false); + for (int i = 0; i < layoutHelper.mSuggestionsCountInStrip; i++) { + mWordViews.get(i).setPressed(false); } return true; } @@ -832,18 +305,20 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick @Override public void onClick(final View view) { - if (mParams.isAddToDictionaryShowing(view)) { - mListener.addWordToUserDictionary(mParams.getAddToDictionaryWord().toString()); + if (mLayoutHelper.isAddToDictionaryShowing(view)) { + mListener.addWordToUserDictionary(mLayoutHelper.getAddToDictionaryWord().toString()); clear(); return; } final Object tag = view.getTag(); - if (!(tag instanceof Integer)) + if (!(tag instanceof Integer)) { return; + } final int index = (Integer) tag; - if (index >= mSuggestedWords.size()) + if (index >= mSuggestedWords.size()) { return; + } final SuggestedWordInfo wordInfo = mSuggestedWords.getInfo(index); mListener.pickSuggestionManually(index, wordInfo); diff --git a/native/jni/Android.mk b/native/jni/Android.mk index cbe9515fe..1b758c680 100644 --- a/native/jni/Android.mk +++ b/native/jni/Android.mk @@ -46,17 +46,9 @@ LATIN_IME_JNI_SRC_FILES := \ jni_common.cpp LATIN_IME_CORE_SRC_FILES := \ - additional_proximity_chars.cpp \ bigram_dictionary.cpp \ - char_utils.cpp \ correction.cpp \ - dictionary.cpp \ dic_traverse_wrapper.cpp \ - digraph_utils.cpp \ - proximity_info.cpp \ - proximity_info_params.cpp \ - proximity_info_state.cpp \ - proximity_info_state_utils.cpp \ unigram_dictionary.cpp \ words_priority_queue.cpp \ suggest/core/suggest.cpp \ @@ -64,6 +56,16 @@ LATIN_IME_CORE_SRC_FILES := \ dic_node.cpp \ dic_node_utils.cpp \ dic_nodes_cache.cpp) \ + $(addprefix suggest/core/dictionary/, \ + char_utils.cpp \ + dictionary.cpp \ + digraph_utils.cpp) \ + $(addprefix suggest/core/layout/, \ + additional_proximity_chars.cpp \ + proximity_info.cpp \ + proximity_info_params.cpp \ + proximity_info_state.cpp \ + proximity_info_state_utils.cpp) \ suggest/core/policy/weighting.cpp \ suggest/core/session/dic_traverse_session.cpp \ suggest/policyimpl/gesture/gesture_suggest_policy_factory.cpp \ diff --git a/native/jni/com_android_inputmethod_keyboard_ProximityInfo.cpp b/native/jni/com_android_inputmethod_keyboard_ProximityInfo.cpp index dedb02abf..e312aeabc 100644 --- a/native/jni/com_android_inputmethod_keyboard_ProximityInfo.cpp +++ b/native/jni/com_android_inputmethod_keyboard_ProximityInfo.cpp @@ -17,10 +17,11 @@ #define LOG_TAG "LatinIME: jni: ProximityInfo" #include "com_android_inputmethod_keyboard_ProximityInfo.h" + #include "defines.h" #include "jni.h" #include "jni_common.h" -#include "proximity_info.h" +#include "suggest/core/layout/proximity_info.h" namespace latinime { diff --git a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp index 0e9c29261..9f5e2ae73 100644 --- a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp +++ b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp @@ -29,13 +29,14 @@ #include <cstdio> // for fopen() etc. #endif // USE_MMAP_FOR_DICTIONARY -#include "binary_format.h" #include "com_android_inputmethod_latin_BinaryDictionary.h" + #include "correction.h" -#include "dictionary.h" #include "jni.h" #include "jni_common.h" #include "suggest_options.h" +#include "suggest/core/dictionary/binary_format.h" +#include "suggest/core/dictionary/dictionary.h" namespace latinime { @@ -162,7 +163,7 @@ static int latinime_BinaryDictionary_getSuggestions(JNIEnv *env, jclass clazz, j const jsize numberOfOptions = env->GetArrayLength(suggestOptions); int options[numberOfOptions]; env->GetIntArrayRegion(suggestOptions, 0, numberOfOptions, options); - SuggestOptions givenOptions(options, numberOfOptions); + SuggestOptions givenSuggestOptions(options, numberOfOptions); // Output values /* By the way, let's check the output array length here to make sure */ @@ -190,12 +191,11 @@ static int latinime_BinaryDictionary_getSuggestions(JNIEnv *env, jclass clazz, j memset(outputTypes, 0, sizeof(outputTypes)); int count; - if (givenOptions.isGesture() || inputSize > 0) { + if (givenSuggestOptions.isGesture() || inputSize > 0) { count = dictionary->getSuggestions(pInfo, traverseSession, xCoordinates, yCoordinates, times, pointerIds, inputCodePoints, inputSize, prevWordCodePoints, - prevWordCodePointsLength, commitPoint, givenOptions.isGesture(), - givenOptions.useFullEditDistance(), outputCodePoints, scores, - spaceIndices, outputTypes); + prevWordCodePointsLength, commitPoint, &givenSuggestOptions, outputCodePoints, + scores, spaceIndices, outputTypes); } else { count = dictionary->getBigrams(prevWordCodePoints, prevWordCodePointsLength, inputCodePoints, inputSize, outputCodePoints, scores, outputTypes); diff --git a/native/jni/com_android_inputmethod_latin_DicTraverseSession.cpp b/native/jni/com_android_inputmethod_latin_DicTraverseSession.cpp index dfe3b09d8..08124ffc8 100644 --- a/native/jni/com_android_inputmethod_latin_DicTraverseSession.cpp +++ b/native/jni/com_android_inputmethod_latin_DicTraverseSession.cpp @@ -34,12 +34,14 @@ static void latinime_initDicTraverseSession(JNIEnv *env, jclass clazz, jlong tra void *ts = reinterpret_cast<void *>(traverseSession); Dictionary *dict = reinterpret_cast<Dictionary *>(dictionary); if (!previousWord) { - DicTraverseWrapper::initDicTraverseSession(ts, dict, 0, 0); + DicTraverseWrapper::initDicTraverseSession( + ts, dict, 0 /* prevWord */, 0 /* prevWordLength*/, 0 /* suggestOptions */); return; } int prevWord[previousWordLength]; env->GetIntArrayRegion(previousWord, 0, previousWordLength, prevWord); - DicTraverseWrapper::initDicTraverseSession(ts, dict, prevWord, previousWordLength); + DicTraverseWrapper::initDicTraverseSession( + ts, dict, prevWord, previousWordLength, 0 /* suggestOptions */); } static void latinime_releaseDicTraverseSession(JNIEnv *env, jclass clazz, jlong traverseSession) { diff --git a/native/jni/src/bigram_dictionary.cpp b/native/jni/src/bigram_dictionary.cpp index 9053e7226..ebe27994f 100644 --- a/native/jni/src/bigram_dictionary.cpp +++ b/native/jni/src/bigram_dictionary.cpp @@ -19,11 +19,12 @@ #define LOG_TAG "LatinIME: bigram_dictionary.cpp" #include "bigram_dictionary.h" -#include "binary_format.h" -#include "bloom_filter.h" -#include "char_utils.h" + #include "defines.h" -#include "dictionary.h" +#include "suggest/core/dictionary/binary_format.h" +#include "suggest/core/dictionary/bloom_filter.h" +#include "suggest/core/dictionary/char_utils.h" +#include "suggest/core/dictionary/dictionary.h" namespace latinime { @@ -51,7 +52,7 @@ void BigramDictionary::addWordBigram(int *word, int length, int probability, int int insertAt = 0; while (insertAt < MAX_RESULTS) { if (probability > bigramProbability[insertAt] || (bigramProbability[insertAt] == probability - && length < getCodePointCount(MAX_WORD_LENGTH, + && length < CharUtils::getCodePointCount(MAX_WORD_LENGTH, bigramCodePoints + insertAt * MAX_WORD_LENGTH))) { break; } @@ -195,9 +196,9 @@ bool BigramDictionary::checkFirstCharacter(int *word, int *inputCodePoints) cons // what user typed. int maxAlt = MAX_ALTERNATIVES; - const int firstBaseLowerCodePoint = toBaseLowerCase(*word); + const int firstBaseLowerCodePoint = CharUtils::toBaseLowerCase(*word); while (maxAlt > 0) { - if (toBaseLowerCase(*inputCodePoints) == firstBaseLowerCodePoint) { + if (CharUtils::toBaseLowerCase(*inputCodePoints) == firstBaseLowerCodePoint) { return true; } inputCodePoints++; diff --git a/native/jni/src/char_utils.h b/native/jni/src/char_utils.h deleted file mode 100644 index b429f40b2..000000000 --- a/native/jni/src/char_utils.h +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (C) 2010 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. - */ - -#ifndef LATINIME_CHAR_UTILS_H -#define LATINIME_CHAR_UTILS_H - -#include <cctype> - -#include "defines.h" - -namespace latinime { - -inline static bool isAsciiUpper(int c) { - // Note: isupper(...) reports false positives for some Cyrillic characters, causing them to - // be incorrectly lower-cased using toAsciiLower(...) rather than latin_tolower(...). - return (c >= 'A' && c <= 'Z'); -} - -inline static int toAsciiLower(int c) { - return c - 'A' + 'a'; -} - -inline static bool isAscii(int c) { - return isascii(c) != 0; -} - -unsigned short latin_tolower(const unsigned short c); - -/** - * Table mapping most combined Latin, Greek, and Cyrillic characters - * to their base characters. If c is in range, BASE_CHARS[c] == c - * if c is not a combined character, or the base character if it - * is combined. - */ -static const int BASE_CHARS_SIZE = 0x0500; -extern const unsigned short BASE_CHARS[BASE_CHARS_SIZE]; - -inline static int toBaseCodePoint(int c) { - if (c < BASE_CHARS_SIZE) { - return static_cast<int>(BASE_CHARS[c]); - } - return c; -} - -AK_FORCE_INLINE static int toLowerCase(const int c) { - if (isAsciiUpper(c)) { - return toAsciiLower(c); - } - if (isAscii(c)) { - return c; - } - return static_cast<int>(latin_tolower(static_cast<unsigned short>(c))); -} - -AK_FORCE_INLINE static int toBaseLowerCase(const int c) { - return toLowerCase(toBaseCodePoint(c)); -} - -inline static bool isIntentionalOmissionCodePoint(const int codePoint) { - // TODO: Do not hardcode here - return codePoint == KEYCODE_SINGLE_QUOTE || codePoint == KEYCODE_HYPHEN_MINUS; -} - -inline static int getCodePointCount(const int arraySize, const int *const codePoints) { - int size = 0; - for (; size < arraySize; ++size) { - if (codePoints[size] == '\0') { - break; - } - } - return size; -} - -} // namespace latinime -#endif // LATINIME_CHAR_UTILS_H diff --git a/native/jni/src/correction.cpp b/native/jni/src/correction.cpp index 61bf3f619..3dc2f3748 100644 --- a/native/jni/src/correction.cpp +++ b/native/jni/src/correction.cpp @@ -18,11 +18,11 @@ #include <cmath> -#include "char_utils.h" #include "correction.h" #include "defines.h" -#include "proximity_info_state.h" -#include "suggest_utils.h" +#include "suggest/core/dictionary/char_utils.h" +#include "suggest/core/layout/proximity_info_state.h" +#include "suggest/core/layout/touch_position_correction_utils.h" #include "suggest/policyimpl/utils/edit_distance.h" #include "suggest/policyimpl/utils/damerau_levenshtein_edit_distance_policy.h" @@ -528,7 +528,7 @@ inline static int getQuoteCount(const int *word, const int length) { } inline static bool isUpperCase(unsigned short c) { - return isAsciiUpper(toBaseCodePoint(c)); + return CharUtils::isAsciiUpper(CharUtils::toBaseCodePoint(c)); } ////////////////////// @@ -676,8 +676,8 @@ inline static bool isUpperCase(unsigned short c) { if (i < adjustedProximityMatchedCount) { multiplyIntCapped(typedLetterMultiplier, &finalFreq); } - const float factor = - SuggestUtils::getLengthScalingFactor(static_cast<float>(squaredDistance)); + const float factor = TouchPositionCorrectionUtils::getLengthScalingFactor( + static_cast<float>(squaredDistance)); if (factor > 0.0f) { multiplyRate(static_cast<int>(factor * 100.0f), &finalFreq); } else if (squaredDistance == PROXIMITY_CHAR_WITHOUT_DISTANCE_INFO) { diff --git a/native/jni/src/correction.h b/native/jni/src/correction.h index a9e9b48a6..3f60d48cf 100644 --- a/native/jni/src/correction.h +++ b/native/jni/src/correction.h @@ -21,7 +21,8 @@ #include "correction_state.h" #include "defines.h" -#include "proximity_info_state.h" +#include "suggest/core/dictionary/char_utils.h" +#include "suggest/core/layout/proximity_info_state.h" namespace latinime { @@ -342,13 +343,13 @@ AK_FORCE_INLINE static void calcEditDistanceOneStep(int *editDistanceTable, cons const int *const prevprev = outputLength >= 2 ? editDistanceTable + (outputLength - 2) * (inputSize + 1) : 0; current[0] = outputLength; - const int co = toBaseLowerCase(output[outputLength - 1]); - const int prevCO = outputLength >= 2 ? toBaseLowerCase(output[outputLength - 2]) : 0; + const int co = CharUtils::toBaseLowerCase(output[outputLength - 1]); + const int prevCO = outputLength >= 2 ? CharUtils::toBaseLowerCase(output[outputLength - 2]) : 0; for (int i = 1; i <= inputSize; ++i) { - const int ci = toBaseLowerCase(input[i - 1]); + const int ci = CharUtils::toBaseLowerCase(input[i - 1]); const int cost = (ci == co) ? 0 : 1; current[i] = min(current[i - 1] + 1, min(prev[i] + 1, prev[i - 1] + cost)); - if (i >= 2 && prevprev && ci == prevCO && co == toBaseLowerCase(input[i - 2])) { + if (i >= 2 && prevprev && ci == prevCO && co == CharUtils::toBaseLowerCase(input[i - 2])) { current[i] = min(current[i], prevprev[i - 2] + 1); } } diff --git a/native/jni/src/dic_traverse_wrapper.cpp b/native/jni/src/dic_traverse_wrapper.cpp index 88ca9fa0d..ec8c62dcc 100644 --- a/native/jni/src/dic_traverse_wrapper.cpp +++ b/native/jni/src/dic_traverse_wrapper.cpp @@ -22,5 +22,5 @@ namespace latinime { void *(*DicTraverseWrapper::sDicTraverseSessionFactoryMethod)(JNIEnv *, jstring) = 0; void (*DicTraverseWrapper::sDicTraverseSessionReleaseMethod)(void *) = 0; void (*DicTraverseWrapper::sDicTraverseSessionInitMethod)( - void *, const Dictionary *const, const int *, const int) = 0; + void *, const Dictionary *const, const int *, const int, const SuggestOptions *const) = 0; } // namespace latinime diff --git a/native/jni/src/dic_traverse_wrapper.h b/native/jni/src/dic_traverse_wrapper.h index 1108a45c8..43b4c9ade 100644 --- a/native/jni/src/dic_traverse_wrapper.h +++ b/native/jni/src/dic_traverse_wrapper.h @@ -22,6 +22,7 @@ namespace latinime { class Dictionary; +class SuggestOptions; // TODO: Remove class DicTraverseWrapper { public: @@ -32,9 +33,11 @@ class DicTraverseWrapper { return 0; } static void initDicTraverseSession(void *traverseSession, const Dictionary *const dictionary, - const int *prevWord, const int prevWordLength) { + const int *prevWord, const int prevWordLength, + const SuggestOptions *const suggestOptions) { if (sDicTraverseSessionInitMethod) { - sDicTraverseSessionInitMethod(traverseSession, dictionary, prevWord, prevWordLength); + sDicTraverseSessionInitMethod( + traverseSession, dictionary, prevWord, prevWordLength, suggestOptions); } } static void releaseDicTraverseSession(void *traverseSession) { @@ -46,7 +49,8 @@ class DicTraverseWrapper { sDicTraverseSessionFactoryMethod = factoryMethod; } static void setTraverseSessionInitMethod( - void (*initMethod)(void *, const Dictionary *const, const int *, const int)) { + void (*initMethod)(void *, const Dictionary *const, const int *, const int, + const SuggestOptions *const)) { sDicTraverseSessionInitMethod = initMethod; } static void setTraverseSessionReleaseMethod(void (*releaseMethod)(void *)) { @@ -57,7 +61,7 @@ class DicTraverseWrapper { DISALLOW_IMPLICIT_CONSTRUCTORS(DicTraverseWrapper); static void *(*sDicTraverseSessionFactoryMethod)(JNIEnv *, jstring); static void (*sDicTraverseSessionInitMethod)( - void *, const Dictionary *const, const int *, const int); + void *, const Dictionary *const, const int *, const int, const SuggestOptions *const); static void (*sDicTraverseSessionReleaseMethod)(void *); }; } // namespace latinime diff --git a/native/jni/src/geometry_utils.h b/native/jni/src/geometry_utils.h deleted file mode 100644 index 4cbb127e8..000000000 --- a/native/jni/src/geometry_utils.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * 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. - */ - -#ifndef LATINIME_GEOMETRY_UTILS_H -#define LATINIME_GEOMETRY_UTILS_H - -#include <cmath> - -#include "defines.h" - -#define ROUND_FLOAT_10000(f) ((f) < 1000.0f && (f) > 0.001f) \ - ? (floorf((f) * 10000.0f) / 10000.0f) : (f) - -namespace latinime { - -static inline float SQUARE_FLOAT(const float x) { return x * x; } - -static AK_FORCE_INLINE float getAngle(const int x1, const int y1, const int x2, const int y2) { - const int dx = x1 - x2; - const int dy = y1 - y2; - if (dx == 0 && dy == 0) return 0.0f; - return atan2f(static_cast<float>(dy), static_cast<float>(dx)); -} - -static AK_FORCE_INLINE float getAngleDiff(const float a1, const float a2) { - const float deltaA = fabsf(a1 - a2); - const float diff = ROUND_FLOAT_10000(deltaA); - if (diff > M_PI_F) { - const float normalizedDiff = 2.0f * M_PI_F - diff; - return ROUND_FLOAT_10000(normalizedDiff); - } - return diff; -} - -static AK_FORCE_INLINE int getDistanceInt(const int x1, const int y1, const int x2, - const int y2) { - return static_cast<int>(hypotf(static_cast<float>(x1 - x2), static_cast<float>(y1 - y2))); -} -} // namespace latinime -#endif // LATINIME_GEOMETRY_UTILS_H diff --git a/native/jni/src/suggest/core/dicnode/dic_node.cpp b/native/jni/src/suggest/core/dicnode/dic_node.cpp index 8c48c587b..de088c7d0 100644 --- a/native/jni/src/suggest/core/dicnode/dic_node.cpp +++ b/native/jni/src/suggest/core/dicnode/dic_node.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "dic_node.h" +#include "suggest/core/dicnode/dic_node.h" namespace latinime { diff --git a/native/jni/src/suggest/core/dicnode/dic_node.h b/native/jni/src/suggest/core/dicnode/dic_node.h index 4225bb3e5..14bd2d57a 100644 --- a/native/jni/src/suggest/core/dicnode/dic_node.h +++ b/native/jni/src/suggest/core/dicnode/dic_node.h @@ -17,13 +17,13 @@ #ifndef LATINIME_DIC_NODE_H #define LATINIME_DIC_NODE_H -#include "char_utils.h" #include "defines.h" -#include "dic_node_state.h" -#include "dic_node_profiler.h" -#include "dic_node_properties.h" -#include "dic_node_release_listener.h" -#include "digraph_utils.h" +#include "suggest/core/dicnode/dic_node_state.h" +#include "suggest/core/dicnode/dic_node_profiler.h" +#include "suggest/core/dicnode/dic_node_properties.h" +#include "suggest/core/dicnode/dic_node_release_listener.h" +#include "suggest/core/dictionary/char_utils.h" +#include "suggest/core/dictionary/digraph_utils.h" #if DEBUG_DICT #define LOGI_SHOW_ADD_COST_PROP \ @@ -221,7 +221,7 @@ class DicNode { bool isFirstCharUppercase() const { const int c = getOutputWordBuf()[0]; - return isAsciiUpper(c); + return CharUtils::isAsciiUpper(c); } bool isFirstWord() const { @@ -375,7 +375,7 @@ class DicNode { // Whether the current codepoint can be an intentional omission, in which case the traversal // algorithm will always check for a possible omission here. bool canBeIntentionalOmission() const { - return isIntentionalOmissionCodePoint(getNodeCodePoint()); + return CharUtils::isIntentionalOmissionCodePoint(getNodeCodePoint()); } // Whether the omission is so frequent that it should incur zero cost. diff --git a/native/jni/src/suggest/core/dicnode/dic_node_priority_queue.h b/native/jni/src/suggest/core/dicnode/dic_node_priority_queue.h index d3f28a8bd..970e3bda4 100644 --- a/native/jni/src/suggest/core/dicnode/dic_node_priority_queue.h +++ b/native/jni/src/suggest/core/dicnode/dic_node_priority_queue.h @@ -21,8 +21,8 @@ #include <vector> #include "defines.h" -#include "dic_node.h" -#include "dic_node_release_listener.h" +#include "suggest/core/dicnode/dic_node.h" +#include "suggest/core/dicnode/dic_node_release_listener.h" #define MAX_DIC_NODE_PRIORITY_QUEUE_CAPACITY 200 diff --git a/native/jni/src/suggest/core/dicnode/dic_node_properties.h b/native/jni/src/suggest/core/dicnode/dic_node_properties.h index 63a6b1340..d2f87c10b 100644 --- a/native/jni/src/suggest/core/dicnode/dic_node_properties.h +++ b/native/jni/src/suggest/core/dicnode/dic_node_properties.h @@ -19,8 +19,8 @@ #include <stdint.h> -#include "binary_format.h" #include "defines.h" +#include "suggest/core/dictionary/binary_format.h" namespace latinime { diff --git a/native/jni/src/suggest/core/dicnode/dic_node_state.h b/native/jni/src/suggest/core/dicnode/dic_node_state.h index 239b63c32..d35e7d79f 100644 --- a/native/jni/src/suggest/core/dicnode/dic_node_state.h +++ b/native/jni/src/suggest/core/dicnode/dic_node_state.h @@ -18,10 +18,10 @@ #define LATINIME_DIC_NODE_STATE_H #include "defines.h" -#include "dic_node_state_input.h" -#include "dic_node_state_output.h" -#include "dic_node_state_prevword.h" -#include "dic_node_state_scoring.h" +#include "suggest/core/dicnode/dic_node_state_input.h" +#include "suggest/core/dicnode/dic_node_state_output.h" +#include "suggest/core/dicnode/dic_node_state_prevword.h" +#include "suggest/core/dicnode/dic_node_state_scoring.h" namespace latinime { diff --git a/native/jni/src/suggest/core/dicnode/dic_node_state_prevword.h b/native/jni/src/suggest/core/dicnode/dic_node_state_prevword.h index e3b892bda..c3968c090 100644 --- a/native/jni/src/suggest/core/dicnode/dic_node_state_prevword.h +++ b/native/jni/src/suggest/core/dicnode/dic_node_state_prevword.h @@ -21,7 +21,7 @@ #include <stdint.h> #include "defines.h" -#include "dic_node_utils.h" +#include "suggest/core/dicnode/dic_node_utils.h" namespace latinime { diff --git a/native/jni/src/suggest/core/dicnode/dic_node_state_scoring.h b/native/jni/src/suggest/core/dicnode/dic_node_state_scoring.h index dca9d60da..4c884225a 100644 --- a/native/jni/src/suggest/core/dicnode/dic_node_state_scoring.h +++ b/native/jni/src/suggest/core/dicnode/dic_node_state_scoring.h @@ -20,7 +20,7 @@ #include <stdint.h> #include "defines.h" -#include "digraph_utils.h" +#include "suggest/core/dictionary/digraph_utils.h" namespace latinime { diff --git a/native/jni/src/suggest/core/dicnode/dic_node_utils.cpp b/native/jni/src/suggest/core/dicnode/dic_node_utils.cpp index 5357c3773..c754a5ec2 100644 --- a/native/jni/src/suggest/core/dicnode/dic_node_utils.cpp +++ b/native/jni/src/suggest/core/dicnode/dic_node_utils.cpp @@ -17,13 +17,14 @@ #include <cstring> #include <vector> -#include "binary_format.h" -#include "dic_node.h" -#include "dic_node_utils.h" -#include "dic_node_vector.h" -#include "multi_bigram_map.h" -#include "proximity_info.h" -#include "proximity_info_state.h" +#include "suggest/core/dicnode/dic_node.h" +#include "suggest/core/dicnode/dic_node_utils.h" +#include "suggest/core/dicnode/dic_node_vector.h" +#include "suggest/core/dictionary/binary_format.h" +#include "suggest/core/dictionary/char_utils.h" +#include "suggest/core/dictionary/multi_bigram_map.h" +#include "suggest/core/layout/proximity_info.h" +#include "suggest/core/layout/proximity_info_state.h" namespace latinime { @@ -62,9 +63,9 @@ namespace latinime { DicNodeVector *childDicNodes) { // Passing multiple chars node. No need to traverse child const int codePoint = dicNode->getNodeTypedCodePoint(); - const int baseLowerCaseCodePoint = toBaseLowerCase(codePoint); + const int baseLowerCaseCodePoint = CharUtils::toBaseLowerCase(codePoint); const bool isMatch = isMatchedNodeCodePoint(pInfoState, pointIndex, exactOnly, codePoint); - if (isMatch || isIntentionalOmissionCodePoint(baseLowerCaseCodePoint)) { + if (isMatch || CharUtils::isIntentionalOmissionCodePoint(baseLowerCaseCodePoint)) { childDicNodes->pushPassingChild(dicNode); } } @@ -125,13 +126,13 @@ namespace latinime { return false; } if (pInfo && (pInfo->getKeyIndexOf(nodeCodePoint) == NOT_AN_INDEX - || isIntentionalOmissionCodePoint(nodeCodePoint))) { + || CharUtils::isIntentionalOmissionCodePoint(nodeCodePoint))) { // If normalized nodeCodePoint is not on the keyboard or skippable, this child is never // filtered. return false; } - const int lowerCodePoint = toLowerCase(nodeCodePoint); - const int baseLowerCodePoint = toBaseCodePoint(lowerCodePoint); + const int lowerCodePoint = CharUtils::toLowerCase(nodeCodePoint); + const int baseLowerCodePoint = CharUtils::toBaseCodePoint(lowerCodePoint); // TODO: Avoid linear search for (int i = 0; i < filterSize; ++i) { // Checking if a normalized code point is in filter characters when pInfo is not diff --git a/native/jni/src/suggest/core/dicnode/dic_node_vector.h b/native/jni/src/suggest/core/dicnode/dic_node_vector.h index ca07edaee..e23c411f0 100644 --- a/native/jni/src/suggest/core/dicnode/dic_node_vector.h +++ b/native/jni/src/suggest/core/dicnode/dic_node_vector.h @@ -20,7 +20,7 @@ #include <vector> #include "defines.h" -#include "dic_node.h" +#include "suggest/core/dicnode/dic_node.h" namespace latinime { diff --git a/native/jni/src/suggest/core/dicnode/dic_nodes_cache.cpp b/native/jni/src/suggest/core/dicnode/dic_nodes_cache.cpp index b9a60780b..c3d2a2e74 100644 --- a/native/jni/src/suggest/core/dicnode/dic_nodes_cache.cpp +++ b/native/jni/src/suggest/core/dicnode/dic_nodes_cache.cpp @@ -17,9 +17,9 @@ #include <list> #include "defines.h" -#include "dic_node_priority_queue.h" -#include "dic_node_utils.h" -#include "dic_nodes_cache.h" +#include "suggest/core/dicnode/dic_node_priority_queue.h" +#include "suggest/core/dicnode/dic_node_utils.h" +#include "suggest/core/dicnode/dic_nodes_cache.h" namespace latinime { diff --git a/native/jni/src/suggest/core/dicnode/dic_nodes_cache.h b/native/jni/src/suggest/core/dicnode/dic_nodes_cache.h index a62aa422a..7f5bdbcf6 100644 --- a/native/jni/src/suggest/core/dicnode/dic_nodes_cache.h +++ b/native/jni/src/suggest/core/dicnode/dic_nodes_cache.h @@ -20,7 +20,7 @@ #include <stdint.h> #include "defines.h" -#include "dic_node_priority_queue.h" +#include "suggest/core/dicnode/dic_node_priority_queue.h" #define INITIAL_QUEUE_ID_ACTIVE 0 #define INITIAL_QUEUE_ID_NEXT_ACTIVE 1 diff --git a/native/jni/src/binary_format.h b/native/jni/src/suggest/core/dictionary/binary_format.h index 98241532f..ef9fd3785 100644 --- a/native/jni/src/binary_format.h +++ b/native/jni/src/suggest/core/dictionary/binary_format.h @@ -21,9 +21,9 @@ #include <map> #include <stdint.h> -#include "bloom_filter.h" -#include "char_utils.h" #include "hash_map_compat.h" +#include "suggest/core/dictionary/bloom_filter.h" +#include "suggest/core/dictionary/char_utils.h" namespace latinime { @@ -473,7 +473,8 @@ AK_FORCE_INLINE int BinaryFormat::getTerminalPosition(const uint8_t *const root, // there was no match (or we would have found it). if (wordPos >= length) return NOT_VALID_WORD; int charGroupCount = BinaryFormat::getGroupCountAndForwardPointer(root, &pos); - const int wChar = forceLowerCaseSearch ? toLowerCase(inWord[wordPos]) : inWord[wordPos]; + const int wChar = forceLowerCaseSearch + ? CharUtils::toLowerCase(inWord[wordPos]) : inWord[wordPos]; while (true) { // If there are no more character groups in this node, it means we could not // find a matching character for this depth, therefore there is no match. diff --git a/native/jni/src/bloom_filter.h b/native/jni/src/suggest/core/dictionary/bloom_filter.h index bcce1f7ea..bcce1f7ea 100644 --- a/native/jni/src/bloom_filter.h +++ b/native/jni/src/suggest/core/dictionary/bloom_filter.h diff --git a/native/jni/src/char_utils.cpp b/native/jni/src/suggest/core/dictionary/char_utils.cpp index e219beb62..8d40e54c9 100644 --- a/native/jni/src/char_utils.cpp +++ b/native/jni/src/suggest/core/dictionary/char_utils.cpp @@ -14,9 +14,10 @@ * limitations under the License. */ +#include "suggest/core/dictionary/char_utils.h" + #include <cstdlib> -#include "char_utils.h" #include "defines.h" namespace latinime { @@ -36,8 +37,7 @@ struct LatinCapitalSmallPair { * $ apt-get install libicu-dev * * 3. Build the following code - * (You need this file, char_utils.h, and defines.h) - * $ g++ -o char_utils -DUPDATING_CHAR_UTILS char_utils.cpp -licuuc + * $ g++ -o char_utils -I../../.. -DUPDATING_CHAR_UTILS char_utils.cpp -licuuc */ #ifdef UPDATING_CHAR_UTILS #include <stdio.h> @@ -47,7 +47,7 @@ extern "C" int main() { for (unsigned short c = 0; c < 0xFFFF; c++) { if (c <= 0x7F) continue; const unsigned short icu4cLowerC = u_tolower(c); - const unsigned short myLowerC = latin_tolower(c); + const unsigned short myLowerC = CharUtils::latin_tolower(c); if (c != icu4cLowerC) { #ifdef CONFIRMING_CHAR_UTILS if (icu4cLowerC != myLowerC) { @@ -70,7 +70,8 @@ extern "C" int main() { * * 5. Update the SORTED_CHAR_MAP[] array below with the output above. * Then, rebuild with -DCONFIRMING_CHAR_UTILS and confirm the program exits successfully. - * $ g++ -o char_utils -DUPDATING_CHAR_UTILS -DCONFIRMING_CHAR_UTILS char_utils.cpp -licuuc + * $ g++ -o char_utils -I../../.. -DUPDATING_CHAR_UTILS -DCONFIRMING_CHAR_UTILS char_utils.cpp \ + * -licuuc * $ ./char_utils * $ */ @@ -1054,7 +1055,7 @@ static int compare_pair_capital(const void *a, const void *b) { - static_cast<int>((static_cast<const struct LatinCapitalSmallPair *>(b))->capital); } -unsigned short latin_tolower(const unsigned short c) { +/* static */ unsigned short CharUtils::latin_tolower(const unsigned short c) { struct LatinCapitalSmallPair *p = static_cast<struct LatinCapitalSmallPair *>(bsearch(&c, SORTED_CHAR_MAP, NELEMS(SORTED_CHAR_MAP), sizeof(SORTED_CHAR_MAP[0]), compare_pair_capital)); @@ -1063,7 +1064,7 @@ unsigned short latin_tolower(const unsigned short c) { /* * Table mapping most combined Latin, Greek, and Cyrillic characters - * to their base characters. If c is in range, BASE_CHARS[c] == c + * to their base characters. If c is in range, CharUtils::BASE_CHARS[c] == c * if c is not a combined character, or the base character if it * is combined. * @@ -1074,7 +1075,7 @@ unsigned short latin_tolower(const unsigned short c) { * for ($j = $i; $j < $i + 8; $j++) { \ * printf("0x%04X, ", $base[$j] ? $base[$j] : $j)}; print "\n"; }' */ -const unsigned short BASE_CHARS[BASE_CHARS_SIZE] = { +/* static */ const unsigned short CharUtils::BASE_CHARS[CharUtils::BASE_CHARS_SIZE] = { /* U+0000 */ 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, /* U+0008 */ 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, /* U+0010 */ 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, diff --git a/native/jni/src/suggest/core/dictionary/char_utils.h b/native/jni/src/suggest/core/dictionary/char_utils.h new file mode 100644 index 000000000..2e735a81c --- /dev/null +++ b/native/jni/src/suggest/core/dictionary/char_utils.h @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2010 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. + */ + +#ifndef LATINIME_CHAR_UTILS_H +#define LATINIME_CHAR_UTILS_H + +#include <cctype> + +#include "defines.h" + +namespace latinime { + +class CharUtils { + public: + static AK_FORCE_INLINE bool isAsciiUpper(int c) { + // Note: isupper(...) reports false positives for some Cyrillic characters, causing them to + // be incorrectly lower-cased using toAsciiLower(...) rather than latin_tolower(...). + return (c >= 'A' && c <= 'Z'); + } + + static AK_FORCE_INLINE int toAsciiLower(int c) { + return c - 'A' + 'a'; + } + + static AK_FORCE_INLINE bool isAscii(int c) { + return isascii(c) != 0; + } + + static AK_FORCE_INLINE int toLowerCase(const int c) { + if (isAsciiUpper(c)) { + return toAsciiLower(c); + } + if (isAscii(c)) { + return c; + } + return static_cast<int>(latin_tolower(static_cast<unsigned short>(c))); + } + + static AK_FORCE_INLINE int toBaseLowerCase(const int c) { + return toLowerCase(toBaseCodePoint(c)); + } + + static AK_FORCE_INLINE bool isIntentionalOmissionCodePoint(const int codePoint) { + // TODO: Do not hardcode here + return codePoint == KEYCODE_SINGLE_QUOTE || codePoint == KEYCODE_HYPHEN_MINUS; + } + + static AK_FORCE_INLINE int getCodePointCount(const int arraySize, const int *const codePoints) { + int size = 0; + for (; size < arraySize; ++size) { + if (codePoints[size] == '\0') { + break; + } + } + return size; + } + + static AK_FORCE_INLINE int toBaseCodePoint(int c) { + if (c < BASE_CHARS_SIZE) { + return static_cast<int>(BASE_CHARS[c]); + } + return c; + } + + static unsigned short latin_tolower(const unsigned short c); + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(CharUtils); + + /** + * Table mapping most combined Latin, Greek, and Cyrillic characters + * to their base characters. If c is in range, BASE_CHARS[c] == c + * if c is not a combined character, or the base character if it + * is combined. + */ + static const int BASE_CHARS_SIZE = 0x0500; + static const unsigned short BASE_CHARS[BASE_CHARS_SIZE]; +}; +} // namespace latinime +#endif // LATINIME_CHAR_UTILS_H diff --git a/native/jni/src/dictionary.cpp b/native/jni/src/suggest/core/dictionary/dictionary.cpp index dadb2bab2..1939c7420 100644 --- a/native/jni/src/dictionary.cpp +++ b/native/jni/src/suggest/core/dictionary/dictionary.cpp @@ -16,16 +16,17 @@ #define LOG_TAG "LatinIME: dictionary.cpp" -#include "dictionary.h" +#include "suggest/core/dictionary/dictionary.h" #include <map> // TODO: remove #include <stdint.h> #include "bigram_dictionary.h" -#include "binary_format.h" #include "defines.h" #include "dic_traverse_wrapper.h" +#include "suggest_options.h" #include "suggest/core/suggest.h" +#include "suggest/core/dictionary/binary_format.h" #include "suggest/policyimpl/gesture/gesture_suggest_policy_factory.h" #include "suggest/policyimpl/typing/typing_suggest_policy_factory.h" #include "unigram_dictionary.h" @@ -53,13 +54,13 @@ Dictionary::~Dictionary() { int Dictionary::getSuggestions(ProximityInfo *proximityInfo, void *traverseSession, int *xcoordinates, int *ycoordinates, int *times, int *pointerIds, int *inputCodePoints, - int inputSize, int *prevWordCodePoints, int prevWordLength, int commitPoint, bool isGesture, - bool useFullEditDistance, int *outWords, int *frequencies, int *spaceIndices, - int *outputTypes) const { + int inputSize, int *prevWordCodePoints, int prevWordLength, int commitPoint, + const SuggestOptions *const suggestOptions, int *outWords, int *frequencies, + int *spaceIndices, int *outputTypes) const { int result = 0; - if (isGesture) { + if (suggestOptions->isGesture()) { DicTraverseWrapper::initDicTraverseSession( - traverseSession, this, prevWordCodePoints, prevWordLength); + traverseSession, this, prevWordCodePoints, prevWordLength, suggestOptions); result = mGestureSuggest->getSuggestions(proximityInfo, traverseSession, xcoordinates, ycoordinates, times, pointerIds, inputCodePoints, inputSize, commitPoint, outWords, frequencies, spaceIndices, outputTypes); @@ -70,7 +71,7 @@ int Dictionary::getSuggestions(ProximityInfo *proximityInfo, void *traverseSessi } else { if (USE_SUGGEST_INTERFACE_FOR_TYPING) { DicTraverseWrapper::initDicTraverseSession( - traverseSession, this, prevWordCodePoints, prevWordLength); + traverseSession, this, prevWordCodePoints, prevWordLength, suggestOptions); result = mTypingSuggest->getSuggestions(proximityInfo, traverseSession, xcoordinates, ycoordinates, times, pointerIds, inputCodePoints, inputSize, commitPoint, outWords, frequencies, spaceIndices, outputTypes); @@ -84,8 +85,8 @@ int Dictionary::getSuggestions(ProximityInfo *proximityInfo, void *traverseSessi mBigramDictionary->fillBigramAddressToProbabilityMapAndFilter(prevWordCodePoints, prevWordLength, &bigramMap, bigramFilter); result = mUnigramDictionary->getSuggestions(proximityInfo, xcoordinates, ycoordinates, - inputCodePoints, inputSize, &bigramMap, bigramFilter, useFullEditDistance, - outWords, frequencies, outputTypes); + inputCodePoints, inputSize, &bigramMap, bigramFilter, + suggestOptions->useFullEditDistance(), outWords, frequencies, outputTypes); return result; } } diff --git a/native/jni/src/dictionary.h b/native/jni/src/suggest/core/dictionary/dictionary.h index edec83fbf..e6861a3dd 100644 --- a/native/jni/src/dictionary.h +++ b/native/jni/src/suggest/core/dictionary/dictionary.h @@ -26,6 +26,7 @@ namespace latinime { class BigramDictionary; class ProximityInfo; class SuggestInterface; +class SuggestOptions; class UnigramDictionary; class Dictionary { @@ -54,9 +55,9 @@ class Dictionary { int getSuggestions(ProximityInfo *proximityInfo, void *traverseSession, int *xcoordinates, int *ycoordinates, int *times, int *pointerIds, int *inputCodePoints, int inputSize, - int *prevWordCodePoints, int prevWordLength, int commitPoint, bool isGesture, - bool useFullEditDistance, int *outWords, int *frequencies, int *spaceIndices, - int *outputTypes) const; + int *prevWordCodePoints, int prevWordLength, int commitPoint, + const SuggestOptions *const suggestOptions, int *outWords, int *frequencies, + int *spaceIndices, int *outputTypes) const; int getBigrams(const int *word, int length, int *inputCodePoints, int inputSize, int *outWords, int *frequencies, int *outputTypes) const; diff --git a/native/jni/src/digraph_utils.cpp b/native/jni/src/suggest/core/dictionary/digraph_utils.cpp index 083442669..e8cdd5352 100644 --- a/native/jni/src/digraph_utils.cpp +++ b/native/jni/src/suggest/core/dictionary/digraph_utils.cpp @@ -14,10 +14,11 @@ * limitations under the License. */ -#include "char_utils.h" -#include "binary_format.h" +#include "suggest/core/dictionary/digraph_utils.h" + #include "defines.h" -#include "digraph_utils.h" +#include "suggest/core/dictionary/binary_format.h" +#include "suggest/core/dictionary/char_utils.h" namespace latinime { @@ -121,7 +122,7 @@ const DigraphUtils::DigraphType DigraphUtils::USED_DIGRAPH_TYPES[] = /* static */ const DigraphUtils::digraph_t *DigraphUtils::getDigraphForDigraphTypeAndCodePoint( const DigraphUtils::DigraphType digraphType, const int compositeGlyphCodePoint) { const DigraphUtils::digraph_t *digraphs = 0; - const int compositeGlyphLowerCodePoint = toLowerCase(compositeGlyphCodePoint); + const int compositeGlyphLowerCodePoint = CharUtils::toLowerCase(compositeGlyphCodePoint); const int digraphsSize = DigraphUtils::getAllDigraphsForDictionaryAndReturnSize(digraphType, &digraphs); for (int i = 0; i < digraphsSize; i++) { diff --git a/native/jni/src/digraph_utils.h b/native/jni/src/suggest/core/dictionary/digraph_utils.h index 94435228e..c1205940c 100644 --- a/native/jni/src/digraph_utils.h +++ b/native/jni/src/suggest/core/dictionary/digraph_utils.h @@ -17,6 +17,8 @@ #ifndef DIGRAPH_UTILS_H #define DIGRAPH_UTILS_H +#include "defines.h" + namespace latinime { class DigraphUtils { diff --git a/native/jni/src/multi_bigram_map.h b/native/jni/src/suggest/core/dictionary/multi_bigram_map.h index 7e1b6301f..fcac98f35 100644 --- a/native/jni/src/multi_bigram_map.h +++ b/native/jni/src/suggest/core/dictionary/multi_bigram_map.h @@ -17,12 +17,11 @@ #ifndef LATINIME_MULTI_BIGRAM_MAP_H #define LATINIME_MULTI_BIGRAM_MAP_H -#include <cstring> #include <stdint.h> #include "defines.h" -#include "binary_format.h" #include "hash_map_compat.h" +#include "suggest/core/dictionary/binary_format.h" namespace latinime { diff --git a/native/jni/src/suggest/core/dictionary/shortcut_utils.h b/native/jni/src/suggest/core/dictionary/shortcut_utils.h index c411408ec..601ac5f5a 100644 --- a/native/jni/src/suggest/core/dictionary/shortcut_utils.h +++ b/native/jni/src/suggest/core/dictionary/shortcut_utils.h @@ -19,7 +19,7 @@ #include "defines.h" #include "suggest/core/dicnode/dic_node_utils.h" -#include "terminal_attributes.h" +#include "suggest/core/dictionary/terminal_attributes.h" namespace latinime { diff --git a/native/jni/src/terminal_attributes.h b/native/jni/src/suggest/core/dictionary/terminal_attributes.h index 92ef71c2c..8377c603d 100644 --- a/native/jni/src/terminal_attributes.h +++ b/native/jni/src/suggest/core/dictionary/terminal_attributes.h @@ -18,7 +18,8 @@ #define LATINIME_TERMINAL_ATTRIBUTES_H #include <stdint.h> -#include "binary_format.h" + +#include "suggest/core/dictionary/binary_format.h" namespace latinime { diff --git a/native/jni/src/additional_proximity_chars.cpp b/native/jni/src/suggest/core/layout/additional_proximity_chars.cpp index 661c50e91..34b8b37b0 100644 --- a/native/jni/src/additional_proximity_chars.cpp +++ b/native/jni/src/suggest/core/layout/additional_proximity_chars.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "additional_proximity_chars.h" +#include "suggest/core/layout/additional_proximity_chars.h" namespace latinime { // TODO: Stop using hardcoded additional proximity characters. diff --git a/native/jni/src/additional_proximity_chars.h b/native/jni/src/suggest/core/layout/additional_proximity_chars.h index a88fd6cea..a88fd6cea 100644 --- a/native/jni/src/additional_proximity_chars.h +++ b/native/jni/src/suggest/core/layout/additional_proximity_chars.h diff --git a/native/jni/src/suggest/core/layout/geometry_utils.h b/native/jni/src/suggest/core/layout/geometry_utils.h new file mode 100644 index 000000000..b667df68f --- /dev/null +++ b/native/jni/src/suggest/core/layout/geometry_utils.h @@ -0,0 +1,59 @@ +/* + * 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. + */ + +#ifndef LATINIME_GEOMETRY_UTILS_H +#define LATINIME_GEOMETRY_UTILS_H + +#include <cmath> + +#include "defines.h" + +#define ROUND_FLOAT_10000(f) ((f) < 1000.0f && (f) > 0.001f) \ + ? (floorf((f) * 10000.0f) / 10000.0f) : (f) + +namespace latinime { + +class GeometryUtils { + public: + static inline float SQUARE_FLOAT(const float x) { return x * x; } + + static AK_FORCE_INLINE float getAngle(const int x1, const int y1, const int x2, const int y2) { + const int dx = x1 - x2; + const int dy = y1 - y2; + if (dx == 0 && dy == 0) return 0.0f; + return atan2f(static_cast<float>(dy), static_cast<float>(dx)); + } + + static AK_FORCE_INLINE float getAngleDiff(const float a1, const float a2) { + const float deltaA = fabsf(a1 - a2); + const float diff = ROUND_FLOAT_10000(deltaA); + if (diff > M_PI_F) { + const float normalizedDiff = 2.0f * M_PI_F - diff; + return ROUND_FLOAT_10000(normalizedDiff); + } + return diff; + } + + static AK_FORCE_INLINE int getDistanceInt(const int x1, const int y1, const int x2, + const int y2) { + return static_cast<int>(hypotf(static_cast<float>(x1 - x2), static_cast<float>(y1 - y2))); + } + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(GeometryUtils); +}; +} // namespace latinime +#endif // LATINIME_GEOMETRY_UTILS_H diff --git a/native/jni/src/proximity_info.cpp b/native/jni/src/suggest/core/layout/proximity_info.cpp index 88d670d61..0b5d71a43 100644 --- a/native/jni/src/proximity_info.cpp +++ b/native/jni/src/suggest/core/layout/proximity_info.cpp @@ -14,18 +14,19 @@ * limitations under the License. */ +#define LOG_TAG "LatinIME: proximity_info.cpp" + +#include "suggest/core/layout/proximity_info.h" + #include <cstring> #include <cmath> -#define LOG_TAG "LatinIME: proximity_info.cpp" - -#include "additional_proximity_chars.h" -#include "char_utils.h" #include "defines.h" -#include "geometry_utils.h" #include "jni.h" -#include "proximity_info.h" -#include "proximity_info_params.h" +#include "suggest/core/dictionary/char_utils.h" +#include "suggest/core/layout/additional_proximity_chars.h" +#include "suggest/core/layout/geometry_utils.h" +#include "suggest/core/layout/proximity_info_params.h" namespace latinime { @@ -58,7 +59,7 @@ ProximityInfo::ProximityInfo(JNIEnv *env, const jstring localeJStr, MOST_COMMON_KEY_WIDTH_SQUARE(mostCommonKeyWidth * mostCommonKeyWidth), MOST_COMMON_KEY_HEIGHT(mostCommonKeyHeight), NORMALIZED_SQUARED_MOST_COMMON_KEY_HYPOTENUSE(1.0f + - SQUARE_FLOAT(static_cast<float>(mostCommonKeyHeight) / + GeometryUtils::SQUARE_FLOAT(static_cast<float>(mostCommonKeyHeight) / static_cast<float>(mostCommonKeyWidth))), CELL_WIDTH((keyboardWidth + gridWidth - 1) / gridWidth), CELL_HEIGHT((keyboardHeight + gridHeight - 1) / gridHeight), @@ -150,7 +151,7 @@ float ProximityInfo::getNormalizedSquaredDistanceFromCenterFloatG( const float touchY = static_cast<float>(y); const float keyWidth = static_cast<float>(getMostCommonKeyWidth()); return ProximityInfoUtils::getSquaredDistanceFloat(centerX, centerY, touchX, touchY) - / SQUARE_FLOAT(keyWidth); + / GeometryUtils::SQUARE_FLOAT(keyWidth); } int ProximityInfo::getCodePointOf(const int keyIndex) const { @@ -164,7 +165,7 @@ void ProximityInfo::initializeG() { // TODO: Optimize for (int i = 0; i < KEY_COUNT; ++i) { const int code = mKeyCodePoints[i]; - const int lowerCode = toLowerCase(code); + const int lowerCode = CharUtils::toLowerCase(code); mCenterXsG[i] = mKeyXCoordinates[i] + mKeyWidths[i] / 2; mCenterYsG[i] = mKeyYCoordinates[i] + mKeyHeights[i] / 2; mCodeToKeyMap[lowerCode] = i; @@ -173,7 +174,7 @@ void ProximityInfo::initializeG() { for (int i = 0; i < KEY_COUNT; i++) { mKeyKeyDistancesG[i][i] = 0; for (int j = i + 1; j < KEY_COUNT; j++) { - mKeyKeyDistancesG[i][j] = getDistanceInt( + mKeyKeyDistancesG[i][j] = GeometryUtils::getDistanceInt( mCenterXsG[i], mCenterYsG[i], mCenterXsG[j], mCenterYsG[j]); mKeyKeyDistancesG[j][i] = mKeyKeyDistancesG[i][j]; } diff --git a/native/jni/src/proximity_info.h b/native/jni/src/suggest/core/layout/proximity_info.h index deb9ae0de..6d2ddd4bc 100644 --- a/native/jni/src/proximity_info.h +++ b/native/jni/src/suggest/core/layout/proximity_info.h @@ -20,7 +20,7 @@ #include "defines.h" #include "hash_map_compat.h" #include "jni.h" -#include "proximity_info_utils.h" +#include "suggest/core/layout/proximity_info_utils.h" namespace latinime { diff --git a/native/jni/src/proximity_info_params.cpp b/native/jni/src/suggest/core/layout/proximity_info_params.cpp index 2675d9e70..0e887f700 100644 --- a/native/jni/src/proximity_info_params.cpp +++ b/native/jni/src/suggest/core/layout/proximity_info_params.cpp @@ -15,7 +15,7 @@ */ #include "defines.h" -#include "proximity_info_params.h" +#include "suggest/core/layout/proximity_info_params.h" namespace latinime { const float ProximityInfoParams::NOT_A_DISTANCE_FLOAT = -1.0f; diff --git a/native/jni/src/proximity_info_params.h b/native/jni/src/suggest/core/layout/proximity_info_params.h index 4e47f7308..4e47f7308 100644 --- a/native/jni/src/proximity_info_params.h +++ b/native/jni/src/suggest/core/layout/proximity_info_params.h diff --git a/native/jni/src/proximity_info_state.cpp b/native/jni/src/suggest/core/layout/proximity_info_state.cpp index cc5b736bd..412d5508b 100644 --- a/native/jni/src/proximity_info_state.cpp +++ b/native/jni/src/suggest/core/layout/proximity_info_state.cpp @@ -14,17 +14,19 @@ * limitations under the License. */ +#define LOG_TAG "LatinIME: proximity_info_state.cpp" + +#include "suggest/core/layout/proximity_info_state.h" + #include <cstring> // for memset() and memcpy() #include <sstream> // for debug prints #include <vector> -#define LOG_TAG "LatinIME: proximity_info_state.cpp" - #include "defines.h" -#include "geometry_utils.h" -#include "proximity_info.h" -#include "proximity_info_state.h" -#include "proximity_info_state_utils.h" +#include "suggest/core/dictionary/char_utils.h" +#include "suggest/core/layout/geometry_utils.h" +#include "suggest/core/layout/proximity_info.h" +#include "suggest/core/layout/proximity_info_state_utils.h" namespace latinime { @@ -174,7 +176,7 @@ float ProximityInfoState::getPointToKeyLength( const int index = inputIndex * mProximityInfo->getKeyCount() + keyId; return min(mSampledNormalizedSquaredLengthCache[index], mMaxPointToKeyLength); } - if (isIntentionalOmissionCodePoint(codePoint)) { + if (CharUtils::isIntentionalOmissionCodePoint(codePoint)) { return 0.0f; } // If the char is not a key on the keyboard then return the max length. @@ -202,7 +204,7 @@ ProximityType ProximityInfoState::getProximityType(const int index, const int co const bool checkProximityChars, int *proximityIndex) const { const int *currentCodePoints = getProximityCodePointsAt(index); const int firstCodePoint = currentCodePoints[0]; - const int baseLowerC = toBaseLowerCase(codePoint); + const int baseLowerC = CharUtils::toBaseLowerCase(codePoint); // The first char in the array is what user typed. If it matches right away, that means the // user typed that same char for this pos. @@ -214,7 +216,7 @@ ProximityType ProximityInfoState::getProximityType(const int index, const int co // If the non-accented, lowercased version of that first character matches c, then we have a // non-accented version of the accented character the user typed. Treat it as a close char. - if (toBaseLowerCase(firstCodePoint) == baseLowerC) { + if (CharUtils::toBaseLowerCase(firstCodePoint) == baseLowerC) { return PROXIMITY_CHAR; } @@ -256,8 +258,8 @@ ProximityType ProximityInfoState::getProximityTypeG(const int index, const int c if (!isUsed()) { return UNRELATED_CHAR; } - const int lowerCodePoint = toLowerCase(codePoint); - const int baseLowerCodePoint = toBaseCodePoint(lowerCodePoint); + const int lowerCodePoint = CharUtils::toLowerCase(codePoint); + const int baseLowerCodePoint = CharUtils::toBaseCodePoint(lowerCodePoint); for (int i = 0; i < static_cast<int>(mSampledSearchKeyVectors[index].size()); ++i) { if (mSampledSearchKeyVectors[index][i] == lowerCodePoint || mSampledSearchKeyVectors[index][i] == baseLowerCodePoint) { diff --git a/native/jni/src/proximity_info_state.h b/native/jni/src/suggest/core/layout/proximity_info_state.h index bbe8af240..a971294e3 100644 --- a/native/jni/src/proximity_info_state.h +++ b/native/jni/src/suggest/core/layout/proximity_info_state.h @@ -20,11 +20,10 @@ #include <cstring> // for memset() #include <vector> -#include "char_utils.h" #include "defines.h" #include "hash_map_compat.h" -#include "proximity_info_params.h" -#include "proximity_info_state_utils.h" +#include "suggest/core/layout/proximity_info_params.h" +#include "suggest/core/layout/proximity_info_state_utils.h" namespace latinime { diff --git a/native/jni/src/proximity_info_state_utils.cpp b/native/jni/src/suggest/core/layout/proximity_info_state_utils.cpp index 359673cd8..6f88833a2 100644 --- a/native/jni/src/proximity_info_state_utils.cpp +++ b/native/jni/src/suggest/core/layout/proximity_info_state_utils.cpp @@ -14,16 +14,17 @@ * limitations under the License. */ +#include "suggest/core/layout/proximity_info_state_utils.h" + #include <cmath> #include <cstring> // for memset() #include <sstream> // for debug prints #include <vector> #include "defines.h" -#include "geometry_utils.h" -#include "proximity_info.h" -#include "proximity_info_params.h" -#include "proximity_info_state_utils.h" +#include "suggest/core/layout/geometry_utils.h" +#include "suggest/core/layout/proximity_info.h" +#include "suggest/core/layout/proximity_info_params.h" namespace latinime { @@ -103,12 +104,12 @@ namespace latinime { const int time = times ? times[i] : -1; if (i > 1) { - const float prevAngle = getAngle( + const float prevAngle = GeometryUtils::getAngle( inputXCoordinates[i - 2], inputYCoordinates[i - 2], inputXCoordinates[i - 1], inputYCoordinates[i - 1]); - const float currentAngle = getAngle( + const float currentAngle = GeometryUtils::getAngle( inputXCoordinates[i - 1], inputYCoordinates[i - 1], x, y); - sumAngle += getAngleDiff(prevAngle, currentAngle); + sumAngle += GeometryUtils::getAngleDiff(prevAngle, currentAngle); } if (pushTouchPoint(proximityInfo, maxPointToKeyLength, i, c, x, y, time, @@ -157,7 +158,8 @@ namespace latinime { const float sweetSpotCenterY = proximityInfo->getSweetSpotCenterYAt(keyIndex); const float inputX = static_cast<float>((*sampledInputXs)[inputIndex]); const float inputY = static_cast<float>((*sampledInputYs)[inputIndex]); - return SQUARE_FLOAT(inputX - sweetSpotCenterX) + SQUARE_FLOAT(inputY - sweetSpotCenterY); + return GeometryUtils::SQUARE_FLOAT(inputX - sweetSpotCenterX) + + GeometryUtils::SQUARE_FLOAT(inputY - sweetSpotCenterY); } /* static */ float ProximityInfoStateUtils::calculateNormalizedSquaredDistance( @@ -174,7 +176,8 @@ namespace latinime { } const float squaredDistance = calculateSquaredDistanceFromSweetSpotCenter(proximityInfo, sampledInputXs, sampledInputYs, keyIndex, inputIndex); - const float squaredRadius = SQUARE_FLOAT(proximityInfo->getSweetSpotRadiiAt(keyIndex)); + const float squaredRadius = GeometryUtils::SQUARE_FLOAT( + proximityInfo->getSweetSpotRadiiAt(keyIndex)); return squaredDistance / squaredRadius; } @@ -285,7 +288,7 @@ namespace latinime { if (i < sampledInputSize - 1 && j >= (*sampledInputIndice)[i + 1]) { break; } - length += getDistanceInt(xCoordinates[j], yCoordinates[j], + length += GeometryUtils::getDistanceInt(xCoordinates[j], yCoordinates[j], xCoordinates[j + 1], yCoordinates[j + 1]); duration += times[j + 1] - times[j]; } @@ -296,7 +299,7 @@ namespace latinime { break; } // TODO: use mSampledLengthCache instead? - length += getDistanceInt(xCoordinates[j], yCoordinates[j], + length += GeometryUtils::getDistanceInt(xCoordinates[j], yCoordinates[j], xCoordinates[j + 1], yCoordinates[j + 1]); duration += times[j + 1] - times[j]; } @@ -349,7 +352,7 @@ namespace latinime { const int y1 = (*sampledInputYs)[index0]; const int x2 = (*sampledInputXs)[index1]; const int y2 = (*sampledInputYs)[index1]; - return getAngle(x1, y1, x2, y2); + return GeometryUtils::getAngle(x1, y1, x2, y2); } // Calculating point to key distance for all near keys and returning the distance between @@ -411,9 +414,9 @@ namespace latinime { } const int baseSampleRate = mostCommonKeyWidth; - const int distPrev = getDistanceInt(sampledInputXs->back(), sampledInputYs->back(), - (*sampledInputXs)[size - 2], (*sampledInputYs)[size - 2]) - * ProximityInfoParams::DISTANCE_BASE_SCALE; + const int distPrev = GeometryUtils::getDistanceInt(sampledInputXs->back(), + sampledInputYs->back(), (*sampledInputXs)[size - 2], + (*sampledInputYs)[size - 2]) * ProximityInfoParams::DISTANCE_BASE_SCALE; float score = 0.0f; // Location @@ -425,10 +428,11 @@ namespace latinime { score += ProximityInfoParams::LOCALMIN_DISTANCE_AND_NEAR_TO_KEY_SCORE; } // Angle - const float angle1 = getAngle(x, y, sampledInputXs->back(), sampledInputYs->back()); - const float angle2 = getAngle(sampledInputXs->back(), sampledInputYs->back(), + const float angle1 = GeometryUtils::getAngle(x, y, sampledInputXs->back(), + sampledInputYs->back()); + const float angle2 = GeometryUtils::getAngle(sampledInputXs->back(), sampledInputYs->back(), (*sampledInputXs)[size - 2], (*sampledInputYs)[size - 2]); - const float angleDiff = getAngleDiff(angle1, angle2); + const float angleDiff = GeometryUtils::getAngleDiff(angle1, angle2); // Save corner if (distPrev > baseSampleRate * ProximityInfoParams::CORNER_CHECK_DISTANCE_THRESHOLD_SCALE @@ -472,13 +476,13 @@ namespace latinime { } // Check if the last point should be skipped. if (isLastPoint && size > 0) { - if (getDistanceInt(x, y, sampledInputXs->back(), sampledInputYs->back()) + if (GeometryUtils::getDistanceInt(x, y, sampledInputXs->back(), sampledInputYs->back()) * ProximityInfoParams::LAST_POINT_SKIP_DISTANCE_SCALE < mostCommonKeyWidth) { // This point is not used because it's too close to the previous point. if (DEBUG_GEO_FULL) { AKLOGI("p0: size = %zd, x = %d, y = %d, lx = %d, ly = %d, dist = %d, " "width = %d", size, x, y, sampledInputXs->back(), - sampledInputYs->back(), getDistanceInt( + sampledInputYs->back(), GeometryUtils::getDistanceInt( x, y, sampledInputXs->back(), sampledInputYs->back()), mostCommonKeyWidth / ProximityInfoParams::LAST_POINT_SKIP_DISTANCE_SCALE); @@ -499,7 +503,7 @@ namespace latinime { // Pushing point information. if (size > 0) { sampledLengthCache->push_back( - sampledLengthCache->back() + getDistanceInt( + sampledLengthCache->back() + GeometryUtils::getDistanceInt( x, y, sampledInputXs->back(), sampledInputYs->back())); } else { sampledLengthCache->push_back(0); @@ -540,7 +544,8 @@ namespace latinime { while (start > 0 && tempBeelineDistance < lookupRadius) { tempTime += times[start] - times[start - 1]; --start; - tempBeelineDistance = getDistanceInt(x0, y0, xCoordinates[start], yCoordinates[start]); + tempBeelineDistance = GeometryUtils::getDistanceInt(x0, y0, xCoordinates[start], + yCoordinates[start]); } // Exclusive unless this is an edge point if (start > 0 && start < actualInputIndex) { @@ -553,7 +558,8 @@ namespace latinime { while (end < (inputSize - 1) && tempBeelineDistance < lookupRadius) { tempTime += times[end + 1] - times[end]; ++end; - tempBeelineDistance = getDistanceInt(x0, y0, xCoordinates[end], yCoordinates[end]); + tempBeelineDistance = GeometryUtils::getDistanceInt(x0, y0, xCoordinates[end], + yCoordinates[end]); } // Exclusive unless this is an edge point if (end > actualInputIndex && end < (inputSize - 1)) { @@ -571,7 +577,7 @@ namespace latinime { const int y2 = yCoordinates[start]; const int x3 = xCoordinates[end]; const int y3 = yCoordinates[end]; - const int beelineDistance = getDistanceInt(x2, y2, x3, y3); + const int beelineDistance = GeometryUtils::getDistanceInt(x2, y2, x3, y3); int adjustedStartTime = times[start]; if (start == 0 && actualInputIndex == 0 && inputSize > 1) { adjustedStartTime += ProximityInfoParams::FIRST_POINT_TIME_OFFSET_MILLIS; @@ -613,7 +619,7 @@ namespace latinime { } const float previousDirection = getDirection(sampledInputXs, sampledInputYs, index - 1, index); const float nextDirection = getDirection(sampledInputXs, sampledInputYs, index, index + 1); - const float directionDiff = getAngleDiff(previousDirection, nextDirection); + const float directionDiff = GeometryUtils::getAngleDiff(previousDirection, nextDirection); return directionDiff; } @@ -636,7 +642,7 @@ namespace latinime { } const float previousDirection = getDirection(sampledInputXs, sampledInputYs, index0, index1); const float nextDirection = getDirection(sampledInputXs, sampledInputYs, index1, index2); - return getAngleDiff(previousDirection, nextDirection); + return GeometryUtils::getAngleDiff(previousDirection, nextDirection); } // This function basically converts from a length to an edit distance. Accordingly, it's obviously diff --git a/native/jni/src/proximity_info_state_utils.h b/native/jni/src/suggest/core/layout/proximity_info_state_utils.h index 1837c7ab6..1837c7ab6 100644 --- a/native/jni/src/proximity_info_state_utils.h +++ b/native/jni/src/suggest/core/layout/proximity_info_state_utils.h diff --git a/native/jni/src/proximity_info_utils.h b/native/jni/src/suggest/core/layout/proximity_info_utils.h index 71c97e325..3588f4df8 100644 --- a/native/jni/src/proximity_info_utils.h +++ b/native/jni/src/suggest/core/layout/proximity_info_utils.h @@ -19,11 +19,11 @@ #include <cmath> -#include "additional_proximity_chars.h" -#include "char_utils.h" #include "defines.h" -#include "geometry_utils.h" #include "hash_map_compat.h" +#include "suggest/core/dictionary/char_utils.h" +#include "suggest/core/layout/additional_proximity_chars.h" +#include "suggest/core/layout/geometry_utils.h" namespace latinime { class ProximityInfoUtils { @@ -37,7 +37,7 @@ class ProximityInfoUtils { if (c == NOT_A_CODE_POINT) { return NOT_AN_INDEX; } - const int lowerCode = toLowerCase(c); + const int lowerCode = CharUtils::toLowerCase(c); hash_map_compat<int, int>::const_iterator mapPos = codeToKeyMap->find(lowerCode); if (mapPos != codeToKeyMap->end()) { return mapPos->second; @@ -87,7 +87,7 @@ class ProximityInfoUtils { static inline float getSquaredDistanceFloat(const float x1, const float y1, const float x2, const float y2) { - return SQUARE_FLOAT(x1 - x2) + SQUARE_FLOAT(y1 - y2); + return GeometryUtils::SQUARE_FLOAT(x1 - x2) + GeometryUtils::SQUARE_FLOAT(y1 - y2); } static inline float pointToLineSegSquaredDistanceFloat(const float x, const float y, @@ -98,7 +98,8 @@ class ProximityInfoUtils { const float ray2y = y2 - y1; const float dotProduct = ray1x * ray2x + ray1y * ray2y; - const float lineLengthSqr = SQUARE_FLOAT(ray2x) + SQUARE_FLOAT(ray2y); + const float lineLengthSqr = GeometryUtils::SQUARE_FLOAT(ray2x) + + GeometryUtils::SQUARE_FLOAT(ray2y); const float projectionLengthSqr = dotProduct / lineLengthSqr; float projectionX; @@ -121,12 +122,14 @@ class ProximityInfoUtils { public: NormalDistribution(const float u, const float sigma) : mU(u), mSigma(sigma), - mPreComputedNonExpPart(1.0f / sqrtf(2.0f * M_PI_F * SQUARE_FLOAT(sigma))), - mPreComputedExponentPart(-1.0f / (2.0f * SQUARE_FLOAT(sigma))) {} + mPreComputedNonExpPart(1.0f / sqrtf(2.0f * M_PI_F + * GeometryUtils::SQUARE_FLOAT(sigma))), + mPreComputedExponentPart(-1.0f / (2.0f * GeometryUtils::SQUARE_FLOAT(sigma))) {} float getProbabilityDensity(const float x) const { const float shiftedX = x - mU; - return mPreComputedNonExpPart * expf(mPreComputedExponentPart * SQUARE_FLOAT(shiftedX)); + return mPreComputedNonExpPart + * expf(mPreComputedExponentPart * GeometryUtils::SQUARE_FLOAT(shiftedX)); } private: diff --git a/native/jni/src/suggest_utils.h b/native/jni/src/suggest/core/layout/touch_position_correction_utils.h index e053dd662..429dcae0d 100644 --- a/native/jni/src/suggest_utils.h +++ b/native/jni/src/suggest/core/layout/touch_position_correction_utils.h @@ -14,14 +14,14 @@ * limitations under the License. */ -#ifndef LATINIME_SUGGEST_UTILS_H -#define LATINIME_SUGGEST_UTILS_H +#ifndef LATINIME_TOUCH_POSITION_CORRECTION_UTILS_H +#define LATINIME_TOUCH_POSITION_CORRECTION_UTILS_H #include "defines.h" -#include "proximity_info_params.h" +#include "suggest/core/layout/proximity_info_params.h" namespace latinime { -class SuggestUtils { +class TouchPositionCorrectionUtils { public: // TODO: (OLD) Remove static float getLengthScalingFactor(const float normalizedSquaredDistance) { @@ -82,7 +82,7 @@ class SuggestUtils { } } private: - DISALLOW_IMPLICIT_CONSTRUCTORS(SuggestUtils); + DISALLOW_IMPLICIT_CONSTRUCTORS(TouchPositionCorrectionUtils); }; } // namespace latinime -#endif // LATINIME_SUGGEST_UTILS_H +#endif // LATINIME_TOUCH_POSITION_CORRECTION_UTILS_H diff --git a/native/jni/src/suggest/core/policy/weighting.cpp b/native/jni/src/suggest/core/policy/weighting.cpp index d01531f07..7ad568e25 100644 --- a/native/jni/src/suggest/core/policy/weighting.cpp +++ b/native/jni/src/suggest/core/policy/weighting.cpp @@ -16,7 +16,6 @@ #include "suggest/core/policy/weighting.h" -#include "char_utils.h" #include "defines.h" #include "suggest/core/dicnode/dic_node.h" #include "suggest/core/dicnode/dic_node_profiler.h" diff --git a/native/jni/src/suggest/core/session/dic_traverse_session.cpp b/native/jni/src/suggest/core/session/dic_traverse_session.cpp index 6408f0163..4e634500c 100644 --- a/native/jni/src/suggest/core/session/dic_traverse_session.cpp +++ b/native/jni/src/suggest/core/session/dic_traverse_session.cpp @@ -16,12 +16,12 @@ #include "suggest/core/session/dic_traverse_session.h" -#include "binary_format.h" #include "defines.h" -#include "dictionary.h" #include "dic_traverse_wrapper.h" #include "jni.h" #include "suggest/core/dicnode/dic_node_utils.h" +#include "suggest/core/dictionary/binary_format.h" +#include "suggest/core/dictionary/dictionary.h" namespace latinime { @@ -34,10 +34,11 @@ static void *getSessionInstance(JNIEnv *env, jstring localeStr) { // TODO: Pass "DicTraverseSession *traverseSession" when the source code structure settles down. static void initSessionInstance(void *traverseSession, const Dictionary *const dictionary, - const int *prevWord, const int prevWordLength) { + const int *prevWord, const int prevWordLength, + const SuggestOptions *const suggestOptions) { if (traverseSession) { DicTraverseSession *tSession = static_cast<DicTraverseSession *>(traverseSession); - tSession->init(dictionary, prevWord, prevWordLength); + tSession->init(dictionary, prevWord, prevWordLength, suggestOptions); } } @@ -62,10 +63,11 @@ class TraverseSessionFactoryRegisterer { static TraverseSessionFactoryRegisterer traverseSessionFactoryRegisterer; void DicTraverseSession::init(const Dictionary *const dictionary, const int *prevWord, - int prevWordLength) { + int prevWordLength, const SuggestOptions *const suggestOptions) { mDictionary = dictionary; mMultiWordCostMultiplier = BinaryFormat::getMultiWordCostMultiplier(mDictionary->getDict(), mDictionary->getDictSize()); + mSuggestOptions = suggestOptions; if (!prevWord) { mPrevWordPos = NOT_VALID_WORD; return; diff --git a/native/jni/src/suggest/core/session/dic_traverse_session.h b/native/jni/src/suggest/core/session/dic_traverse_session.h index d88be5b88..e5c7f8e0c 100644 --- a/native/jni/src/suggest/core/session/dic_traverse_session.h +++ b/native/jni/src/suggest/core/session/dic_traverse_session.h @@ -22,20 +22,21 @@ #include "defines.h" #include "jni.h" -#include "multi_bigram_map.h" -#include "proximity_info_state.h" #include "suggest/core/dicnode/dic_nodes_cache.h" +#include "suggest/core/dictionary/multi_bigram_map.h" +#include "suggest/core/layout/proximity_info_state.h" namespace latinime { class Dictionary; class ProximityInfo; +class SuggestOptions; class DicTraverseSession { public: AK_FORCE_INLINE DicTraverseSession(JNIEnv *env, jstring localeStr) : mPrevWordPos(NOT_VALID_WORD), mProximityInfo(0), - mDictionary(0), mDicNodesCache(), mMultiBigramMap(), + mDictionary(0), mSuggestOptions(0), mDicNodesCache(), mMultiBigramMap(), mInputSize(0), mPartiallyCommited(false), mMaxPointerCount(1), mMultiWordCostMultiplier(1.0f) { // NOTE: mProximityInfoStates is an array of instances. @@ -45,7 +46,8 @@ class DicTraverseSession { // Non virtual inline destructor -- never inherit this class AK_FORCE_INLINE ~DicTraverseSession() {} - void init(const Dictionary *dictionary, const int *prevWord, int prevWordLength); + void init(const Dictionary *dictionary, const int *prevWord, int prevWordLength, + const SuggestOptions *const suggestOptions); // TODO: Remove and merge into init void setupForGetSuggestions(const ProximityInfo *pInfo, const int *inputCodePoints, const int inputSize, const int *const inputXs, const int *const inputYs, @@ -61,6 +63,7 @@ class DicTraverseSession { // getters and setters //-------------------- const ProximityInfo *getProximityInfo() const { return mProximityInfo; } + const SuggestOptions *getSuggestOptions() const { return mSuggestOptions; } int getPrevWordPos() const { return mPrevWordPos; } // TODO: REMOVE void setPrevWordPos(int pos) { mPrevWordPos = pos; } @@ -167,6 +170,7 @@ class DicTraverseSession { int mPrevWordPos; const ProximityInfo *mProximityInfo; const Dictionary *mDictionary; + const SuggestOptions *mSuggestOptions; DicNodesCache mDicNodesCache; // Temporary cache for bigram frequencies diff --git a/native/jni/src/suggest/core/suggest.cpp b/native/jni/src/suggest/core/suggest.cpp index a18794850..720222363 100644 --- a/native/jni/src/suggest/core/suggest.cpp +++ b/native/jni/src/suggest/core/suggest.cpp @@ -16,19 +16,18 @@ #include "suggest/core/suggest.h" -#include "char_utils.h" -#include "dictionary.h" -#include "digraph_utils.h" -#include "proximity_info.h" #include "suggest/core/dicnode/dic_node.h" #include "suggest/core/dicnode/dic_node_priority_queue.h" #include "suggest/core/dicnode/dic_node_vector.h" +#include "suggest/core/dictionary/dictionary.h" +#include "suggest/core/dictionary/digraph_utils.h" #include "suggest/core/dictionary/shortcut_utils.h" +#include "suggest/core/dictionary/terminal_attributes.h" +#include "suggest/core/layout/proximity_info.h" #include "suggest/core/policy/scoring.h" #include "suggest/core/policy/traversal.h" #include "suggest/core/policy/weighting.h" #include "suggest/core/session/dic_traverse_session.h" -#include "terminal_attributes.h" namespace latinime { diff --git a/native/jni/src/suggest/policyimpl/typing/typing_traversal.h b/native/jni/src/suggest/policyimpl/typing/typing_traversal.h index 12110d54f..e0664185c 100644 --- a/native/jni/src/suggest/policyimpl/typing/typing_traversal.h +++ b/native/jni/src/suggest/policyimpl/typing/typing_traversal.h @@ -19,11 +19,11 @@ #include <stdint.h> -#include "char_utils.h" #include "defines.h" -#include "proximity_info_state.h" #include "suggest/core/dicnode/dic_node.h" #include "suggest/core/dicnode/dic_node_vector.h" +#include "suggest/core/dictionary/char_utils.h" +#include "suggest/core/layout/proximity_info_state.h" #include "suggest/core/policy/traversal.h" #include "suggest/core/session/dic_traverse_session.h" #include "suggest/policyimpl/typing/scoring_params.h" @@ -64,9 +64,9 @@ class TypingTraversal : public Traversal { } const int point0Index = dicNode->getInputIndex(0); const int currentBaseLowerCodePoint = - toBaseLowerCase(childDicNode->getNodeCodePoint()); + CharUtils::toBaseLowerCase(childDicNode->getNodeCodePoint()); const int typedBaseLowerCodePoint = - toBaseLowerCase(traverseSession->getProximityInfoState(0) + CharUtils::toBaseLowerCase(traverseSession->getProximityInfoState(0) ->getPrimaryCodePointAt(point0Index)); return (currentBaseLowerCodePoint != typedBaseLowerCodePoint); } @@ -172,7 +172,7 @@ class TypingTraversal : public Traversal { } const int c = dicNode->getOutputWordBuf()[0]; const bool shortCappedWord = dicNode->getDepth() - < ScoringParams::THRESHOLD_SHORT_WORD_LENGTH && isAsciiUpper(c); + < ScoringParams::THRESHOLD_SHORT_WORD_LENGTH && CharUtils::isAsciiUpper(c); return !shortCappedWord || probability >= ScoringParams::THRESHOLD_NEXT_WORD_PROBABILITY_FOR_CAPPED; } diff --git a/native/jni/src/suggest/policyimpl/typing/typing_weighting.h b/native/jni/src/suggest/policyimpl/typing/typing_weighting.h index 3938c0ec5..9e0c0d2f7 100644 --- a/native/jni/src/suggest/policyimpl/typing/typing_weighting.h +++ b/native/jni/src/suggest/policyimpl/typing/typing_weighting.h @@ -18,8 +18,9 @@ #define LATINIME_TYPING_WEIGHTING_H #include "defines.h" -#include "suggest_utils.h" #include "suggest/core/dicnode/dic_node_utils.h" +#include "suggest/core/dictionary/char_utils.h" +#include "suggest/core/layout/touch_position_correction_utils.h" #include "suggest/core/policy/weighting.h" #include "suggest/core/session/dic_traverse_session.h" #include "suggest/policyimpl/typing/scoring_params.h" @@ -74,7 +75,7 @@ class TypingWeighting : public Weighting { // the keyboard (like accented letters) const float normalizedSquaredLength = traverseSession->getProximityInfoState(0) ->getPointToKeyLength(pointIndex, dicNode->getNodeCodePoint()); - const float normalizedDistance = SuggestUtils::getSweetSpotFactor( + const float normalizedDistance = TouchPositionCorrectionUtils::getSweetSpotFactor( traverseSession->isTouchPositionCorrectionEnabled(), normalizedSquaredLength); const float weightedDistance = ScoringParams::DISTANCE_WEIGHT_LENGTH * normalizedDistance; @@ -98,9 +99,9 @@ class TypingWeighting : public Weighting { bool isProximityDicNode(const DicTraverseSession *const traverseSession, const DicNode *const dicNode) const { const int pointIndex = dicNode->getInputIndex(0); - const int primaryCodePoint = toBaseLowerCase( + const int primaryCodePoint = CharUtils::toBaseLowerCase( traverseSession->getProximityInfoState(0)->getPrimaryCodePointAt(pointIndex)); - const int dicNodeChar = toBaseLowerCase(dicNode->getNodeCodePoint()); + const int dicNodeChar = CharUtils::toBaseLowerCase(dicNode->getNodeCodePoint()); return primaryCodePoint != dicNodeChar; } diff --git a/native/jni/src/suggest/policyimpl/utils/damerau_levenshtein_edit_distance_policy.h b/native/jni/src/suggest/policyimpl/utils/damerau_levenshtein_edit_distance_policy.h index ec1457455..09f986adf 100644 --- a/native/jni/src/suggest/policyimpl/utils/damerau_levenshtein_edit_distance_policy.h +++ b/native/jni/src/suggest/policyimpl/utils/damerau_levenshtein_edit_distance_policy.h @@ -17,7 +17,7 @@ #ifndef LATINIME_DAEMARU_LEVENSHTEIN_EDIT_DISTANCE_POLICY_H #define LATINIME_DAEMARU_LEVENSHTEIN_EDIT_DISTANCE_POLICY_H -#include "char_utils.h" +#include "suggest/core/dictionary/char_utils.h" #include "suggest/policyimpl/utils/edit_distance_policy.h" namespace latinime { @@ -31,8 +31,8 @@ class DamerauLevenshteinEditDistancePolicy : public EditDistancePolicy { ~DamerauLevenshteinEditDistancePolicy() {} AK_FORCE_INLINE float getSubstitutionCost(const int index0, const int index1) const { - const int c0 = toBaseLowerCase(mString0[index0]); - const int c1 = toBaseLowerCase(mString1[index1]); + const int c0 = CharUtils::toBaseLowerCase(mString0[index0]); + const int c1 = CharUtils::toBaseLowerCase(mString1[index1]); return (c0 == c1) ? 0.0f : 1.0f; } @@ -45,10 +45,10 @@ class DamerauLevenshteinEditDistancePolicy : public EditDistancePolicy { } AK_FORCE_INLINE bool allowTransposition(const int index0, const int index1) const { - const int c0 = toBaseLowerCase(mString0[index0]); - const int c1 = toBaseLowerCase(mString1[index1]); - if (index0 > 0 && index1 > 0 && c0 == toBaseLowerCase(mString1[index1 - 1]) - && c1 == toBaseLowerCase(mString0[index0 - 1])) { + const int c0 = CharUtils::toBaseLowerCase(mString0[index0]); + const int c1 = CharUtils::toBaseLowerCase(mString1[index1]); + if (index0 > 0 && index1 > 0 && c0 == CharUtils::toBaseLowerCase(mString1[index1 - 1]) + && c1 == CharUtils::toBaseLowerCase(mString0[index0 - 1])) { return true; } return false; diff --git a/native/jni/src/suggest_options.h b/native/jni/src/suggest_options.h index 1bed47c93..1b21aafcf 100644 --- a/native/jni/src/suggest_options.h +++ b/native/jni/src/suggest_options.h @@ -23,6 +23,9 @@ namespace latinime { class SuggestOptions{ public: + SuggestOptions(const int *const options, const int length) + : mOptions(options), mLength(length) {} + AK_FORCE_INLINE bool isGesture() const { return getBoolOption(IS_GESTURE); } @@ -31,14 +34,20 @@ class SuggestOptions{ return getBoolOption(USE_FULL_EDIT_DISTANCE); } - SuggestOptions(const int *const options, const int length) - : mOptions(options), mLength(length) {} + AK_FORCE_INLINE bool getAdditionalFeaturesBoolOption(const int key) const { + return getBoolOption(key + ADDITIONAL_FEATURES_OPTIONS); + } private: + DISALLOW_IMPLICIT_CONSTRUCTORS(SuggestOptions); + // Need to update com.android.inputmethod.latin.NativeSuggestOptions when you add, remove or // reorder options. static const int IS_GESTURE = 0; static const int USE_FULL_EDIT_DISTANCE = 1; + // Additional features options are stored after the other options and used as setting values of + // experimental features. + static const int ADDITIONAL_FEATURES_OPTIONS = 2; const int *const mOptions; const int mLength; diff --git a/native/jni/src/unigram_dictionary.cpp b/native/jni/src/unigram_dictionary.cpp index a672294b5..66a8b8542 100644 --- a/native/jni/src/unigram_dictionary.cpp +++ b/native/jni/src/unigram_dictionary.cpp @@ -18,13 +18,13 @@ #define LOG_TAG "LatinIME: unigram_dictionary.cpp" -#include "binary_format.h" -#include "char_utils.h" #include "defines.h" -#include "dictionary.h" -#include "digraph_utils.h" -#include "proximity_info.h" -#include "terminal_attributes.h" +#include "suggest/core/dictionary/binary_format.h" +#include "suggest/core/dictionary/char_utils.h" +#include "suggest/core/dictionary/dictionary.h" +#include "suggest/core/dictionary/digraph_utils.h" +#include "suggest/core/dictionary/terminal_attributes.h" +#include "suggest/core/layout/proximity_info.h" #include "unigram_dictionary.h" #include "words_priority_queue.h" #include "words_priority_queue_pool.h" @@ -696,8 +696,8 @@ static inline bool testCharGroupForContinuedLikeness(const uint8_t flags, const bool hasMultipleChars = (0 != (BinaryFormat::FLAG_HAS_MULTIPLE_CHARS & flags)); int pos = startPos; int codePoint = BinaryFormat::getCodePointAndForwardPointer(root, &pos); - int baseChar = toBaseLowerCase(codePoint); - const int wChar = toBaseLowerCase(inWord[startInputIndex]); + int baseChar = CharUtils::toBaseLowerCase(codePoint); + const int wChar = CharUtils::toBaseLowerCase(inWord[startInputIndex]); if (baseChar != wChar) { *outPos = hasMultipleChars ? BinaryFormat::skipOtherCharacters(root, pos) : pos; @@ -709,8 +709,9 @@ static inline bool testCharGroupForContinuedLikeness(const uint8_t flags, if (hasMultipleChars) { codePoint = BinaryFormat::getCodePointAndForwardPointer(root, &pos); while (NOT_A_CODE_POINT != codePoint) { - baseChar = toBaseLowerCase(codePoint); - if (inputIndex + 1 >= inputSize || toBaseLowerCase(inWord[++inputIndex]) != baseChar) { + baseChar = CharUtils::toBaseLowerCase(codePoint); + if (inputIndex + 1 >= inputSize + || CharUtils::toBaseLowerCase(inWord[++inputIndex]) != baseChar) { *outPos = BinaryFormat::skipOtherCharacters(root, pos); *outInputIndex = startInputIndex; return false; diff --git a/native/jni/src/unigram_dictionary.h b/native/jni/src/unigram_dictionary.h index a64a539bd..a50503256 100644 --- a/native/jni/src/unigram_dictionary.h +++ b/native/jni/src/unigram_dictionary.h @@ -19,8 +19,9 @@ #include <map> #include <stdint.h> + #include "defines.h" -#include "digraph_utils.h" +#include "suggest/core/dictionary/digraph_utils.h" namespace latinime { |