aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--java/res/values/attrs.xml1
-rw-r--r--java/res/values/config.xml1
-rw-r--r--java/res/values/styles.xml4
-rw-r--r--java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java47
-rw-r--r--java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java27
-rw-r--r--java/src/com/android/inputmethod/compat/AudioManagerCompatWrapper.java54
-rw-r--r--java/src/com/android/inputmethod/latin/CandidateView.java9
-rw-r--r--java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java17
-rw-r--r--java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java25
-rw-r--r--native/src/correction.cpp198
-rw-r--r--native/src/correction.h3
-rw-r--r--native/src/defines.h3
12 files changed, 257 insertions, 132 deletions
diff --git a/java/res/values/attrs.xml b/java/res/values/attrs.xml
index c2200b5ad..172ca2f25 100644
--- a/java/res/values/attrs.xml
+++ b/java/res/values/attrs.xml
@@ -107,7 +107,6 @@
</declare-styleable>
<declare-styleable name="CandidateView">
- <attr name="autoCorrectionVisualFlashEnabled" format="boolean" />
<attr name="autoCorrectHighlight" format="integer">
<flag name="autoCorrectBold" value="0x01" />
<flag name="autoCorrectUnderline" value="0x02" />
diff --git a/java/res/values/config.xml b/java/res/values/config.xml
index 6327ede38..1c7c1a172 100644
--- a/java/res/values/config.xml
+++ b/java/res/values/config.xml
@@ -39,7 +39,6 @@
<bool name="config_default_compat_recorrection_enabled">true</bool>
<bool name="config_default_sound_enabled">false</bool>
<bool name="config_auto_correction_spacebar_led_enabled">true</bool>
- <bool name="config_auto_correction_suggestion_strip_visual_flash_enabled">false</bool>
<!-- Showing mini keyboard, just above the touched point if true, aligned to the key if false -->
<bool name="config_show_mini_keyboard_at_touched_point">false</bool>
<!-- The language is never displayed if == 0, always displayed if < 0 -->
diff --git a/java/res/values/styles.xml b/java/res/values/styles.xml
index a47eeed12..8145d0510 100644
--- a/java/res/values/styles.xml
+++ b/java/res/values/styles.xml
@@ -85,7 +85,6 @@
<item name="android:background">@drawable/candidate_feedback_background</item>
</style>
<style name="CandidateViewStyle" parent="SuggestionsStripBackgroundStyle">
- <item name="autoCorrectionVisualFlashEnabled">@bool/config_auto_correction_suggestion_strip_visual_flash_enabled</item>
<item name="autoCorrectHighlight">autoCorrectBold</item>
<item name="colorTypedWord">#FFFFFFFF</item>
<item name="colorAutoCorrect">#FFFCAE00</item>
@@ -189,8 +188,7 @@
<item name="android:background">@drawable/keyboard_popup_panel_background_holo</item>
</style>
<style name="CandidateViewStyle.IceCreamSandwich" parent="SuggestionsStripBackgroundStyle.IceCreamSandwich">
- <item name="autoCorrectionVisualFlashEnabled">@bool/config_auto_correction_suggestion_strip_visual_flash_enabled</item>
- <item name="autoCorrectHighlight">autoCorrectBold|autoCorrectInvert</item>
+ <item name="autoCorrectHighlight">autoCorrectBold</item>
<item name="colorTypedWord">#FFFFFFFF</item>
<item name="colorAutoCorrect">#FF3DC8FF</item>
<item name="colorSuggested">#FFFFFFFF</item>
diff --git a/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java b/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java
index ae9809e56..3dca9aae6 100644
--- a/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java
+++ b/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java
@@ -20,12 +20,18 @@ import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.Color;
import android.graphics.Paint;
+import android.inputmethodservice.InputMethodService;
+import android.media.AudioManager;
import android.util.Log;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
import android.view.accessibility.AccessibilityEvent;
+import android.view.inputmethod.EditorInfo;
import com.android.inputmethod.compat.AccessibilityEventCompatUtils;
+import com.android.inputmethod.compat.AudioManagerCompatWrapper;
+import com.android.inputmethod.compat.EditorInfoCompatUtils;
+import com.android.inputmethod.compat.InputTypeCompatUtils;
import com.android.inputmethod.compat.MotionEventCompatUtils;
import com.android.inputmethod.keyboard.Key;
import com.android.inputmethod.keyboard.KeyDetector;
@@ -39,17 +45,19 @@ public class AccessibleKeyboardViewProxy {
// Delay in milliseconds between key press DOWN and UP events
private static final long DELAY_KEY_PRESS = 10;
- private int mScaledEdgeSlop;
+ private InputMethodService mInputMethod;
+ private FlickGestureDetector mGestureDetector;
private LatinKeyboardBaseView mView;
private AccessibleKeyboardActionListener mListener;
- private FlickGestureDetector mGestureDetector;
+ private AudioManagerCompatWrapper mAudioManager;
+ private int mScaledEdgeSlop;
private int mLastHoverKeyIndex = KeyDetector.NOT_A_KEY;
private int mLastX = -1;
private int mLastY = -1;
- public static void init(Context context, SharedPreferences prefs) {
- sInstance.initInternal(context, prefs);
+ public static void init(InputMethodService inputMethod, SharedPreferences prefs) {
+ sInstance.initInternal(inputMethod, prefs);
sInstance.mListener = AccessibleInputMethodServiceProxy.getInstance();
}
@@ -65,15 +73,36 @@ public class AccessibleKeyboardViewProxy {
// Not publicly instantiable.
}
- private void initInternal(Context context, SharedPreferences prefs) {
+ private void initInternal(InputMethodService inputMethod, SharedPreferences prefs) {
final Paint paint = new Paint();
paint.setTextAlign(Paint.Align.LEFT);
paint.setTextSize(14.0f);
paint.setAntiAlias(true);
paint.setColor(Color.YELLOW);
- mGestureDetector = new KeyboardFlickGestureDetector(context);
- mScaledEdgeSlop = ViewConfiguration.get(context).getScaledEdgeSlop();
+ mInputMethod = inputMethod;
+ mGestureDetector = new KeyboardFlickGestureDetector(inputMethod);
+ mScaledEdgeSlop = ViewConfiguration.get(inputMethod).getScaledEdgeSlop();
+
+ final AudioManager audioManager = (AudioManager) inputMethod
+ .getSystemService(Context.AUDIO_SERVICE);
+ mAudioManager = new AudioManagerCompatWrapper(audioManager);
+ }
+
+ /**
+ * @return {@code true} if the device should not speak text (eg. non-control) characters
+ */
+ private boolean shouldObscureInput() {
+ // Always speak if the user is listening through headphones.
+ if (mAudioManager.isWiredHeadsetOn() || mAudioManager.isBluetoothA2dpOn())
+ return false;
+
+ final EditorInfo info = mInputMethod.getCurrentInputEditorInfo();
+ if (info == null)
+ return false;
+
+ // Don't speak if the IME is connected to a password field.
+ return InputTypeCompatUtils.isPasswordInputType(info.inputType);
}
public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event,
@@ -90,8 +119,10 @@ public class AccessibleKeyboardViewProxy {
if (key == null)
break;
+ final boolean shouldObscure = shouldObscureInput();
final CharSequence description = KeyCodeDescriptionMapper.getInstance()
- .getDescriptionForKey(mView.getContext(), mView.getKeyboard(), key);
+ .getDescriptionForKey(mView.getContext(), mView.getKeyboard(), key,
+ shouldObscure);
if (description == null)
return false;
diff --git a/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java b/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java
index ec4287dda..7302830d4 100644
--- a/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java
+++ b/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java
@@ -28,6 +28,9 @@ import com.android.inputmethod.latin.R;
import java.util.HashMap;
public class KeyCodeDescriptionMapper {
+ // The resource ID of the string spoken for obscured keys
+ private static final int OBSCURED_KEY_RES_ID = R.string.spoken_description_dot;
+
private static KeyCodeDescriptionMapper sInstance = new KeyCodeDescriptionMapper();
// Map of key labels to spoken description resource IDs
@@ -118,10 +121,12 @@ public class KeyCodeDescriptionMapper {
* @param context The package's context.
* @param keyboard The keyboard on which the key resides.
* @param key The key from which to obtain a description.
+ * @param shouldObscure {@true} if text (e.g. non-control) characters should be obscured.
* @return a character sequence describing the action performed by pressing
* the key
*/
- public CharSequence getDescriptionForKey(Context context, Keyboard keyboard, Key key) {
+ public CharSequence getDescriptionForKey(Context context, Keyboard keyboard, Key key,
+ boolean shouldObscure) {
if (key.mCode == Keyboard.CODE_SWITCH_ALPHA_SYMBOL) {
final CharSequence description = getDescriptionForSwitchAlphaSymbol(context, keyboard);
if (description != null)
@@ -136,12 +141,12 @@ public class KeyCodeDescriptionMapper {
} else if (label.length() == 1
|| (keyboard.isManualTemporaryUpperCase() && !TextUtils
.isEmpty(key.mHintLabel))) {
- return getDescriptionForKeyCode(context, keyboard, key);
+ return getDescriptionForKeyCode(context, keyboard, key, shouldObscure);
} else {
return label;
}
} else if (key.mCode != Keyboard.CODE_DUMMY) {
- return getDescriptionForKeyCode(context, keyboard, key);
+ return getDescriptionForKeyCode(context, keyboard, key, shouldObscure);
}
return null;
@@ -206,19 +211,29 @@ public class KeyCodeDescriptionMapper {
* @param context The package's context.
* @param keyboard The keyboard on which the key resides.
* @param key The key from which to obtain a description.
+ * @param shouldObscure {@true} if text (e.g. non-control) characters should be obscured.
* @return a character sequence describing the action performed by pressing
* the key
*/
- private CharSequence getDescriptionForKeyCode(Context context, Keyboard keyboard, Key key) {
+ private CharSequence getDescriptionForKeyCode(Context context, Keyboard keyboard, Key key,
+ boolean shouldObscure) {
final int code = getCorrectKeyCode(keyboard, key);
if (keyboard.isShiftLocked() && mShiftLockedKeyCodeMap.containsKey(code)) {
return context.getString(mShiftLockedKeyCodeMap.get(code));
} else if (keyboard.isShiftedOrShiftLocked() && mShiftedKeyCodeMap.containsKey(code)) {
return context.getString(mShiftedKeyCodeMap.get(code));
- } else if (mKeyCodeMap.containsKey(code)) {
+ }
+
+ // If the key description should be obscured, now is the time to do it.
+ final boolean isDefinedNonCtrl = Character.isDefined(code) && !Character.isISOControl(code);
+ if (shouldObscure && isDefinedNonCtrl) {
+ return context.getString(OBSCURED_KEY_RES_ID);
+ }
+
+ if (mKeyCodeMap.containsKey(code)) {
return context.getString(mKeyCodeMap.get(code));
- } else if (Character.isDefined(code) && !Character.isISOControl(code)) {
+ } else if (isDefinedNonCtrl) {
return Character.toString((char) code);
} else {
return context.getString(R.string.spoken_description_unknown, code);
diff --git a/java/src/com/android/inputmethod/compat/AudioManagerCompatWrapper.java b/java/src/com/android/inputmethod/compat/AudioManagerCompatWrapper.java
new file mode 100644
index 000000000..b6c3e2a88
--- /dev/null
+++ b/java/src/com/android/inputmethod/compat/AudioManagerCompatWrapper.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2011 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.compat;
+
+import android.media.AudioManager;
+
+import java.lang.reflect.Method;
+
+public class AudioManagerCompatWrapper {
+ private static final Method METHOD_isWiredHeadsetOn = CompatUtils.getMethod(
+ AudioManager.class, "isWiredHeadsetOn");
+ private static final Method METHOD_isBluetoothA2dpOn = CompatUtils.getMethod(
+ AudioManager.class, "isBluetoothA2dpOn");
+
+ private final AudioManager mManager;
+
+ public AudioManagerCompatWrapper(AudioManager manager) {
+ mManager = manager;
+ }
+
+ /**
+ * Checks whether audio routing to the wired headset is on or off.
+ *
+ * @return true if audio is being routed to/from wired headset;
+ * false if otherwise
+ */
+ public boolean isWiredHeadsetOn() {
+ return (Boolean) CompatUtils.invoke(mManager, false, METHOD_isWiredHeadsetOn);
+ }
+
+ /**
+ * Checks whether A2DP audio routing to the Bluetooth headset is on or off.
+ *
+ * @return true if A2DP audio is being routed to/from Bluetooth headset;
+ * false if otherwise
+ */
+ public boolean isBluetoothA2dpOn() {
+ return (Boolean) CompatUtils.invoke(mManager, false, METHOD_isBluetoothA2dpOn);
+ }
+}
diff --git a/java/src/com/android/inputmethod/latin/CandidateView.java b/java/src/com/android/inputmethod/latin/CandidateView.java
index 0640fd0b1..f499bc0bb 100644
--- a/java/src/com/android/inputmethod/latin/CandidateView.java
+++ b/java/src/com/android/inputmethod/latin/CandidateView.java
@@ -278,7 +278,6 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo
private final ArrayList<CharSequence> mTexts = new ArrayList<CharSequence>();
- public final boolean mAutoCorrectionVisualFlashEnabled;
public boolean mMoreSuggestionsAvailable;
public SuggestionsStripParams(Context context, AttributeSet attrs, int defStyle,
@@ -286,8 +285,6 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo
super(words, dividers, infos);
final TypedArray a = context.obtainStyledAttributes(
attrs, R.styleable.CandidateView, defStyle, R.style.CandidateViewStyle);
- mAutoCorrectionVisualFlashEnabled = a.getBoolean(
- R.styleable.CandidateView_autoCorrectionVisualFlashEnabled, false);
mAutoCorrectHighlight = a.getInt(R.styleable.CandidateView_autoCorrectHighlight, 0);
mColorTypedWord = a.getColor(R.styleable.CandidateView_colorTypedWord, 0);
mColorAutoCorrect = a.getColor(R.styleable.CandidateView_colorAutoCorrect, 0);
@@ -584,7 +581,7 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo
final int width = getWidth();
final int countInStrip = mStripParams.layout(
mSuggestions, mCandidatesStrip, mCandidatesPane, width);
- final int countInPane = mPaneParams.layout(
+ mPaneParams.layout(
mSuggestions, mCandidatesPane, countInStrip, mStripParams.getTextColor(), width);
}
@@ -633,6 +630,7 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo
private static CharSequence getEllipsizedText(CharSequence text, int maxWidth,
TextPaint paint) {
+ if (text == null) return null;
paint.setTextScaleX(1.0f);
final int width = getTextWidth(text, paint);
if (width <= maxWidth) {
@@ -703,9 +701,6 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo
}
public void onAutoCorrectionInverted(CharSequence autoCorrectedWord) {
- if (!mStripParams.mAutoCorrectionVisualFlashEnabled) {
- return;
- }
final CharSequence inverted = mStripParams.getInvertedText(autoCorrectedWord);
if (inverted == null)
return;
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
index c71841042..649774d78 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
@@ -16,6 +16,7 @@
package com.android.inputmethod.latin.spellcheck;
+import android.content.Intent;
import android.content.res.Resources;
import android.service.textservice.SpellCheckerService;
import android.service.textservice.SpellCheckerService.Session;
@@ -48,7 +49,7 @@ public class AndroidSpellCheckerService extends SpellCheckerService {
private static final int POOL_SIZE = 2;
private final static String[] emptyArray = new String[0];
- private final Map<String, DictionaryPool> mDictionaryPools =
+ private Map<String, DictionaryPool> mDictionaryPools =
Collections.synchronizedMap(new TreeMap<String, DictionaryPool>());
@Override
@@ -104,6 +105,16 @@ public class AndroidSpellCheckerService extends SpellCheckerService {
}
}
+ @Override
+ public boolean onUnbind(final Intent intent) {
+ final Map<String, DictionaryPool> oldPools = mDictionaryPools;
+ mDictionaryPools = Collections.synchronizedMap(new TreeMap<String, DictionaryPool>());
+ for (DictionaryPool pool : oldPools.values()) {
+ pool.close();
+ }
+ return false;
+ }
+
private DictionaryPool getDictionaryPool(final String locale) {
DictionaryPool pool = mDictionaryPools.get(locale);
if (null == pool) {
@@ -167,7 +178,9 @@ public class AndroidSpellCheckerService extends SpellCheckerService {
dictInfo.mDictionary.getWords(composer, suggestionsGatherer,
dictInfo.mProximityInfo);
isInDict = dictInfo.mDictionary.isValidWord(text);
- mDictionaryPool.offer(dictInfo);
+ if (!mDictionaryPool.offer(dictInfo)) {
+ Log.e(TAG, "Can't re-insert a dictionary into its pool");
+ }
} catch (InterruptedException e) {
// I don't think this can happen.
return new SuggestionsInfo(0, new String[0]);
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java b/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java
index dfbfcc7f6..ee294f6b0 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java
@@ -28,7 +28,8 @@ public class DictionaryPool extends LinkedBlockingQueue<DictAndProximity> {
private final AndroidSpellCheckerService mService;
private final int mMaxSize;
private final Locale mLocale;
- private int mSize = 0;
+ private int mSize;
+ private volatile boolean mClosed;
public DictionaryPool(final int maxSize, final AndroidSpellCheckerService service,
final Locale locale) {
@@ -36,6 +37,8 @@ public class DictionaryPool extends LinkedBlockingQueue<DictAndProximity> {
mMaxSize = maxSize;
mService = service;
mLocale = locale;
+ mSize = 0;
+ mClosed = false;
}
@Override
@@ -52,4 +55,24 @@ public class DictionaryPool extends LinkedBlockingQueue<DictAndProximity> {
}
}
}
+
+ public void close() {
+ synchronized(this) {
+ mClosed = true;
+ for (DictAndProximity dict : this) {
+ dict.mDictionary.close();
+ }
+ clear();
+ }
+ }
+
+ @Override
+ public boolean offer(final DictAndProximity dict) {
+ if (mClosed) {
+ dict.mDictionary.close();
+ return false;
+ } else {
+ return super.offer(dict);
+ }
+ }
}
diff --git a/native/src/correction.cpp b/native/src/correction.cpp
index a4090a966..99412b211 100644
--- a/native/src/correction.cpp
+++ b/native/src/correction.cpp
@@ -95,10 +95,8 @@ int Correction::getFinalFreq(const int freq, unsigned short **word, int *wordLen
}
*word = mWord;
- const bool sameLength = (mExcessivePos == mInputLength - 1) ? (mInputLength == inputIndex + 2)
- : (mInputLength == inputIndex + 1);
return Correction::RankingAlgorithm::calculateFinalFreq(
- inputIndex, outputIndex, freq, sameLength, mEditDistanceTable, this);
+ inputIndex, outputIndex, freq, mEditDistanceTable, this);
}
bool Correction::initProcessState(const int outputIndex) {
@@ -205,20 +203,6 @@ Correction::CorrectionType Correction::processCharAndCalcState(
}
if (mNeedsToTraverseAllNodes || isQuote(c)) {
- const bool checkProximityChars =
- !(mSkippedCount > 0 || mExcessivePos >= 0 || mTransposedPos >= 0);
- // 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.
- if (checkProximityChars
- && mInputIndex > 0
- && mCorrectionStates[mOutputIndex].mProximityMatching
- && mCorrectionStates[mOutputIndex].mSkipping
- && mProximityInfo->getMatchedProximityId(
- mInputIndex - 1, c, false)
- == ProximityInfo::SAME_OR_ACCENTED_OR_CAPITALIZED_CHAR) {
- ++mSkippedCount;
- --mProximityCount;
- }
return processSkipChar(c, isTerminal);
} else {
int inputIndexForProximity = mInputIndex;
@@ -250,6 +234,8 @@ Correction::CorrectionType Correction::processCharAndCalcState(
&& mProximityInfo->getMatchedProximityId(
inputIndexForProximity - 1, c, false)
== ProximityInfo::SAME_OR_ACCENTED_OR_CAPITALIZED_CHAR) {
+ // 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.
++mSkippedCount;
--mProximityCount;
return processSkipChar(c, isTerminal);
@@ -344,6 +330,16 @@ inline static void multiplyRate(const int rate, int *freq) {
}
}
+inline static int getQuoteCount(const unsigned short* word, const int length) {
+ int quoteCount = 0;
+ for (int i = 0; i < length; ++i) {
+ if(word[i] == '\'') {
+ ++quoteCount;
+ }
+ }
+ return quoteCount;
+}
+
/* static */
inline static int editDistance(
int* editDistanceTable, const unsigned short* input,
@@ -392,8 +388,7 @@ inline static int editDistance(
/* static */
int Correction::RankingAlgorithm::calculateFinalFreq(const int inputIndex, const int outputIndex,
- const int freq, const bool sameLength, int* editDistanceTable,
- const Correction* correction) {
+ const int freq, int* editDistanceTable, const Correction* correction) {
const int excessivePos = correction->getExcessivePos();
const int transposedPos = correction->getTransposedPos();
const int inputLength = correction->mInputLength;
@@ -402,6 +397,12 @@ int Correction::RankingAlgorithm::calculateFinalFreq(const int inputIndex, const
const ProximityInfo *proximityInfo = correction->mProximityInfo;
const int skipCount = correction->mSkippedCount;
const int proximityMatchedCount = correction->mProximityCount;
+ if (skipCount >= inputLength || inputLength == 0) {
+ return -1;
+ }
+ const bool sameLength = (excessivePos == inputLength - 1) ? (inputLength == inputIndex + 2)
+ : (inputLength == inputIndex + 1);
+
// TODO: use mExcessiveCount
int matchCount = inputLength - correction->mProximityCount - (excessivePos >= 0 ? 1 : 0);
@@ -409,67 +410,52 @@ int Correction::RankingAlgorithm::calculateFinalFreq(const int inputIndex, const
const unsigned short* word = correction->mWord;
const bool skipped = skipCount > 0;
- // ----- TODO: use edit distance here as follows? ---------------------- /
- //if (!skipped && excessivePos < 0 && transposedPos < 0) {
- // const int ed = editDistance(dp, proximityInfo->getInputWord(),
- // inputLength, word, outputIndex + 1);
- // matchCount = outputIndex + 1 - ed;
- // if (ed == 1 && !sameLength) ++matchCount;
- //}
- // const int ed = editDistance(dp, proximityInfo->getInputWord(),
- // inputLength, word, outputIndex + 1);
- // if (ed == 1 && !sameLength) ++matchCount; ------------------------ /
- int matchWeight = powerIntCapped(typedLetterMultiplier, matchCount);
+ const int quoteDiffCount = max(0, getQuoteCount(word, outputIndex + 1)
+ - getQuoteCount(proximityInfo->getPrimaryInputWord(), inputLength));
- // TODO: Demote by edit distance
- int finalFreq = freq * matchWeight;
- // +1 +11/-12
- /*if (inputLength == outputIndex && !skipped && excessivePos < 0 && transposedPos < 0) {
- const int ed = editDistance(dp, proximityInfo->getInputWord(),
+ // TODO: Calculate edit distance for transposed and excessive
+ int matchWeight;
+ int ed = 0;
+ int adJustedProximityMatchedCount = proximityMatchedCount;
+ if (excessivePos < 0 && transposedPos < 0 && (proximityMatchedCount > 0 || skipped)) {
+ const unsigned short* primaryInputWord = proximityInfo->getPrimaryInputWord();
+ ed = editDistance(editDistanceTable, primaryInputWord,
inputLength, word, outputIndex + 1);
- if (ed == 1) {
- multiplyRate(160, &finalFreq);
- }
- }*/
- if (inputLength == outputIndex && excessivePos < 0 && transposedPos < 0
- && (proximityMatchedCount > 0 || skipped)) {
- const int ed = editDistance(editDistanceTable, proximityInfo->getPrimaryInputWord(),
- inputLength, word, outputIndex + 1);
- if (ed == 1) {
- multiplyRate(160, &finalFreq);
+ matchWeight = powerIntCapped(typedLetterMultiplier, outputIndex + 1 - ed);
+ if (ed == 1 && inputLength == outputIndex) {
+ // Promote a word with just one skipped char
+ multiplyRate(WORDS_WITH_JUST_ONE_CORRECTION_PROMOTION_RATE, &matchWeight);
}
+ ed = max(0, ed - quoteDiffCount);
+ adJustedProximityMatchedCount = min(max(0, ed - (outputIndex + 1 - inputLength)),
+ proximityMatchedCount);
+ } else {
+ matchWeight = powerIntCapped(typedLetterMultiplier, matchCount);
}
- // TODO: Promote properly?
- //if (skipCount == 1 && excessivePos < 0 && transposedPos < 0 && inputLength == outputIndex
- // && !sameLength) {
- // multiplyRate(150, &finalFreq);
- //}
- //if (skipCount == 0 && excessivePos < 0 && transposedPos < 0 && inputLength == outputIndex
- // && !sameLength) {
- // multiplyRate(150, &finalFreq);
- //}
- //if (skipCount == 0 && excessivePos < 0 && transposedPos < 0
- // && inputLength == outputIndex + 1) {
- // multiplyRate(150, &finalFreq);
- //}
+ // TODO: Demote by edit distance
+ int finalFreq = freq * matchWeight;
+
+ ///////////////////////////////////////////////
+ // Promotion and Demotion for each correction
+ // Demotion for a word with missing character
if (skipped) {
- if (inputLength >= 2) {
- const int demotionRate = WORDS_WITH_MISSING_CHARACTER_DEMOTION_RATE
- * (10 * inputLength - WORDS_WITH_MISSING_CHARACTER_DEMOTION_START_POS_10X)
- / (10 * inputLength
- - WORDS_WITH_MISSING_CHARACTER_DEMOTION_START_POS_10X + 10);
- if (DEBUG_DICT_FULL) {
- LOGI("Demotion rate for missing character is %d.", demotionRate);
- }
- multiplyRate(demotionRate, &finalFreq);
- } else {
- finalFreq = 0;
+ const int demotionRate = WORDS_WITH_MISSING_CHARACTER_DEMOTION_RATE
+ * (10 * inputLength - WORDS_WITH_MISSING_CHARACTER_DEMOTION_START_POS_10X)
+ / (10 * inputLength
+ - WORDS_WITH_MISSING_CHARACTER_DEMOTION_START_POS_10X + 10);
+ if (DEBUG_DICT_FULL) {
+ LOGI("Demotion rate for missing character is %d.", demotionRate);
}
+ multiplyRate(demotionRate, &finalFreq);
}
+
+ // Demotion for a word with transposed character
if (transposedPos >= 0) multiplyRate(
WORDS_WITH_TRANSPOSED_CHARACTERS_DEMOTION_RATE, &finalFreq);
+
+ // Demotion for a word with excessive character
if (excessivePos >= 0) {
multiplyRate(WORDS_WITH_EXCESSIVE_CHARACTER_DEMOTION_RATE, &finalFreq);
if (!proximityInfo->existsAdjacentProximityChars(inputIndex)) {
@@ -478,52 +464,62 @@ int Correction::RankingAlgorithm::calculateFinalFreq(const int inputIndex, const
multiplyRate(WORDS_WITH_EXCESSIVE_CHARACTER_OUT_OF_PROXIMITY_DEMOTION_RATE, &finalFreq);
}
}
- int lengthFreq = typedLetterMultiplier;
- multiplyIntCapped(powerIntCapped(typedLetterMultiplier, outputIndex), &lengthFreq);
- if ((outputIndex + 1) == matchCount) {
- // Full exact match
- if (outputIndex > 1) {
- if (DEBUG_DICT) {
- LOGI("Found full matched word.");
- }
- multiplyRate(FULL_MATCHED_WORDS_PROMOTION_RATE, &finalFreq);
- }
- if (sameLength && transposedPos < 0 && !skipped && excessivePos < 0) {
- finalFreq = capped255MultForFullMatchAccentsOrCapitalizationDifference(finalFreq);
- }
- } else if (sameLength && transposedPos < 0 && !skipped && excessivePos < 0
- && outputIndex > 0) {
+
+ // Promotion for a word with proximity characters
+ for (int i = 0; i < adJustedProximityMatchedCount; ++i) {
// A word with proximity corrections
- if (DEBUG_DICT) {
- LOGI("Found one proximity correction.");
+ if (DEBUG_DICT_FULL) {
+ LOGI("Found a proximity correction.");
}
multiplyIntCapped(typedLetterMultiplier, &finalFreq);
multiplyRate(WORDS_WITH_PROXIMITY_CHARACTER_DEMOTION_RATE, &finalFreq);
}
- if (DEBUG_DICT_FULL) {
- LOGI("calc: %d, %d", outputIndex, sameLength);
+
+ const int errorCount = proximityMatchedCount + skipCount;
+ multiplyRate(
+ 100 - CORRECTION_COUNT_RATE_DEMOTION_RATE_BASE * errorCount / inputLength, &finalFreq);
+
+ // Promotion for an exactly matched word
+ if (matchCount == outputIndex + 1) {
+ // Full exact match
+ if (sameLength && transposedPos < 0 && !skipped && excessivePos < 0) {
+ finalFreq = capped255MultForFullMatchAccentsOrCapitalizationDifference(finalFreq);
+ }
}
- if (sameLength) multiplyIntCapped(fullWordMultiplier, &finalFreq);
- // TODO: check excessive count and transposed count
+ // Promote a word with no correction
+ if (proximityMatchedCount == 0 && transposedPos < 0 && !skipped && excessivePos < 0) {
+ multiplyRate(FULL_MATCHED_WORDS_PROMOTION_RATE, &finalFreq);
+ }
+
+ // TODO: Check excessive count and transposed count
+ // TODO: Remove this if possible
/*
- If the last character of the user input word is the same as the next character
- of the output word, and also all of characters of the user input are matched
- to the output word, we'll promote that word a bit because
- that word can be considered the combination of skipped and matched characters.
- This means that the 'sm' pattern wins over the 'ma' pattern.
- e.g.)
- shel -> shell [mmmma] or [mmmsm]
- hel -> hello [mmmaa] or [mmsma]
- m ... matching
- s ... skipping
- a ... traversing all
+ If the last character of the user input word is the same as the next character
+ of the output word, and also all of characters of the user input are matched
+ to the output word, we'll promote that word a bit because
+ that word can be considered the combination of skipped and matched characters.
+ This means that the 'sm' pattern wins over the 'ma' pattern.
+ e.g.)
+ shel -> shell [mmmma] or [mmmsm]
+ hel -> hello [mmmaa] or [mmsma]
+ m ... matching
+ s ... skipping
+ a ... traversing all
*/
if (matchCount == inputLength && matchCount >= 2 && !skipped
&& word[matchCount] == word[matchCount - 1]) {
multiplyRate(WORDS_WITH_MATCH_SKIP_PROMOTION_RATE, &finalFreq);
}
+ if (sameLength) {
+ multiplyIntCapped(fullWordMultiplier, &finalFreq);
+ }
+
+ if (DEBUG_DICT_FULL) {
+ LOGI("calc: %d, %d", outputIndex, sameLength);
+ }
+
return finalFreq;
}
diff --git a/native/src/correction.h b/native/src/correction.h
index 9d385a44e..871a04251 100644
--- a/native/src/correction.h
+++ b/native/src/correction.h
@@ -139,8 +139,7 @@ private:
class RankingAlgorithm {
public:
static int calculateFinalFreq(const int inputIndex, const int depth,
- const int freq, const bool sameLength, int *editDistanceTable,
- const Correction* correction);
+ const int freq, int *editDistanceTable, const Correction* correction);
static int calcFreqForSplitTwoWords(const int firstFreq, const int secondFreq,
const Correction* correction);
};
diff --git a/native/src/defines.h b/native/src/defines.h
index c1d08e695..a29fb7e5b 100644
--- a/native/src/defines.h
+++ b/native/src/defines.h
@@ -177,6 +177,8 @@ static void dumpWord(const unsigned short* word, const int length) {
#define FULL_MATCHED_WORDS_PROMOTION_RATE 120
#define WORDS_WITH_PROXIMITY_CHARACTER_DEMOTION_RATE 90
#define WORDS_WITH_MATCH_SKIP_PROMOTION_RATE 105
+#define WORDS_WITH_JUST_ONE_CORRECTION_PROMOTION_RATE 160
+#define CORRECTION_COUNT_RATE_DEMOTION_RATE_BASE 42
// This should be greater than or equal to MAX_WORD_LENGTH defined in BinaryDictionary.java
// This is only used for the size of array. Not to be used in c functions.
@@ -194,5 +196,6 @@ static void dumpWord(const unsigned short* word, const int length) {
#define MIN_USER_TYPED_LENGTH_FOR_EXCESSIVE_CHARACTER_SUGGESTION 3
#define min(a,b) ((a)<(b)?(a):(b))
+#define max(a,b) ((a)>(b)?(a):(b))
#endif // LATINIME_DEFINES_H