am e05b3f4b: Support additional proximity characters
* commit 'e05b3f4b3a57dcf99ade35bfbc1e1cdc3c3e476c': Support additional proximity charactersmain
commit
d1ee49a939
|
@ -0,0 +1,62 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
/*
|
||||
**
|
||||
** Copyright 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.
|
||||
*/
|
||||
-->
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
|
||||
<string-array name="additional_proximitychars">
|
||||
<!-- Empty entry terminates the proximity chars array. -->
|
||||
|
||||
<!-- Additional proximity chars for a -->
|
||||
<item>a</item>
|
||||
<item>e</item>
|
||||
<item>i</item>
|
||||
<item>o</item>
|
||||
<item>u</item>
|
||||
<item></item>
|
||||
<!-- Additional proximity chars for e -->
|
||||
<item>e</item>
|
||||
<item>a</item>
|
||||
<item>i</item>
|
||||
<item>o</item>
|
||||
<item>u</item>
|
||||
<item></item>
|
||||
<!-- Additional proximity chars for i -->
|
||||
<item>i</item>
|
||||
<item>a</item>
|
||||
<item>e</item>
|
||||
<item>o</item>
|
||||
<item>u</item>
|
||||
<item></item>
|
||||
<!-- Additional proximity chars for o -->
|
||||
<item>o</item>
|
||||
<item>a</item>
|
||||
<item>e</item>
|
||||
<item>i</item>
|
||||
<item>u</item>
|
||||
<item></item>
|
||||
<!-- Additional proximity chars for u -->
|
||||
<item>u</item>
|
||||
<item>a</item>
|
||||
<item>e</item>
|
||||
<item>i</item>
|
||||
<item>o</item>
|
||||
<item></item>
|
||||
</string-array>
|
||||
|
||||
</resources>
|
|
@ -0,0 +1,23 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
/*
|
||||
**
|
||||
** Copyright 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.
|
||||
*/
|
||||
-->
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string-array name="additional_proximitychars">
|
||||
</string-array>
|
||||
</resources>
|
|
@ -19,12 +19,14 @@ package com.android.inputmethod.keyboard;
|
|||
import android.util.Log;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class KeyDetector {
|
||||
private static final String TAG = KeyDetector.class.getSimpleName();
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
public static final int NOT_A_CODE = -1;
|
||||
private static final int ADDITIONAL_PROXIMITY_CHAR_DELIMITER_CODE = 2;
|
||||
|
||||
private final int mKeyHysteresisDistanceSquared;
|
||||
|
||||
|
@ -154,8 +156,9 @@ public class KeyDetector {
|
|||
return distances.length;
|
||||
}
|
||||
|
||||
private void getNearbyKeyCodes(final int[] allCodes) {
|
||||
private void getNearbyKeyCodes(final int primaryCode, final int[] allCodes) {
|
||||
final Key[] neighborKeys = mNeighborKeys;
|
||||
final int maxCodesSize = allCodes.length;
|
||||
|
||||
// allCodes[0] should always have the key code even if it is a non-letter key.
|
||||
if (neighborKeys[0] == null) {
|
||||
|
@ -164,7 +167,7 @@ public class KeyDetector {
|
|||
}
|
||||
|
||||
int numCodes = 0;
|
||||
for (int j = 0; j < neighborKeys.length && numCodes < allCodes.length; j++) {
|
||||
for (int j = 0; j < neighborKeys.length && numCodes < maxCodesSize; j++) {
|
||||
final Key key = neighborKeys[j];
|
||||
if (key == null)
|
||||
break;
|
||||
|
@ -174,6 +177,38 @@ public class KeyDetector {
|
|||
continue;
|
||||
allCodes[numCodes++] = code;
|
||||
}
|
||||
if (maxCodesSize <= numCodes) {
|
||||
return;
|
||||
}
|
||||
if (primaryCode != NOT_A_CODE) {
|
||||
final List<Integer> additionalChars =
|
||||
mKeyboard.getAdditionalProximityChars().get(primaryCode);
|
||||
if (additionalChars == null || additionalChars.size() == 0) {
|
||||
return;
|
||||
}
|
||||
int currentCodesSize = numCodes;
|
||||
allCodes[numCodes++] = ADDITIONAL_PROXIMITY_CHAR_DELIMITER_CODE;
|
||||
if (maxCodesSize <= numCodes) {
|
||||
return;
|
||||
}
|
||||
// TODO: This is O(N^2). Assuming additionalChars.size() is up to 4 or 5.
|
||||
for (int i = 0; i < additionalChars.size(); ++i) {
|
||||
final int additionalChar = additionalChars.get(i);
|
||||
boolean contains = false;
|
||||
for (int j = 0; j < currentCodesSize; ++j) {
|
||||
if (additionalChar == allCodes[j]) {
|
||||
contains = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!contains) {
|
||||
allCodes[numCodes++] = additionalChar;
|
||||
if (maxCodesSize <= numCodes) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -205,7 +240,7 @@ public class KeyDetector {
|
|||
}
|
||||
|
||||
if (allCodes != null && allCodes.length > 0) {
|
||||
getNearbyKeyCodes(allCodes);
|
||||
getNearbyKeyCodes(primaryKey != null ? primaryKey.mCode : NOT_A_CODE, allCodes);
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "x=" + x + " y=" + y
|
||||
+ " primary=" + printableCode(primaryKey)
|
||||
|
|
|
@ -20,6 +20,7 @@ import android.content.Context;
|
|||
import android.content.res.Resources;
|
||||
import android.content.res.TypedArray;
|
||||
import android.content.res.XmlResourceParser;
|
||||
import android.text.TextUtils;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.Log;
|
||||
|
@ -38,10 +39,12 @@ import org.xmlpull.v1.XmlPullParser;
|
|||
import org.xmlpull.v1.XmlPullParserException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
|
@ -130,6 +133,8 @@ public class Keyboard {
|
|||
|
||||
private final ProximityInfo mProximityInfo;
|
||||
|
||||
public final Map<Integer, List<Integer>> mAdditionalProximityChars;
|
||||
|
||||
public Keyboard(Params params) {
|
||||
mId = params.mId;
|
||||
mThemeId = params.mThemeId;
|
||||
|
@ -146,10 +151,12 @@ public class Keyboard {
|
|||
mKeys = Collections.unmodifiableSet(params.mKeys);
|
||||
mShiftKeys = Collections.unmodifiableSet(params.mShiftKeys);
|
||||
mIconsSet = params.mIconsSet;
|
||||
mAdditionalProximityChars = params.mAdditionalProximityChars;
|
||||
|
||||
mProximityInfo = new ProximityInfo(
|
||||
params.GRID_WIDTH, params.GRID_HEIGHT, mOccupiedWidth, mOccupiedHeight,
|
||||
mMostCommonKeyWidth, mMostCommonKeyHeight, mKeys, params.mTouchPositionCorrection);
|
||||
mMostCommonKeyWidth, mMostCommonKeyHeight, mKeys, params.mTouchPositionCorrection,
|
||||
params.mAdditionalProximityChars);
|
||||
}
|
||||
|
||||
public ProximityInfo getProximityInfo() {
|
||||
|
@ -227,6 +234,9 @@ public class Keyboard {
|
|||
public final Set<Key> mKeys = new HashSet<Key>();
|
||||
public final Set<Key> mShiftKeys = new HashSet<Key>();
|
||||
public final KeyboardIconsSet mIconsSet = new KeyboardIconsSet();
|
||||
// TODO: Should be in Key instead of Keyboard.Params?
|
||||
public final Map<Integer, List<Integer>> mAdditionalProximityChars =
|
||||
new HashMap<Integer, List<Integer>>();
|
||||
|
||||
public KeyboardSet.KeysCache mKeysCache;
|
||||
|
||||
|
@ -358,6 +368,10 @@ public class Keyboard {
|
|||
return mProximityInfo.getNearestKeys(adjustedX, adjustedY);
|
||||
}
|
||||
|
||||
public Map<Integer, List<Integer>> getAdditionalProximityChars() {
|
||||
return mAdditionalProximityChars;
|
||||
}
|
||||
|
||||
public static String printableCode(int code) {
|
||||
switch (code) {
|
||||
case CODE_SHIFT: return "shift";
|
||||
|
@ -614,6 +628,7 @@ public class Keyboard {
|
|||
mParams = params;
|
||||
|
||||
setTouchPositionCorrectionData(context, params);
|
||||
setAdditionalProximityChars(context, params);
|
||||
|
||||
params.GRID_WIDTH = res.getInteger(R.integer.config_keyboard_grid_width);
|
||||
params.GRID_HEIGHT = res.getInteger(R.integer.config_keyboard_grid_height);
|
||||
|
@ -636,6 +651,25 @@ public class Keyboard {
|
|||
params.mTouchPositionCorrection.load(data);
|
||||
}
|
||||
|
||||
private static void setAdditionalProximityChars(Context context, Params params) {
|
||||
final String[] additionalChars =
|
||||
context.getResources().getStringArray(R.array.additional_proximitychars);
|
||||
int currentPrimaryIndex = 0;
|
||||
for (int i = 0; i < additionalChars.length; ++i) {
|
||||
final String additionalChar = additionalChars[i];
|
||||
if (TextUtils.isEmpty(additionalChar)) {
|
||||
currentPrimaryIndex = 0;
|
||||
} else if (currentPrimaryIndex == 0) {
|
||||
currentPrimaryIndex = additionalChar.charAt(0);
|
||||
params.mAdditionalProximityChars.put(
|
||||
currentPrimaryIndex, new ArrayList<Integer>());
|
||||
} else if (currentPrimaryIndex != 0) {
|
||||
final int c = additionalChar.charAt(0);
|
||||
params.mAdditionalProximityChars.get(currentPrimaryIndex).add(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setAutoGenerate(KeyboardSet.KeysCache keysCache) {
|
||||
mParams.mKeysCache = keysCache;
|
||||
}
|
||||
|
|
|
@ -24,6 +24,9 @@ import com.android.inputmethod.latin.spellcheck.SpellCheckerProximityInfo;
|
|||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class ProximityInfo {
|
||||
|
@ -44,7 +47,8 @@ public class ProximityInfo {
|
|||
private final Key[][] mGridNeighbors;
|
||||
|
||||
ProximityInfo(int gridWidth, int gridHeight, int minWidth, int height, int keyWidth,
|
||||
int keyHeight, Set<Key> keys, TouchPositionCorrection touchPositionCorrection) {
|
||||
int keyHeight, Set<Key> keys, TouchPositionCorrection touchPositionCorrection,
|
||||
Map<Integer, List<Integer>> additionalProximityChars) {
|
||||
mGridWidth = gridWidth;
|
||||
mGridHeight = gridHeight;
|
||||
mGridSize = mGridWidth * mGridHeight;
|
||||
|
@ -58,20 +62,20 @@ public class ProximityInfo {
|
|||
// No proximity required. Keyboard might be mini keyboard.
|
||||
return;
|
||||
}
|
||||
computeNearestNeighbors(keyWidth, keys, touchPositionCorrection);
|
||||
computeNearestNeighbors(keyWidth, keys, touchPositionCorrection, additionalProximityChars);
|
||||
}
|
||||
|
||||
public static ProximityInfo createDummyProximityInfo() {
|
||||
return new ProximityInfo(1, 1, 1, 1, 1, 1, Collections.<Key>emptySet(), null);
|
||||
return new ProximityInfo(1, 1, 1, 1, 1, 1, Collections.<Key> emptySet(),
|
||||
null, Collections.<Integer, List<Integer>> emptyMap());
|
||||
}
|
||||
|
||||
public static ProximityInfo createSpellCheckerProximityInfo(final int[] proximity) {
|
||||
final ProximityInfo spellCheckerProximityInfo = createDummyProximityInfo();
|
||||
spellCheckerProximityInfo.mNativeProximityInfo =
|
||||
spellCheckerProximityInfo.setProximityInfoNative(
|
||||
SpellCheckerProximityInfo.ROW_SIZE,
|
||||
480, 300, 11, 3, proximity,
|
||||
0, null, null, null, null, null, null, null, null);
|
||||
SpellCheckerProximityInfo.ROW_SIZE, 480, 300, 11, 3, proximity, 0,
|
||||
null, null, null, null, null, null, null, null);
|
||||
return spellCheckerProximityInfo;
|
||||
}
|
||||
|
||||
|
@ -79,11 +83,13 @@ public class ProximityInfo {
|
|||
static {
|
||||
Utils.loadNativeLibrary();
|
||||
}
|
||||
|
||||
private native long setProximityInfoNative(int maxProximityCharsSize, int displayWidth,
|
||||
int displayHeight, int gridWidth, int gridHeight, int[] proximityCharsArray,
|
||||
int keyCount, int[] keyXCoordinates, int[] keyYCoordinates,
|
||||
int[] keyWidths, int[] keyHeights, int[] keyCharCodes,
|
||||
float[] sweetSpotCenterX, float[] sweetSpotCenterY, float[] sweetSpotRadii);
|
||||
|
||||
private native void releaseProximityInfoNative(long nativeProximityInfo);
|
||||
|
||||
private final void setProximityInfo(Key[][] gridNeighborKeys, int keyboardWidth,
|
||||
|
@ -138,7 +144,7 @@ public class ProximityInfo {
|
|||
final float radius = touchPositionCorrection.mRadii[row];
|
||||
sweetSpotCenterXs[i] = hitBoxCenterX + x * hitBoxWidth;
|
||||
sweetSpotCenterYs[i] = hitBoxCenterY + y * hitBoxHeight;
|
||||
sweetSpotRadii[i] = radius * (float)Math.sqrt(
|
||||
sweetSpotRadii[i] = radius * (float) Math.sqrt(
|
||||
hitBoxWidth * hitBoxWidth + hitBoxHeight * hitBoxHeight);
|
||||
}
|
||||
}
|
||||
|
@ -168,7 +174,12 @@ public class ProximityInfo {
|
|||
}
|
||||
|
||||
private void computeNearestNeighbors(int defaultWidth, Set<Key> keys,
|
||||
TouchPositionCorrection touchPositionCorrection) {
|
||||
TouchPositionCorrection touchPositionCorrection,
|
||||
Map<Integer, List<Integer>> additionalProximityChars) {
|
||||
final Map<Integer, Key> keyCodeMap = new HashMap<Integer, Key>();
|
||||
for (final Key key : keys) {
|
||||
keyCodeMap.put(key.mCode, key);
|
||||
}
|
||||
final int thresholdBase = (int) (defaultWidth * SEARCH_DISTANCE);
|
||||
final int threshold = thresholdBase * thresholdBase;
|
||||
// Round-up so we don't have any pixels outside the grid
|
||||
|
@ -186,6 +197,27 @@ public class ProximityInfo {
|
|||
neighborKeys[count++] = key;
|
||||
}
|
||||
}
|
||||
int currentCodesSize = count;
|
||||
for (int i = 0; i < currentCodesSize; ++i) {
|
||||
final int c = neighborKeys[i].mCode;
|
||||
final List<Integer> additionalChars = additionalProximityChars.get(c);
|
||||
if (additionalChars == null || additionalChars.size() == 0) {
|
||||
continue;
|
||||
}
|
||||
for (int j = 0; j < additionalChars.size(); ++j) {
|
||||
final int additionalChar = additionalChars.get(j);
|
||||
boolean contains = false;
|
||||
for (int k = 0; k < count; ++k) {
|
||||
if(additionalChar == neighborKeys[k].mCode) {
|
||||
contains = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!contains) {
|
||||
neighborKeys[count++] = keyCodeMap.get(additionalChar);
|
||||
}
|
||||
}
|
||||
}
|
||||
mGridNeighbors[(y / mCellHeight) * mGridWidth + (x / mCellWidth)] =
|
||||
Arrays.copyOfRange(neighborKeys, 0, count);
|
||||
}
|
||||
|
@ -199,7 +231,7 @@ public class ProximityInfo {
|
|||
return EMPTY_KEY_ARRAY;
|
||||
}
|
||||
if (x >= 0 && x < mKeyboardMinWidth && y >= 0 && y < mKeyboardHeight) {
|
||||
int index = (y / mCellHeight) * mGridWidth + (x / mCellWidth);
|
||||
int index = (y / mCellHeight) * mGridWidth + (x / mCellWidth);
|
||||
if (index < mGridSize) {
|
||||
return mGridNeighbors[index];
|
||||
}
|
||||
|
|
|
@ -16,8 +16,6 @@
|
|||
|
||||
package com.android.inputmethod.latin;
|
||||
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.android.inputmethod.keyboard.Key;
|
||||
import com.android.inputmethod.keyboard.KeyDetector;
|
||||
import com.android.inputmethod.keyboard.Keyboard;
|
||||
|
@ -33,7 +31,7 @@ public class WordComposer {
|
|||
public static final int NOT_A_CODE = KeyDetector.NOT_A_CODE;
|
||||
public static final int NOT_A_COORDINATE = -1;
|
||||
|
||||
final int N = BinaryDictionary.MAX_WORD_LENGTH;
|
||||
final static int N = BinaryDictionary.MAX_WORD_LENGTH;
|
||||
|
||||
private ArrayList<int[]> mCodes;
|
||||
private int[] mXCoordinates;
|
||||
|
|
|
@ -383,7 +383,10 @@ Correction::CorrectionType Correction::processCharAndCalcState(
|
|||
incrementInputIndex();
|
||||
} else {
|
||||
--mTransposedCount;
|
||||
if (DEBUG_CORRECTION) {
|
||||
if (DEBUG_CORRECTION
|
||||
&& (INPUTLENGTH_FOR_DEBUG <= 0 || INPUTLENGTH_FOR_DEBUG == mInputLength)
|
||||
&& (MIN_OUTPUT_INDEX_FOR_DEBUG <= 0
|
||||
|| MIN_OUTPUT_INDEX_FOR_DEBUG < mOutputIndex)) {
|
||||
DUMP_WORD(mWord, mOutputIndex);
|
||||
AKLOGI("UNRELATED(0): %d, %d, %d, %d, %c", mProximityCount, mSkippedCount,
|
||||
mTransposedCount, mExcessiveCount, c);
|
||||
|
@ -404,13 +407,17 @@ Correction::CorrectionType Correction::processCharAndCalcState(
|
|||
: mProximityInfo->getMatchedProximityId(
|
||||
mInputIndex, c, checkProximityChars, &proximityIndex);
|
||||
|
||||
if (ProximityInfo::UNRELATED_CHAR == matchedProximityCharId) {
|
||||
if (ProximityInfo::UNRELATED_CHAR == matchedProximityCharId
|
||||
|| ProximityInfo::ADDITIONAL_PROXIMITY_CHAR == matchedProximityCharId) {
|
||||
if (canTryCorrection && mOutputIndex > 0
|
||||
&& mCorrectionStates[mOutputIndex].mProximityMatching
|
||||
&& mCorrectionStates[mOutputIndex].mExceeding
|
||||
&& isEquivalentChar(mProximityInfo->getMatchedProximityId(
|
||||
mInputIndex, mWord[mOutputIndex - 1], false))) {
|
||||
if (DEBUG_CORRECTION) {
|
||||
if (DEBUG_CORRECTION
|
||||
&& (INPUTLENGTH_FOR_DEBUG <= 0 || INPUTLENGTH_FOR_DEBUG == mInputLength)
|
||||
&& (MIN_OUTPUT_INDEX_FOR_DEBUG <= 0
|
||||
|| MIN_OUTPUT_INDEX_FOR_DEBUG < mOutputIndex)) {
|
||||
AKLOGI("CONVERSION p->e %c", mWord[mOutputIndex - 1]);
|
||||
}
|
||||
// Conversion p->e
|
||||
|
@ -429,7 +436,8 @@ Correction::CorrectionType Correction::processCharAndCalcState(
|
|||
}
|
||||
}
|
||||
|
||||
if (ProximityInfo::UNRELATED_CHAR == matchedProximityCharId) {
|
||||
if (ProximityInfo::UNRELATED_CHAR == matchedProximityCharId
|
||||
|| ProximityInfo::ADDITIONAL_PROXIMITY_CHAR == matchedProximityCharId) {
|
||||
// TODO: Optimize
|
||||
// As the current char turned out to be an unrelated char,
|
||||
// we will try other correction-types. Please note that mCorrectionStates[mOutputIndex]
|
||||
|
@ -481,12 +489,47 @@ Correction::CorrectionType Correction::processCharAndCalcState(
|
|||
++mExcessiveCount;
|
||||
incrementInputIndex();
|
||||
}
|
||||
if (DEBUG_CORRECTION
|
||||
&& (INPUTLENGTH_FOR_DEBUG <= 0 || INPUTLENGTH_FOR_DEBUG == mInputLength)
|
||||
&& (MIN_OUTPUT_INDEX_FOR_DEBUG <= 0
|
||||
|| MIN_OUTPUT_INDEX_FOR_DEBUG < mOutputIndex)) {
|
||||
DUMP_WORD(mWord, mOutputIndex);
|
||||
if (mTransposing) {
|
||||
AKLOGI("TRANSPOSE: %d, %d, %d, %d, %c", mProximityCount, mSkippedCount,
|
||||
mTransposedCount, mExcessiveCount, c);
|
||||
} else {
|
||||
AKLOGI("EXCEED: %d, %d, %d, %d, %c", mProximityCount, mSkippedCount,
|
||||
mTransposedCount, mExcessiveCount, c);
|
||||
}
|
||||
}
|
||||
} else if (mSkipping) {
|
||||
// 3. Skip correction
|
||||
++mSkippedCount;
|
||||
if (DEBUG_CORRECTION
|
||||
&& (INPUTLENGTH_FOR_DEBUG <= 0 || INPUTLENGTH_FOR_DEBUG == mInputLength)
|
||||
&& (MIN_OUTPUT_INDEX_FOR_DEBUG <= 0
|
||||
|| MIN_OUTPUT_INDEX_FOR_DEBUG < mOutputIndex)) {
|
||||
AKLOGI("SKIP: %d, %d, %d, %d, %c", mProximityCount, mSkippedCount,
|
||||
mTransposedCount, mExcessiveCount, c);
|
||||
}
|
||||
return processSkipChar(c, isTerminal, false);
|
||||
} else if (ProximityInfo::ADDITIONAL_PROXIMITY_CHAR == matchedProximityCharId) {
|
||||
// As a last resort, use additional proximity characters
|
||||
mProximityMatching = true;
|
||||
++mProximityCount;
|
||||
mDistances[mOutputIndex] = ADDITIONAL_PROXIMITY_CHAR_DISTANCE_INFO;
|
||||
if (DEBUG_CORRECTION
|
||||
&& (INPUTLENGTH_FOR_DEBUG <= 0 || INPUTLENGTH_FOR_DEBUG == mInputLength)
|
||||
&& (MIN_OUTPUT_INDEX_FOR_DEBUG <= 0
|
||||
|| MIN_OUTPUT_INDEX_FOR_DEBUG < mOutputIndex)) {
|
||||
AKLOGI("ADDITIONALPROX: %d, %d, %d, %d, %c", mProximityCount, mSkippedCount,
|
||||
mTransposedCount, mExcessiveCount, c);
|
||||
}
|
||||
} else {
|
||||
if (DEBUG_CORRECTION) {
|
||||
if (DEBUG_CORRECTION
|
||||
&& (INPUTLENGTH_FOR_DEBUG <= 0 || INPUTLENGTH_FOR_DEBUG == mInputLength)
|
||||
&& (MIN_OUTPUT_INDEX_FOR_DEBUG <= 0
|
||||
|| MIN_OUTPUT_INDEX_FOR_DEBUG < mOutputIndex)) {
|
||||
DUMP_WORD(mWord, mOutputIndex);
|
||||
AKLOGI("UNRELATED(1): %d, %d, %d, %d, %c", mProximityCount, mSkippedCount,
|
||||
mTransposedCount, mExcessiveCount, c);
|
||||
|
@ -506,6 +549,13 @@ Correction::CorrectionType Correction::processCharAndCalcState(
|
|||
++mProximityCount;
|
||||
mDistances[mOutputIndex] =
|
||||
mProximityInfo->getNormalizedSquaredDistance(mInputIndex, proximityIndex);
|
||||
if (DEBUG_CORRECTION
|
||||
&& (INPUTLENGTH_FOR_DEBUG <= 0 || INPUTLENGTH_FOR_DEBUG == mInputLength)
|
||||
&& (MIN_OUTPUT_INDEX_FOR_DEBUG <= 0
|
||||
|| MIN_OUTPUT_INDEX_FOR_DEBUG < mOutputIndex)) {
|
||||
AKLOGI("PROX: %d, %d, %d, %d, %c", mProximityCount, mSkippedCount,
|
||||
mTransposedCount, mExcessiveCount, c);
|
||||
}
|
||||
}
|
||||
|
||||
addCharToCurrentWord(c);
|
||||
|
@ -539,7 +589,9 @@ Correction::CorrectionType Correction::processCharAndCalcState(
|
|||
|| isSameAsUserTypedLength) && isTerminal) {
|
||||
mTerminalInputIndex = mInputIndex - 1;
|
||||
mTerminalOutputIndex = mOutputIndex - 1;
|
||||
if (DEBUG_CORRECTION) {
|
||||
if (DEBUG_CORRECTION
|
||||
&& (INPUTLENGTH_FOR_DEBUG <= 0 || INPUTLENGTH_FOR_DEBUG == mInputLength)
|
||||
&& (MIN_OUTPUT_INDEX_FOR_DEBUG <= 0 || MIN_OUTPUT_INDEX_FOR_DEBUG < mOutputIndex)) {
|
||||
DUMP_WORD(mWord, mOutputIndex);
|
||||
AKLOGI("ONTERMINAL(1): %d, %d, %d, %d, %c", mProximityCount, mSkippedCount,
|
||||
mTransposedCount, mExcessiveCount, c);
|
||||
|
@ -678,7 +730,7 @@ int Correction::RankingAlgorithm::calculateFinalFreq(const int inputIndex, const
|
|||
if (excessiveCount > 0) {
|
||||
multiplyRate(WORDS_WITH_EXCESSIVE_CHARACTER_DEMOTION_RATE, &finalFreq);
|
||||
if (!lastCharExceeded && !proximityInfo->existsAdjacentProximityChars(excessivePos)) {
|
||||
if (DEBUG_CORRECTION_FREQ) {
|
||||
if (DEBUG_DICT_FULL) {
|
||||
AKLOGI("Double excessive demotion");
|
||||
}
|
||||
// If an excessive character is not adjacent to the left char or the right char,
|
||||
|
@ -687,51 +739,46 @@ int Correction::RankingAlgorithm::calculateFinalFreq(const int inputIndex, const
|
|||
}
|
||||
}
|
||||
|
||||
const bool performTouchPositionCorrection =
|
||||
CALIBRATE_SCORE_BY_TOUCH_COORDINATES && proximityInfo->touchPositionCorrectionEnabled()
|
||||
&& skippedCount == 0 && excessiveCount == 0 && transposedCount == 0;
|
||||
// Score calibration by touch coordinates is being done only for pure-fat finger typing error
|
||||
// cases.
|
||||
// TODO: Remove this constraint.
|
||||
if (CALIBRATE_SCORE_BY_TOUCH_COORDINATES && proximityInfo->touchPositionCorrectionEnabled()
|
||||
&& skippedCount == 0 && excessiveCount == 0 && transposedCount == 0) {
|
||||
for (int i = 0; i < outputLength; ++i) {
|
||||
const int squaredDistance = correction->mDistances[i];
|
||||
if (i < adjustedProximityMatchedCount) {
|
||||
multiplyIntCapped(typedLetterMultiplier, &finalFreq);
|
||||
}
|
||||
if (squaredDistance >= 0) {
|
||||
// Promote or demote the score according to the distance from the sweet spot
|
||||
static const float A = ZERO_DISTANCE_PROMOTION_RATE / 100.0f;
|
||||
static const float B = 1.0f;
|
||||
static const float C = 0.5f;
|
||||
static const float R1 = NEUTRAL_SCORE_SQUARED_RADIUS;
|
||||
static const float R2 = HALF_SCORE_SQUARED_RADIUS;
|
||||
const float x = (float)squaredDistance
|
||||
/ ProximityInfo::NORMALIZED_SQUARED_DISTANCE_SCALING_FACTOR;
|
||||
const float factor = (x < R1)
|
||||
? (A * (R1 - x) + B * x) / R1
|
||||
: (B * (R2 - x) + C * (x - R1)) / (R2 - R1);
|
||||
// factor is piecewise linear function like:
|
||||
// A -_ .
|
||||
// ^-_ .
|
||||
// B \ .
|
||||
// \ .
|
||||
// C \ .
|
||||
// 0 R1 R2
|
||||
if (factor <= 0) {
|
||||
return -1;
|
||||
}
|
||||
multiplyRate((int)(factor * 100), &finalFreq);
|
||||
} else if (squaredDistance == PROXIMITY_CHAR_WITHOUT_DISTANCE_INFO) {
|
||||
multiplyRate(WORDS_WITH_PROXIMITY_CHARACTER_DEMOTION_RATE, &finalFreq);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Promotion for a word with proximity characters
|
||||
for (int i = 0; i < adjustedProximityMatchedCount; ++i) {
|
||||
// A word with proximity corrections
|
||||
if (DEBUG_DICT_FULL) {
|
||||
AKLOGI("Found a proximity correction.");
|
||||
}
|
||||
for (int i = 0; i < outputLength; ++i) {
|
||||
const int squaredDistance = correction->mDistances[i];
|
||||
if (i < adjustedProximityMatchedCount) {
|
||||
multiplyIntCapped(typedLetterMultiplier, &finalFreq);
|
||||
}
|
||||
|
||||
if (performTouchPositionCorrection && squaredDistance >= 0) {
|
||||
// Promote or demote the score according to the distance from the sweet spot
|
||||
static const float A = ZERO_DISTANCE_PROMOTION_RATE / 100.0f;
|
||||
static const float B = 1.0f;
|
||||
static const float C = 0.5f;
|
||||
static const float MIN = 0.3f;
|
||||
static const float R1 = NEUTRAL_SCORE_SQUARED_RADIUS;
|
||||
static const float R2 = HALF_SCORE_SQUARED_RADIUS;
|
||||
const float x = (float)squaredDistance
|
||||
/ ProximityInfo::NORMALIZED_SQUARED_DISTANCE_SCALING_FACTOR;
|
||||
const float factor = max((x < R1)
|
||||
? (A * (R1 - x) + B * x) / R1
|
||||
: (B * (R2 - x) + C * (x - R1)) / (R2 - R1), MIN);
|
||||
// factor is piecewise linear function like:
|
||||
// A -_ .
|
||||
// ^-_ .
|
||||
// B \ .
|
||||
// \_ .
|
||||
// C ------------.
|
||||
// .
|
||||
// 0 R1 R2 .
|
||||
multiplyRate((int)(factor * 100), &finalFreq);
|
||||
} else if (performTouchPositionCorrection
|
||||
&& squaredDistance == PROXIMITY_CHAR_WITHOUT_DISTANCE_INFO) {
|
||||
multiplyRate(WORDS_WITH_PROXIMITY_CHARACTER_DEMOTION_RATE, &finalFreq);
|
||||
} else if (squaredDistance == ADDITIONAL_PROXIMITY_CHAR_DISTANCE_INFO) {
|
||||
multiplyRate(WORDS_WITH_ADDITIONAL_PROXIMITY_CHARACTER_DEMOTION_RATE, &finalFreq);
|
||||
} else if (i < adjustedProximityMatchedCount) {
|
||||
multiplyRate(WORDS_WITH_PROXIMITY_CHARACTER_DEMOTION_RATE, &finalFreq);
|
||||
}
|
||||
}
|
||||
|
@ -794,7 +841,8 @@ int Correction::RankingAlgorithm::calculateFinalFreq(const int inputIndex, const
|
|||
AKLOGI("calc: %d, %d", outputLength, sameLength);
|
||||
}
|
||||
|
||||
if (DEBUG_CORRECTION_FREQ) {
|
||||
if (DEBUG_CORRECTION_FREQ
|
||||
&& (INPUTLENGTH_FOR_DEBUG <= 0 || INPUTLENGTH_FOR_DEBUG == inputLength)) {
|
||||
DUMP_WORD(correction->mWord, outputLength);
|
||||
AKLOGI("FinalFreq: [P%d, S%d, T%d, E%d] %d, %d, %d, %d, %d, %d", proximityMatchedCount,
|
||||
skippedCount, transposedCount, excessiveCount, outputLength, lastCharExceeded,
|
||||
|
|
|
@ -85,7 +85,7 @@ class Correction {
|
|||
}
|
||||
}
|
||||
|
||||
Correction(const int typedLetterMultiplier, const int fullWordMultiplier);
|
||||
Correction(const int typedLetterMultiplier, const int fullWordMultiplier);
|
||||
void initCorrection(
|
||||
const ProximityInfo *pi, const int inputLength, const int maxWordLength);
|
||||
void initCorrectionState(const int rootPos, const int childCount, const bool traverseAll);
|
||||
|
|
|
@ -172,6 +172,7 @@ static void prof_out(void) {
|
|||
#define NOT_A_COORDINATE -1
|
||||
#define EQUIVALENT_CHAR_WITHOUT_DISTANCE_INFO -2
|
||||
#define PROXIMITY_CHAR_WITHOUT_DISTANCE_INFO -3
|
||||
#define ADDITIONAL_PROXIMITY_CHAR_DISTANCE_INFO -4
|
||||
#define NOT_A_INDEX -1
|
||||
#define NOT_A_FREQUENCY -1
|
||||
|
||||
|
@ -194,6 +195,7 @@ static void prof_out(void) {
|
|||
#define WORDS_WITH_TRANSPOSED_CHARACTERS_DEMOTION_RATE 70
|
||||
#define FULL_MATCHED_WORDS_PROMOTION_RATE 120
|
||||
#define WORDS_WITH_PROXIMITY_CHARACTER_DEMOTION_RATE 90
|
||||
#define WORDS_WITH_ADDITIONAL_PROXIMITY_CHARACTER_DEMOTION_RATE 30
|
||||
#define WORDS_WITH_MATCH_SKIP_PROMOTION_RATE 105
|
||||
#define WORDS_WITH_JUST_ONE_CORRECTION_PROMOTION_RATE 160
|
||||
#define CORRECTION_COUNT_RATE_DEMOTION_RATE_BASE 45
|
||||
|
@ -210,6 +212,9 @@ static void prof_out(void) {
|
|||
// This is only used for the size of array. Not to be used in c functions.
|
||||
#define MAX_WORD_LENGTH_INTERNAL 48
|
||||
|
||||
// This must be equal to ADDITIONAL_PROXIMITY_CHAR_DELIMITER_CODE in KeyDetector.java
|
||||
#define ADDITIONAL_PROXIMITY_CHAR_DELIMITER_CODE 2
|
||||
|
||||
// Word limit for sub queues used in WordsPriorityQueuePool. Sub queues are temporary queues used
|
||||
// for better performance.
|
||||
// Holds up to 1 candidate for each word
|
||||
|
@ -241,4 +246,8 @@ static void prof_out(void) {
|
|||
// The ratio of neutral area radius to sweet spot radius.
|
||||
#define NEUTRAL_AREA_RADIUS_RATIO 1.3f
|
||||
|
||||
// DEBUG
|
||||
#define INPUTLENGTH_FOR_DEBUG -1
|
||||
#define MIN_OUTPUT_INDEX_FOR_DEBUG -1
|
||||
|
||||
#endif // LATINIME_DEFINES_H
|
||||
|
|
|
@ -261,7 +261,8 @@ ProximityInfo::ProximityType ProximityInfo::getMatchedProximityId(const int inde
|
|||
|
||||
// Not an exact nor an accent-alike match: search the list of close keys
|
||||
int j = 1;
|
||||
while (j < MAX_PROXIMITY_CHARS_SIZE && currentChars[j] > 0) {
|
||||
while (j < MAX_PROXIMITY_CHARS_SIZE
|
||||
&& currentChars[j] > ADDITIONAL_PROXIMITY_CHAR_DELIMITER_CODE) {
|
||||
const bool matched = (currentChars[j] == baseLowerC || currentChars[j] == c);
|
||||
if (matched) {
|
||||
if (proximityIndex) {
|
||||
|
@ -271,6 +272,21 @@ ProximityInfo::ProximityType ProximityInfo::getMatchedProximityId(const int inde
|
|||
}
|
||||
++j;
|
||||
}
|
||||
if (j < MAX_PROXIMITY_CHARS_SIZE
|
||||
&& currentChars[j] == ADDITIONAL_PROXIMITY_CHAR_DELIMITER_CODE) {
|
||||
++j;
|
||||
while (j < MAX_PROXIMITY_CHARS_SIZE
|
||||
&& currentChars[j] > ADDITIONAL_PROXIMITY_CHAR_DELIMITER_CODE) {
|
||||
const bool matched = (currentChars[j] == baseLowerC || currentChars[j] == c);
|
||||
if (matched) {
|
||||
if (proximityIndex) {
|
||||
*proximityIndex = j;
|
||||
}
|
||||
return ADDITIONAL_PROXIMITY_CHAR;
|
||||
}
|
||||
++j;
|
||||
}
|
||||
}
|
||||
|
||||
// Was not included, signal this as an unrelated character.
|
||||
return UNRELATED_CHAR;
|
||||
|
|
|
@ -38,7 +38,9 @@ class ProximityInfo {
|
|||
// It is a char located nearby on the keyboard
|
||||
NEAR_PROXIMITY_CHAR,
|
||||
// It is an unrelated char
|
||||
UNRELATED_CHAR
|
||||
UNRELATED_CHAR,
|
||||
// Additional proximity char which can differ by language.
|
||||
ADDITIONAL_PROXIMITY_CHAR
|
||||
} ProximityType;
|
||||
|
||||
ProximityInfo(const int maxProximityCharsSize, const int keyboardWidth,
|
||||
|
|
Loading…
Reference in New Issue