Merge "Classify touches into three types."

main
Yusuke Nojima 2011-09-30 01:26:15 -07:00 committed by Android (Google) Code Review
commit da9f556a15
9 changed files with 181 additions and 62 deletions

View File

@ -31,141 +31,145 @@
android:label="@string/subtype_en_US" android:label="@string/subtype_en_US"
android:imeSubtypeLocale="en_US" android:imeSubtypeLocale="en_US"
android:imeSubtypeMode="keyboard" android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="TrySuppressingImeSwitcher,AsciiCapable" android:imeSubtypeExtraValue="TrySuppressingImeSwitcher,AsciiCapable,SupportTouchPositionCorrection"
/> />
<subtype android:icon="@drawable/ic_subtype_keyboard" <subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_en_GB" android:label="@string/subtype_en_GB"
android:imeSubtypeLocale="en_GB" android:imeSubtypeLocale="en_GB"
android:imeSubtypeMode="keyboard" android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="TrySuppressingImeSwitcher,AsciiCapable" android:imeSubtypeExtraValue="TrySuppressingImeSwitcher,AsciiCapable,SupportTouchPositionCorrection"
/> />
<subtype android:icon="@drawable/ic_subtype_keyboard" <subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic" android:label="@string/subtype_generic"
android:imeSubtypeLocale="ar" android:imeSubtypeLocale="ar"
android:imeSubtypeMode="keyboard" android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="SupportTouchPositionCorrection"
/> />
<subtype android:icon="@drawable/ic_subtype_keyboard" <subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic" android:label="@string/subtype_generic"
android:imeSubtypeLocale="cs" android:imeSubtypeLocale="cs"
android:imeSubtypeMode="keyboard" android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="AsciiCapable" android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection"
/> />
<subtype android:icon="@drawable/ic_subtype_keyboard" <subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic" android:label="@string/subtype_generic"
android:imeSubtypeLocale="da" android:imeSubtypeLocale="da"
android:imeSubtypeMode="keyboard" android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="AsciiCapable" android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection"
/> />
<subtype android:icon="@drawable/ic_subtype_keyboard" <subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic" android:label="@string/subtype_generic"
android:imeSubtypeLocale="de" android:imeSubtypeLocale="de"
android:imeSubtypeMode="keyboard" android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="AsciiCapable" android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection"
/> />
<subtype android:icon="@drawable/ic_subtype_keyboard" <subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_de_qwerty" android:label="@string/subtype_de_qwerty"
android:imeSubtypeLocale="de" android:imeSubtypeLocale="de"
android:imeSubtypeMode="keyboard" android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="AsciiCapable,KeyboardLocale=de_ZZ" android:imeSubtypeExtraValue="AsciiCapable,KeyboardLocale=de_ZZ,SupportTouchPositionCorrection"
/> />
<subtype android:icon="@drawable/ic_subtype_keyboard" <subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic" android:label="@string/subtype_generic"
android:imeSubtypeLocale="es" android:imeSubtypeLocale="es"
android:imeSubtypeMode="keyboard" android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="AsciiCapable" android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection"
/> />
<subtype android:icon="@drawable/ic_subtype_keyboard" <subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic" android:label="@string/subtype_generic"
android:imeSubtypeLocale="fi" android:imeSubtypeLocale="fi"
android:imeSubtypeMode="keyboard" android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="AsciiCapable" android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection"
/> />
<subtype android:icon="@drawable/ic_subtype_keyboard" <subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic" android:label="@string/subtype_generic"
android:imeSubtypeLocale="fr" android:imeSubtypeLocale="fr"
android:imeSubtypeMode="keyboard" android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="AsciiCapable" android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection"
/> />
<subtype android:icon="@drawable/ic_subtype_keyboard" <subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic" android:label="@string/subtype_generic"
android:imeSubtypeLocale="fr_CA" android:imeSubtypeLocale="fr_CA"
android:imeSubtypeMode="keyboard" android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="AsciiCapable" android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection"
/> />
<subtype android:icon="@drawable/ic_subtype_keyboard" <subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic" android:label="@string/subtype_generic"
android:imeSubtypeLocale="fr_CH" android:imeSubtypeLocale="fr_CH"
android:imeSubtypeMode="keyboard" android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="AsciiCapable" android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection"
/> />
<subtype android:icon="@drawable/ic_subtype_keyboard" <subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic" android:label="@string/subtype_generic"
android:imeSubtypeLocale="hr" android:imeSubtypeLocale="hr"
android:imeSubtypeMode="keyboard" android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="AsciiCapable" android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection"
/> />
<subtype android:icon="@drawable/ic_subtype_keyboard" <subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic" android:label="@string/subtype_generic"
android:imeSubtypeLocale="hu" android:imeSubtypeLocale="hu"
android:imeSubtypeMode="keyboard" android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="AsciiCapable" android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection"
/> />
<subtype android:icon="@drawable/ic_subtype_keyboard" <subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic" android:label="@string/subtype_generic"
android:imeSubtypeLocale="it" android:imeSubtypeLocale="it"
android:imeSubtypeMode="keyboard" android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="AsciiCapable" android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection"
/> />
<!-- Java uses the deprecated "iw" code instead of the standard "he" code for Hebrew. --> <!-- Java uses the deprecated "iw" code instead of the standard "he" code for Hebrew. -->
<subtype android:icon="@drawable/ic_subtype_keyboard" <subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic" android:label="@string/subtype_generic"
android:imeSubtypeLocale="iw" android:imeSubtypeLocale="iw"
android:imeSubtypeMode="keyboard" android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="SupportTouchPositionCorrection"
/> />
<subtype android:icon="@drawable/ic_subtype_keyboard" <subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic" android:label="@string/subtype_generic"
android:imeSubtypeLocale="nb" android:imeSubtypeLocale="nb"
android:imeSubtypeMode="keyboard" android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="AsciiCapable" android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection"
/> />
<subtype android:icon="@drawable/ic_subtype_keyboard" <subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic" android:label="@string/subtype_generic"
android:imeSubtypeLocale="nl" android:imeSubtypeLocale="nl"
android:imeSubtypeMode="keyboard" android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="AsciiCapable" android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection"
/> />
<subtype android:icon="@drawable/ic_subtype_keyboard" <subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic" android:label="@string/subtype_generic"
android:imeSubtypeLocale="pl" android:imeSubtypeLocale="pl"
android:imeSubtypeMode="keyboard" android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="AsciiCapable" android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection"
/> />
<subtype android:icon="@drawable/ic_subtype_keyboard" <subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic" android:label="@string/subtype_generic"
android:imeSubtypeLocale="pt" android:imeSubtypeLocale="pt"
android:imeSubtypeMode="keyboard" android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="AsciiCapable" android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection"
/> />
<subtype android:icon="@drawable/ic_subtype_keyboard" <subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic" android:label="@string/subtype_generic"
android:imeSubtypeLocale="ru" android:imeSubtypeLocale="ru"
android:imeSubtypeMode="keyboard" android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="SupportTouchPositionCorrection"
/> />
<subtype android:icon="@drawable/ic_subtype_keyboard" <subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic" android:label="@string/subtype_generic"
android:imeSubtypeLocale="sr" android:imeSubtypeLocale="sr"
android:imeSubtypeMode="keyboard" android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="SupportTouchPositionCorrection"
/> />
<subtype android:icon="@drawable/ic_subtype_keyboard" <subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic" android:label="@string/subtype_generic"
android:imeSubtypeLocale="sv" android:imeSubtypeLocale="sv"
android:imeSubtypeMode="keyboard" android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="AsciiCapable" android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection"
/> />
<subtype android:icon="@drawable/ic_subtype_keyboard" <subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic" android:label="@string/subtype_generic"
android:imeSubtypeLocale="tr" android:imeSubtypeLocale="tr"
android:imeSubtypeMode="keyboard" android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="AsciiCapable" android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection"
/> />
</input-method> </input-method>

View File

@ -19,6 +19,7 @@ package com.android.inputmethod.keyboard;
import android.graphics.Rect; import android.graphics.Rect;
import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.keyboard.Key;
import com.android.inputmethod.latin.SubtypeSwitcher;
import com.android.inputmethod.latin.Utils; import com.android.inputmethod.latin.Utils;
import com.android.inputmethod.latin.spellcheck.SpellCheckerProximityInfo; import com.android.inputmethod.latin.spellcheck.SpellCheckerProximityInfo;
@ -31,6 +32,8 @@ public class ProximityInfo {
/** Number of key widths from current touch point to search for nearest keys. */ /** Number of key widths from current touch point to search for nearest keys. */
private static float SEARCH_DISTANCE = 1.2f; private static float SEARCH_DISTANCE = 1.2f;
private static final int[] EMPTY_INT_ARRAY = new int[0]; private static final int[] EMPTY_INT_ARRAY = new int[0];
private static final String SUPPORT_TOUCH_POSITION_CORRECTION =
"SupportTouchPositionCorrection";
private final int mKeyHeight; private final int mKeyHeight;
private final int mGridWidth; private final int mGridWidth;
@ -120,8 +123,10 @@ public class ProximityInfo {
keyCharCodes[i] = key.mCode; keyCharCodes[i] = key.mCode;
} }
final SubtypeSwitcher switcher = SubtypeSwitcher.getInstance();
final boolean hasTouchPositionCorrectionData = final boolean hasTouchPositionCorrectionData =
mTouchPositionCorrectionXs != null switcher.currentSubtypeContainsExtraValueKey(SUPPORT_TOUCH_POSITION_CORRECTION)
&& mTouchPositionCorrectionXs != null
&& mTouchPositionCorrectionYs != null && mTouchPositionCorrectionYs != null
&& mTouchPositionCorrectionRadii != null && mTouchPositionCorrectionRadii != null
&& mTouchPositionCorrectionXs.length > 0 && mTouchPositionCorrectionXs.length > 0

View File

@ -115,6 +115,9 @@ bool Correction::initProcessState(const int outputIndex) {
mInputIndex = mCorrectionStates[outputIndex].mInputIndex; mInputIndex = mCorrectionStates[outputIndex].mInputIndex;
mNeedsToTraverseAllNodes = mCorrectionStates[outputIndex].mNeedsToTraverseAllNodes; mNeedsToTraverseAllNodes = mCorrectionStates[outputIndex].mNeedsToTraverseAllNodes;
mEquivalentCharStrongCount = mCorrectionStates[outputIndex].mEquivalentCharStrongCount;
mEquivalentCharNormalCount = mCorrectionStates[outputIndex].mEquivalentCharNormalCount;
mEquivalentCharWeakCount = mCorrectionStates[outputIndex].mEquivalentCharWeakCount;
mProximityCount = mCorrectionStates[outputIndex].mProximityCount; mProximityCount = mCorrectionStates[outputIndex].mProximityCount;
mTransposedCount = mCorrectionStates[outputIndex].mTransposedCount; mTransposedCount = mCorrectionStates[outputIndex].mTransposedCount;
mExcessiveCount = mCorrectionStates[outputIndex].mExcessiveCount; mExcessiveCount = mCorrectionStates[outputIndex].mExcessiveCount;
@ -169,6 +172,9 @@ void Correction::incrementOutputIndex() {
mCorrectionStates[mOutputIndex].mInputIndex = mInputIndex; mCorrectionStates[mOutputIndex].mInputIndex = mInputIndex;
mCorrectionStates[mOutputIndex].mNeedsToTraverseAllNodes = mNeedsToTraverseAllNodes; mCorrectionStates[mOutputIndex].mNeedsToTraverseAllNodes = mNeedsToTraverseAllNodes;
mCorrectionStates[mOutputIndex].mEquivalentCharStrongCount = mEquivalentCharStrongCount;
mCorrectionStates[mOutputIndex].mEquivalentCharNormalCount = mEquivalentCharNormalCount;
mCorrectionStates[mOutputIndex].mEquivalentCharWeakCount = mEquivalentCharWeakCount;
mCorrectionStates[mOutputIndex].mProximityCount = mProximityCount; mCorrectionStates[mOutputIndex].mProximityCount = mProximityCount;
mCorrectionStates[mOutputIndex].mTransposedCount = mTransposedCount; mCorrectionStates[mOutputIndex].mTransposedCount = mTransposedCount;
mCorrectionStates[mOutputIndex].mExcessiveCount = mExcessiveCount; mCorrectionStates[mOutputIndex].mExcessiveCount = mExcessiveCount;
@ -210,6 +216,12 @@ Correction::CorrectionType Correction::processSkipChar(
} }
} }
inline bool isEquivalentChar(ProximityInfo::ProximityType type) {
// 'type ProximityInfo::EQUIVALENT_CHAR_WEAK' means that
// type == ..._WEAK or type == ..._NORMAL or type == ..._STRONG.
return type <= ProximityInfo::EQUIVALENT_CHAR_WEAK;
}
Correction::CorrectionType Correction::processCharAndCalcState( Correction::CorrectionType Correction::processCharAndCalcState(
const int32_t c, const bool isTerminal) { const int32_t c, const bool isTerminal) {
const int correctionCount = (mSkippedCount + mExcessiveCount + mTransposedCount); const int correctionCount = (mSkippedCount + mExcessiveCount + mTransposedCount);
@ -221,8 +233,9 @@ Correction::CorrectionType Correction::processCharAndCalcState(
bool incremented = false; bool incremented = false;
if (mLastCharExceeded && mInputIndex == mInputLength - 1) { if (mLastCharExceeded && mInputIndex == mInputLength - 1) {
// TODO: Do not check the proximity if EditDistance exceeds the threshold // TODO: Do not check the proximity if EditDistance exceeds the threshold
const int matchId = mProximityInfo->getMatchedProximityId(mInputIndex, c, true); const ProximityInfo::ProximityType matchId =
if (matchId == ProximityInfo::SAME_OR_ACCENTED_OR_CAPITALIZED_CHAR) { mProximityInfo->getMatchedProximityId(mInputIndex, c, true);
if (isEquivalentChar(matchId)) {
mLastCharExceeded = false; mLastCharExceeded = false;
--mExcessiveCount; --mExcessiveCount;
} else if (matchId == ProximityInfo::NEAR_PROXIMITY_CHAR) { } else if (matchId == ProximityInfo::NEAR_PROXIMITY_CHAR) {
@ -266,8 +279,7 @@ Correction::CorrectionType Correction::processCharAndCalcState(
bool secondTransposing = false; bool secondTransposing = false;
if (mTransposedCount % 2 == 1) { if (mTransposedCount % 2 == 1) {
if (mProximityInfo->getMatchedProximityId(mInputIndex - 1, c, false) if (isEquivalentChar(mProximityInfo->getMatchedProximityId(mInputIndex - 1, c, false))) {
== ProximityInfo::SAME_OR_ACCENTED_OR_CAPITALIZED_CHAR) {
++mTransposedCount; ++mTransposedCount;
secondTransposing = true; secondTransposing = true;
} else if (mCorrectionStates[mOutputIndex].mExceeding) { } else if (mCorrectionStates[mOutputIndex].mExceeding) {
@ -288,8 +300,8 @@ Correction::CorrectionType Correction::processCharAndCalcState(
// TODO: Change the limit if we'll allow two or more proximity chars with corrections // TODO: Change the limit if we'll allow two or more proximity chars with corrections
const bool checkProximityChars = noCorrectionsHappenedSoFar || mProximityCount == 0; const bool checkProximityChars = noCorrectionsHappenedSoFar || mProximityCount == 0;
const int matchedProximityCharId = secondTransposing const ProximityInfo::ProximityType matchedProximityCharId = secondTransposing
? ProximityInfo::SAME_OR_ACCENTED_OR_CAPITALIZED_CHAR ? ProximityInfo::EQUIVALENT_CHAR_NORMAL
: mProximityInfo->getMatchedProximityId(mInputIndex, c, checkProximityChars); : mProximityInfo->getMatchedProximityId(mInputIndex, c, checkProximityChars);
if (ProximityInfo::UNRELATED_CHAR == matchedProximityCharId) { if (ProximityInfo::UNRELATED_CHAR == matchedProximityCharId) {
@ -299,19 +311,18 @@ Correction::CorrectionType Correction::processCharAndCalcState(
// here refers to the previous state. // here refers to the previous state.
if (canTryCorrection && mCorrectionStates[mOutputIndex].mProximityMatching if (canTryCorrection && mCorrectionStates[mOutputIndex].mProximityMatching
&& mCorrectionStates[mOutputIndex].mExceeding && mCorrectionStates[mOutputIndex].mExceeding
&& mProximityInfo->getMatchedProximityId(mInputIndex, mWord[mOutputIndex], false) && isEquivalentChar(mProximityInfo->getMatchedProximityId(
== ProximityInfo::SAME_OR_ACCENTED_OR_CAPITALIZED_CHAR) { mInputIndex, mWord[mOutputIndex], false))) {
// Conversion p->e // Conversion p->e
++mExcessiveCount; ++mExcessiveCount;
--mProximityCount; --mProximityCount;
} else if (mInputIndex < mInputLength - 1 && mOutputIndex > 0 && mTransposedCount > 0 } else if (mInputIndex < mInputLength - 1 && mOutputIndex > 0 && mTransposedCount > 0
&& !mCorrectionStates[mOutputIndex].mTransposing && !mCorrectionStates[mOutputIndex].mTransposing
&& mCorrectionStates[mOutputIndex - 1].mTransposing && mCorrectionStates[mOutputIndex - 1].mTransposing
&& mProximityInfo->getMatchedProximityId( && isEquivalentChar(mProximityInfo->getMatchedProximityId(
mInputIndex, mWord[mOutputIndex - 1], false) mInputIndex, mWord[mOutputIndex - 1], false))
== ProximityInfo::SAME_OR_ACCENTED_OR_CAPITALIZED_CHAR && isEquivalentChar(
&& mProximityInfo->getMatchedProximityId(mInputIndex + 1, c, false) mProximityInfo->getMatchedProximityId(mInputIndex + 1, c, false))) {
== ProximityInfo::SAME_OR_ACCENTED_OR_CAPITALIZED_CHAR) {
// Conversion t->e // Conversion t->e
// Example: // Example:
// occaisional -> occa sional // occaisional -> occa sional
@ -322,8 +333,8 @@ Correction::CorrectionType Correction::processCharAndCalcState(
} else if (mOutputIndex > 0 && mInputIndex > 0 && mTransposedCount > 0 } else if (mOutputIndex > 0 && mInputIndex > 0 && mTransposedCount > 0
&& !mCorrectionStates[mOutputIndex].mTransposing && !mCorrectionStates[mOutputIndex].mTransposing
&& mCorrectionStates[mOutputIndex - 1].mTransposing && mCorrectionStates[mOutputIndex - 1].mTransposing
&& mProximityInfo->getMatchedProximityId(mInputIndex - 1, c, false) && isEquivalentChar(
== ProximityInfo::SAME_OR_ACCENTED_OR_CAPITALIZED_CHAR) { mProximityInfo->getMatchedProximityId(mInputIndex - 1, c, false))) {
// Conversion t->s // Conversion t->s
// Example: // Example:
// chcolate -> chocolate // chcolate -> chocolate
@ -334,8 +345,8 @@ Correction::CorrectionType Correction::processCharAndCalcState(
} else if (canTryCorrection && mInputIndex > 0 } else if (canTryCorrection && mInputIndex > 0
&& mCorrectionStates[mOutputIndex].mProximityMatching && mCorrectionStates[mOutputIndex].mProximityMatching
&& mCorrectionStates[mOutputIndex].mSkipping && mCorrectionStates[mOutputIndex].mSkipping
&& mProximityInfo->getMatchedProximityId(mInputIndex - 1, c, false) && isEquivalentChar(
== ProximityInfo::SAME_OR_ACCENTED_OR_CAPITALIZED_CHAR) { mProximityInfo->getMatchedProximityId(mInputIndex - 1, c, false))) {
// Conversion p->s // Conversion p->s
// Note: This logic tries saving cases like contrst --> contrast -- "a" is one of // Note: This logic tries saving cases like contrst --> contrast -- "a" is one of
// proximity chars of "s", but it should rather be handled as a skipped char. // proximity chars of "s", but it should rather be handled as a skipped char.
@ -343,8 +354,8 @@ Correction::CorrectionType Correction::processCharAndCalcState(
--mProximityCount; --mProximityCount;
return processSkipChar(c, isTerminal, false); return processSkipChar(c, isTerminal, false);
} else if ((mExceeding || mTransposing) && mInputIndex - 1 < mInputLength } else if ((mExceeding || mTransposing) && mInputIndex - 1 < mInputLength
&& mProximityInfo->getMatchedProximityId(mInputIndex + 1, c, false) && isEquivalentChar(
== ProximityInfo::SAME_OR_ACCENTED_OR_CAPITALIZED_CHAR) { mProximityInfo->getMatchedProximityId(mInputIndex + 1, c, false))) {
// 1.2. Excessive or transpose correction // 1.2. Excessive or transpose correction
if (mTransposing) { if (mTransposing) {
++mTransposedCount; ++mTransposedCount;
@ -364,14 +375,28 @@ Correction::CorrectionType Correction::processCharAndCalcState(
} }
return UNRELATED; return UNRELATED;
} }
} else if (secondTransposing } else if (secondTransposing) {
|| ProximityInfo::SAME_OR_ACCENTED_OR_CAPITALIZED_CHAR == matchedProximityCharId) {
// If inputIndex is greater than mInputLength, that means there is no // If inputIndex is greater than mInputLength, that means there is no
// proximity chars. So, we don't need to check proximity. // proximity chars. So, we don't need to check proximity.
mMatching = true; mMatching = true;
} else if (isEquivalentChar(matchedProximityCharId)) {
mMatching = true;
switch (matchedProximityCharId) {
case ProximityInfo::EQUIVALENT_CHAR_STRONG:
++mEquivalentCharStrongCount;
break;
case ProximityInfo::EQUIVALENT_CHAR_NORMAL:
++mEquivalentCharNormalCount;
break;
case ProximityInfo::EQUIVALENT_CHAR_WEAK:
++mEquivalentCharWeakCount;
break;
default:
assert(false);
}
} else if (ProximityInfo::NEAR_PROXIMITY_CHAR == matchedProximityCharId) { } else if (ProximityInfo::NEAR_PROXIMITY_CHAR == matchedProximityCharId) {
mProximityMatching = true; mProximityMatching = true;
incrementProximityCount(); ++mProximityCount;
} }
mWord[mOutputIndex] = c; mWord[mOutputIndex] = c;

View File

@ -102,11 +102,6 @@ private:
inline CorrectionType processSkipChar( inline CorrectionType processSkipChar(
const int32_t c, const bool isTerminal, const bool inputIndexIncremented); const int32_t c, const bool isTerminal, const bool inputIndexIncremented);
// TODO: remove
inline void incrementProximityCount() {
++mProximityCount;
}
const int TYPED_LETTER_MULTIPLIER; const int TYPED_LETTER_MULTIPLIER;
const int FULL_WORD_MULTIPLIER; const int FULL_WORD_MULTIPLIER;
const ProximityInfo *mProximityInfo; const ProximityInfo *mProximityInfo;
@ -131,6 +126,9 @@ private:
int mOutputIndex; int mOutputIndex;
int mInputIndex; int mInputIndex;
int mEquivalentCharStrongCount;
int mEquivalentCharNormalCount;
int mEquivalentCharWeakCount;
int mProximityCount; int mProximityCount;
int mExcessiveCount; int mExcessiveCount;
int mTransposedCount; int mTransposedCount;

View File

@ -29,6 +29,9 @@ struct CorrectionState {
uint16_t mChildCount; uint16_t mChildCount;
uint8_t mInputIndex; uint8_t mInputIndex;
uint8_t mEquivalentCharStrongCount;
uint8_t mEquivalentCharNormalCount;
uint8_t mEquivalentCharWeakCount;
uint8_t mProximityCount; uint8_t mProximityCount;
uint8_t mTransposedCount; uint8_t mTransposedCount;
uint8_t mExcessiveCount; uint8_t mExcessiveCount;
@ -63,7 +66,9 @@ inline static void initCorrectionState(CorrectionState *state, const int rootPos
state->mExcessivePos = -1; state->mExcessivePos = -1;
state->mSkipPos = -1; state->mSkipPos = -1;
state->mEquivalentCharStrongCount = 0;
state->mEquivalentCharNormalCount = 0;
state->mEquivalentCharWeakCount = 0;
state->mProximityCount = 0; state->mProximityCount = 0;
state->mTransposedCount = 0; state->mTransposedCount = 0;
state->mExcessiveCount = 0; state->mExcessiveCount = 0;

View File

@ -165,6 +165,8 @@ static void dumpWord(const unsigned short* word, const int length) {
#define KEYCODE_SPACE ' ' #define KEYCODE_SPACE ' '
#define CALIBRATE_SCORE_BY_TOUCH_COORDINATES false
#define SUGGEST_WORDS_WITH_MISSING_CHARACTER true #define SUGGEST_WORDS_WITH_MISSING_CHARACTER true
#define SUGGEST_WORDS_WITH_MISSING_SPACE_CHARACTER true #define SUGGEST_WORDS_WITH_MISSING_SPACE_CHARACTER true
#define SUGGEST_WORDS_WITH_EXCESSIVE_CHARACTER true #define SUGGEST_WORDS_WITH_EXCESSIVE_CHARACTER true
@ -204,4 +206,7 @@ static void dumpWord(const unsigned short* word, const int length) {
#define min(a,b) ((a)<(b)?(a):(b)) #define min(a,b) ((a)<(b)?(a):(b))
#define max(a,b) ((a)>(b)?(a):(b)) #define max(a,b) ((a)>(b)?(a):(b))
// The ratio of neutral area radius to sweet spot radius.
#define NEUTRAL_AREA_RADIUS_RATIO 1.3f
#endif // LATINIME_DEFINES_H #endif // LATINIME_DEFINES_H

View File

@ -43,7 +43,8 @@ ProximityInfo::ProximityInfo(const int maxProximityCharsSize, const int keyboard
KEYBOARD_HEIGHT(keyboardHeight), GRID_WIDTH(gridWidth), GRID_HEIGHT(gridHeight), KEYBOARD_HEIGHT(keyboardHeight), GRID_WIDTH(gridWidth), GRID_HEIGHT(gridHeight),
CELL_WIDTH((keyboardWidth + gridWidth - 1) / gridWidth), CELL_WIDTH((keyboardWidth + gridWidth - 1) / gridWidth),
CELL_HEIGHT((keyboardHeight + gridHeight - 1) / gridHeight), CELL_HEIGHT((keyboardHeight + gridHeight - 1) / gridHeight),
KEY_COUNT(min(keyCount, MAX_KEY_COUNT_IN_A_KEYBOARD)) { KEY_COUNT(min(keyCount, MAX_KEY_COUNT_IN_A_KEYBOARD)),
mInputXCoordinates(NULL), mInputYCoordinates(NULL) {
const int len = GRID_WIDTH * GRID_HEIGHT * MAX_PROXIMITY_CHARS_SIZE; const int len = GRID_WIDTH * GRID_HEIGHT * MAX_PROXIMITY_CHARS_SIZE;
mProximityCharsArray = new uint32_t[len]; mProximityCharsArray = new uint32_t[len];
if (DEBUG_PROXIMITY_INFO) { if (DEBUG_PROXIMITY_INFO) {
@ -103,8 +104,11 @@ bool ProximityInfo::hasSpaceProximity(const int x, const int y) const {
} }
// TODO: Calculate nearby codes here. // TODO: Calculate nearby codes here.
void ProximityInfo::setInputParams(const int* inputCodes, const int inputLength) { void ProximityInfo::setInputParams(const int* inputCodes, const int inputLength,
const int* xCoordinates, const int* yCoordinates) {
mInputCodes = inputCodes; mInputCodes = inputCodes;
mInputXCoordinates = xCoordinates;
mInputYCoordinates = yCoordinates;
mInputLength = inputLength; mInputLength = inputLength;
for (int i = 0; i < inputLength; ++i) { for (int i = 0; i < inputLength; ++i) {
mPrimaryInputWord[i] = getPrimaryCharAt(i); mPrimaryInputWord[i] = getPrimaryCharAt(i);
@ -158,19 +162,37 @@ bool ProximityInfo::existsAdjacentProximityChars(const int index) const {
ProximityInfo::ProximityType ProximityInfo::getMatchedProximityId( ProximityInfo::ProximityType ProximityInfo::getMatchedProximityId(
const int index, const unsigned short c, const bool checkProximityChars) const { const int index, const unsigned short c, const bool checkProximityChars) const {
const int *currentChars = getProximityCharsAt(index); const int *currentChars = getProximityCharsAt(index);
const int firstChar = currentChars[0];
const unsigned short baseLowerC = Dictionary::toBaseLowerCase(c); const unsigned short baseLowerC = Dictionary::toBaseLowerCase(c);
// The first char in the array is what user typed. If it matches right away, // 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. // that means the user typed that same char for this pos.
if (currentChars[0] == baseLowerC || currentChars[0] == c) if (firstChar == baseLowerC || firstChar == c) {
return SAME_OR_ACCENTED_OR_CAPITALIZED_CHAR; if (CALIBRATE_SCORE_BY_TOUCH_COORDINATES) {
const SweetSpotType result = calculateSweetSpotType(index, baseLowerC);
switch (result) {
case UNKNOWN:
return EQUIVALENT_CHAR_NORMAL;
case IN_SWEET_SPOT:
return EQUIVALENT_CHAR_STRONG;
case IN_NEUTRAL_AREA:
return EQUIVALENT_CHAR_NORMAL;
case OUT_OF_NEUTRAL_AREA:
return EQUIVALENT_CHAR_WEAK;
default:
assert(false);
}
} else {
return EQUIVALENT_CHAR_NORMAL;
}
}
if (!checkProximityChars) return UNRELATED_CHAR; if (!checkProximityChars) return UNRELATED_CHAR;
// If the non-accented, lowercased version of that first character matches c, // 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 // then we have a non-accented version of the accented character the user
// typed. Treat it as a close char. // typed. Treat it as a close char.
if (Dictionary::toBaseLowerCase(currentChars[0]) == baseLowerC) if (Dictionary::toBaseLowerCase(firstChar) == baseLowerC)
return NEAR_PROXIMITY_CHAR; return NEAR_PROXIMITY_CHAR;
// Not an exact nor an accent-alike match: search the list of close keys // Not an exact nor an accent-alike match: search the list of close keys
@ -185,6 +207,38 @@ ProximityInfo::ProximityType ProximityInfo::getMatchedProximityId(
return UNRELATED_CHAR; return UNRELATED_CHAR;
} }
inline float square(const float x) { return x * x; }
ProximityInfo::SweetSpotType ProximityInfo::calculateSweetSpotType(
int index, unsigned short baseLowerC) const {
if (KEY_COUNT == 0 || !mInputXCoordinates || !mInputYCoordinates
|| baseLowerC > MAX_CHAR_CODE) {
return UNKNOWN;
}
const int keyIndex = mCodeToKeyIndex[baseLowerC];
if (keyIndex < 0) {
return UNKNOWN;
}
const float sweetSpotRadius = mSweetSpotRadii[keyIndex];
if (sweetSpotRadius <= 0.0) {
return UNKNOWN;
}
const float sweetSpotCenterX = mSweetSpotCenterXs[keyIndex];
const float sweetSpotCenterY = mSweetSpotCenterXs[keyIndex];
const float inputX = (float)mInputXCoordinates[index];
const float inputY = (float)mInputYCoordinates[index];
const float squaredDistance =
square(inputX - sweetSpotCenterX) + square(inputY - sweetSpotCenterY);
const float squaredSweetSpotRadius = square(sweetSpotRadius);
if (squaredDistance <= squaredSweetSpotRadius) {
return IN_SWEET_SPOT;
}
if (squaredDistance <= square(NEUTRAL_AREA_RADIUS_RATIO) * squaredSweetSpotRadius) {
return IN_NEUTRAL_AREA;
}
return OUT_OF_NEUTRAL_AREA;
}
bool ProximityInfo::sameAsTyped(const unsigned short *word, int length) const { bool ProximityInfo::sameAsTyped(const unsigned short *word, int length) const {
if (length != mInputLength) { if (length != mInputLength) {
return false; return false;

View File

@ -27,10 +27,18 @@ class Correction;
class ProximityInfo { class ProximityInfo {
public: public:
typedef enum { // Used as a return value for character comparison // Used as a return value for character comparison
SAME_OR_ACCENTED_OR_CAPITALIZED_CHAR, // Same char, possibly with different case or accent typedef enum {
NEAR_PROXIMITY_CHAR, // It is a char located nearby on the keyboard // Same char, possibly with different case or accent, and in the sweet spot of the char
UNRELATED_CHAR // It is an unrelated char EQUIVALENT_CHAR_STRONG,
// Same char, possibly with different case or accent, and in the outer sweet spot
EQUIVALENT_CHAR_NORMAL,
// Same char, possibly with different case or accent, and in the hit box of the char
EQUIVALENT_CHAR_WEAK,
// It is a char located nearby on the keyboard
NEAR_PROXIMITY_CHAR,
// It is an unrelated char
UNRELATED_CHAR
} ProximityType; } ProximityType;
ProximityInfo(const int maxProximityCharsSize, const int keyboardWidth, ProximityInfo(const int maxProximityCharsSize, const int keyboardWidth,
@ -41,7 +49,8 @@ public:
const float *sweetSpotCenterYs, const float *sweetSpotRadii); const float *sweetSpotCenterYs, const float *sweetSpotRadii);
~ProximityInfo(); ~ProximityInfo();
bool hasSpaceProximity(const int x, const int y) const; bool hasSpaceProximity(const int x, const int y) const;
void setInputParams(const int* inputCodes, const int inputLength); void setInputParams(const int* inputCodes, const int inputLength,
const int *xCoordinates, const int *yCoordinates);
const int* getProximityCharsAt(const int index) const; const int* getProximityCharsAt(const int index) const;
unsigned short getPrimaryCharAt(const int index) const; unsigned short getPrimaryCharAt(const int index) const;
bool existsCharInProximityAt(const int index, const int c) const; bool existsCharInProximityAt(const int index, const int c) const;
@ -59,8 +68,20 @@ private:
// The upper limit of the char code in mCodeToKeyIndex // The upper limit of the char code in mCodeToKeyIndex
static const int MAX_CHAR_CODE = 127; static const int MAX_CHAR_CODE = 127;
typedef enum {
// cannot figure out the sweet spot type
UNKNOWN,
// touch position is out of neutral area of the given char
OUT_OF_NEUTRAL_AREA,
// touch position is in the neutral area of the given char
IN_NEUTRAL_AREA,
// touch position is in the sweet spot of the given char
IN_SWEET_SPOT
} SweetSpotType;
int getStartIndexFromCoordinates(const int x, const int y) const; int getStartIndexFromCoordinates(const int x, const int y) const;
void initializeCodeToKeyIndex(); void initializeCodeToKeyIndex();
SweetSpotType calculateSweetSpotType(int index, unsigned short baseLowerC) const;
const int MAX_PROXIMITY_CHARS_SIZE; const int MAX_PROXIMITY_CHARS_SIZE;
const int KEYBOARD_WIDTH; const int KEYBOARD_WIDTH;
const int KEYBOARD_HEIGHT; const int KEYBOARD_HEIGHT;
@ -70,6 +91,8 @@ private:
const int CELL_HEIGHT; const int CELL_HEIGHT;
const int KEY_COUNT; const int KEY_COUNT;
const int *mInputCodes; const int *mInputCodes;
const int *mInputXCoordinates;
const int *mInputYCoordinates;
uint32_t *mProximityCharsArray; uint32_t *mProximityCharsArray;
int32_t mKeyXCoordinates[MAX_KEY_COUNT_IN_A_KEYBOARD]; int32_t mKeyXCoordinates[MAX_KEY_COUNT_IN_A_KEYBOARD];
int32_t mKeyYCoordinates[MAX_KEY_COUNT_IN_A_KEYBOARD]; int32_t mKeyYCoordinates[MAX_KEY_COUNT_IN_A_KEYBOARD];

View File

@ -240,8 +240,8 @@ void UnigramDictionary::getWordSuggestions(ProximityInfo *proximityInfo,
PROF_END(6); PROF_END(6);
} }
void UnigramDictionary::initSuggestions(ProximityInfo *proximityInfo, const int *xcoordinates, void UnigramDictionary::initSuggestions(ProximityInfo *proximityInfo, const int *xCoordinates,
const int *ycoordinates, const int *codes, const int codesSize, const int *yCoordinates, const int *codes, const int codesSize,
unsigned short *outWords, int *frequencies) { unsigned short *outWords, int *frequencies) {
if (DEBUG_DICT) { if (DEBUG_DICT) {
LOGI("initSuggest"); LOGI("initSuggest");
@ -249,7 +249,7 @@ void UnigramDictionary::initSuggestions(ProximityInfo *proximityInfo, const int
mFrequencies = frequencies; mFrequencies = frequencies;
mOutputChars = outWords; mOutputChars = outWords;
mInputLength = codesSize; mInputLength = codesSize;
proximityInfo->setInputParams(codes, codesSize); proximityInfo->setInputParams(codes, codesSize, xCoordinates, yCoordinates);
mProximityInfo = proximityInfo; mProximityInfo = proximityInfo;
} }