diff options
33 files changed, 315 insertions, 364 deletions
diff --git a/java-overridable/src/com/android/inputmethod/latin/accounts/AccountStateChangedListener.java b/java-overridable/src/com/android/inputmethod/latin/accounts/AccountStateChangedListener.java new file mode 100644 index 000000000..c0a599c6e --- /dev/null +++ b/java-overridable/src/com/android/inputmethod/latin/accounts/AccountStateChangedListener.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.latin.accounts; + +import android.support.annotation.NonNull; + +import javax.annotation.Nullable; + +/** + * Handles changes to account used to sign in to the keyboard. + * e.g. account switching/sign-in/sign-out from the keyboard + * user toggling the sync preference. + */ +public class AccountStateChangedListener { + + /** + * Called when the current account being used in keyboard is signed out. + * + * @param oldAccount the account that was signed out of. + */ + public static void onAccountSignedOut(@NonNull String oldAccount) { + } + + /** + * Called when the user signs-in to the keyboard. + * This may be called when the user switches accounts to sign in with a different account. + * + * @param oldAccount the previous account that was being used for sign-in. + * May be null for a fresh sign-in. + * @param newAccount the account being used for sign-in. + */ + public static void onAccountSignedIn(@Nullable String oldAccount, @NonNull String newAccount) { + } + + /** + * Called when the user toggles the sync preference. + * + * @param account the account being used for sync. + * @param syncEnabled indicates whether sync has been enabled or not. + */ + public static void onSyncPreferenceChanged(@Nullable String account, boolean syncEnabled) { + } + + /** + * Forces an immediate sync to happen. + * This should only be used for debugging purposes. + * + * @param account the account to use for sync. + */ + public static void forceSync(@Nullable String account) { + } +} diff --git a/java-overridable/src/com/android/inputmethod/latin/settings/AdditionalFeaturesSettingUtils.java b/java-overridable/src/com/android/inputmethod/latin/settings/AdditionalFeaturesSettingUtils.java index 747a3b06e..f9ebb9702 100644 --- a/java-overridable/src/com/android/inputmethod/latin/settings/AdditionalFeaturesSettingUtils.java +++ b/java-overridable/src/com/android/inputmethod/latin/settings/AdditionalFeaturesSettingUtils.java @@ -18,6 +18,7 @@ package com.android.inputmethod.latin.settings; import android.content.Context; import android.content.SharedPreferences; +import android.content.res.Resources; import android.preference.PreferenceFragment; import android.view.inputmethod.InputMethodSubtype; @@ -48,7 +49,8 @@ public class AdditionalFeaturesSettingUtils { public static RichInputMethodSubtype createRichInputMethodSubtype( @Nonnull final RichInputMethodManager imm, - @Nonnull final InputMethodSubtype subtype) { + @Nonnull final InputMethodSubtype subtype, + final Resources resources) { return new RichInputMethodSubtype(subtype); } } diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java index 52b9284be..1c66c37d3 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java @@ -28,6 +28,7 @@ import android.util.Log; import android.util.SparseArray; import android.util.Xml; import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputMethodSubtype; import com.android.inputmethod.compat.EditorInfoCompatUtils; import com.android.inputmethod.compat.InputMethodSubtypeCompatUtils; @@ -39,6 +40,7 @@ import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.RichInputMethodSubtype; import com.android.inputmethod.latin.SubtypeSwitcher; import com.android.inputmethod.latin.define.DebugFlags; +import com.android.inputmethod.latin.utils.DebugLogUtils; import com.android.inputmethod.latin.utils.InputTypeUtils; import com.android.inputmethod.latin.utils.ScriptUtils; import com.android.inputmethod.latin.utils.SubtypeLocaleUtils; @@ -313,23 +315,78 @@ public final class KeyboardLayoutSet { return this; } + private final static HashMap<InputMethodSubtype, Integer> sScriptIdsForSubtypes = + new HashMap<>(); + public static int getScriptId(final Resources resources, final InputMethodSubtype subtype) { + final Integer value = sScriptIdsForSubtypes.get(subtype); + if (null == value) { + final int scriptId = readScriptId(resources, subtype); + sScriptIdsForSubtypes.put(subtype, scriptId); + return scriptId; + } + return value; + } + + // Super redux version of reading the script ID for some subtype from Xml. + private static int readScriptId(final Resources resources, + final InputMethodSubtype subtype) { + final String layoutSetName = KEYBOARD_LAYOUT_SET_RESOURCE_PREFIX + + SubtypeLocaleUtils.getKeyboardLayoutSetName(subtype); + final int xmlId = getXmlId(resources, layoutSetName); + final XmlResourceParser parser = resources.getXml(xmlId); + try { + while (parser.getEventType() != XmlPullParser.END_DOCUMENT) { + // Bovinate through the XML stupidly searching for TAG_FEATURE, and read + // the script Id from it. + parser.next(); + final String tag = parser.getName(); + if (TAG_FEATURE.equals(tag)) { + return readScriptIdFromTagFeature(resources, parser); + } + } + } catch (final IOException | XmlPullParserException e) { + throw new RuntimeException(e.getMessage() + " in " + layoutSetName, e); + } finally { + parser.close(); + } + // If the tag is not found, then the default script is Latin. + return ScriptUtils.SCRIPT_LATIN; + } + + private static int readScriptIdFromTagFeature(final Resources resources, + final XmlPullParser parser) throws IOException, XmlPullParserException { + final TypedArray featureAttr = resources.obtainAttributes(Xml.asAttributeSet(parser), + R.styleable.KeyboardLayoutSet_Feature); + try { + final int scriptId = + featureAttr.getInt(R.styleable.KeyboardLayoutSet_Feature_supportedScript, + ScriptUtils.SCRIPT_UNKNOWN); + XmlParseUtils.checkEndTag(TAG_FEATURE, parser); + return scriptId; + } finally { + featureAttr.recycle(); + } + } + public KeyboardLayoutSet build() { if (mParams.mSubtype == null) throw new RuntimeException("KeyboardLayoutSet subtype is not specified"); - final String packageName = mResources.getResourcePackageName( - R.xml.keyboard_layout_set_qwerty); - final String keyboardLayoutSetName = mParams.mKeyboardLayoutSetName; - final int xmlId = mResources.getIdentifier(keyboardLayoutSetName, "xml", packageName); + final int xmlId = getXmlId(mResources, mParams.mKeyboardLayoutSetName); try { parseKeyboardLayoutSet(mResources, xmlId); - } catch (final IOException e) { - throw new RuntimeException(e.getMessage() + " in " + keyboardLayoutSetName, e); - } catch (final XmlPullParserException e) { - throw new RuntimeException(e.getMessage() + " in " + keyboardLayoutSetName, e); + } catch (final IOException | XmlPullParserException e) { + throw new RuntimeException(e.getMessage() + " in " + mParams.mKeyboardLayoutSetName, + e); } return new KeyboardLayoutSet(mContext, mParams); } + private static int getXmlId(final Resources resources, final String keyboardLayoutSetName) { + final String packageName = resources.getResourcePackageName( + R.xml.keyboard_layout_set_qwerty); + return resources.getIdentifier(keyboardLayoutSetName, "xml", packageName); + } + private void parseKeyboardLayoutSet(final Resources res, final int resId) throws XmlPullParserException, IOException { final XmlResourceParser parser = res.getXml(resId); @@ -407,17 +464,8 @@ public final class KeyboardLayoutSet { private void parseKeyboardLayoutSetFeature(final XmlPullParser parser) throws XmlPullParserException, IOException { - final TypedArray a = mResources.obtainAttributes(Xml.asAttributeSet(parser), - R.styleable.KeyboardLayoutSet_Feature); - try { - final int scriptId = a.getInt( - R.styleable.KeyboardLayoutSet_Feature_supportedScript, - ScriptUtils.SCRIPT_LATIN); - XmlParseUtils.checkEndTag(TAG_FEATURE, parser); - setScriptId(scriptId); - } finally { - a.recycle(); - } + final int scriptId = readScriptIdFromTagFeature(mResources, parser); + setScriptId(scriptId); } private static int getKeyboardMode(final EditorInfo editorInfo) { diff --git a/java/src/com/android/inputmethod/keyboard/TextDecorator.java b/java/src/com/android/inputmethod/keyboard/TextDecorator.java index ddc65bf36..2b7d2ce3c 100644 --- a/java/src/com/android/inputmethod/keyboard/TextDecorator.java +++ b/java/src/com/android/inputmethod/keyboard/TextDecorator.java @@ -303,7 +303,7 @@ public class TextDecorator { */ private static final class LayoutInvalidator { private final HandlerImpl mHandler; - public LayoutInvalidator(final TextDecorator ownerInstance) { + public LayoutInvalidator(@Nonnull final TextDecorator ownerInstance) { mHandler = new HandlerImpl(ownerInstance); } @@ -311,7 +311,7 @@ public class TextDecorator { private static final class HandlerImpl extends LeakGuardHandlerWrapper<TextDecorator> { - public HandlerImpl(final TextDecorator ownerInstance) { + public HandlerImpl(@Nonnull final TextDecorator ownerInstance) { super(ownerInstance); } diff --git a/java/src/com/android/inputmethod/keyboard/internal/DrawingHandler.java b/java/src/com/android/inputmethod/keyboard/internal/DrawingHandler.java index 4f8a105d5..1a55359f5 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/DrawingHandler.java +++ b/java/src/com/android/inputmethod/keyboard/internal/DrawingHandler.java @@ -23,6 +23,8 @@ import com.android.inputmethod.keyboard.internal.DrawingHandler.Callbacks; import com.android.inputmethod.latin.SuggestedWords; import com.android.inputmethod.latin.utils.LeakGuardHandlerWrapper; +import javax.annotation.Nonnull; + // TODO: Separate this class into KeyPreviewHandler and BatchInputPreviewHandler or so. public class DrawingHandler extends LeakGuardHandlerWrapper<Callbacks> { public interface Callbacks { @@ -34,7 +36,7 @@ public class DrawingHandler extends LeakGuardHandlerWrapper<Callbacks> { private static final int MSG_DISMISS_KEY_PREVIEW = 0; private static final int MSG_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT = 1; - public DrawingHandler(final Callbacks ownerInstance) { + public DrawingHandler(@Nonnull final Callbacks ownerInstance) { super(ownerInstance); } diff --git a/java/src/com/android/inputmethod/keyboard/internal/TimerHandler.java b/java/src/com/android/inputmethod/keyboard/internal/TimerHandler.java index ec7b9b024..80b299bf5 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/TimerHandler.java +++ b/java/src/com/android/inputmethod/keyboard/internal/TimerHandler.java @@ -27,6 +27,8 @@ import com.android.inputmethod.keyboard.internal.TimerHandler.Callbacks; import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.utils.LeakGuardHandlerWrapper; +import javax.annotation.Nonnull; + // TODO: Separate this class into KeyTimerHandler and BatchInputTimerHandler or so. public final class TimerHandler extends LeakGuardHandlerWrapper<Callbacks> implements TimerProxy { public interface Callbacks { @@ -45,7 +47,7 @@ public final class TimerHandler extends LeakGuardHandlerWrapper<Callbacks> imple private final int mIgnoreAltCodeKeyTimeout; private final int mGestureRecognitionUpdateTime; - public TimerHandler(final Callbacks ownerInstance, final int ignoreAltCodeKeyTimeout, + public TimerHandler(@Nonnull final Callbacks ownerInstance, final int ignoreAltCodeKeyTimeout, final int gestureRecognitionUpdateTime) { super(ownerInstance); mIgnoreAltCodeKeyTimeout = ignoreAltCodeKeyTimeout; diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 77016cb8b..f968a6c52 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -110,6 +110,8 @@ import java.util.List; import java.util.Locale; import java.util.concurrent.TimeUnit; +import javax.annotation.Nonnull; + /** * Input method implementation for Qwerty'ish keyboard. */ @@ -208,7 +210,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private int mDelayInMillisecondsToUpdateSuggestions; private int mDelayInMillisecondsToUpdateShiftState; - public UIHandler(final LatinIME ownerInstance) { + public UIHandler(@Nonnull final LatinIME ownerInstance) { super(ownerInstance); } @@ -1073,7 +1075,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // with cursor movement when we have a hardware keyboard since we are not in charge. final SettingsValues settingsValues = mSettings.getCurrent(); if ((!settingsValues.mHasHardwareKeyboard || ProductionFlags.IS_HARDWARE_KEYBOARD_SUPPORTED) - && mInputLogic.onUpdateSelection(oldSelStart, oldSelEnd, newSelStart, newSelEnd)) { + && mInputLogic.onUpdateSelection(oldSelStart, oldSelEnd, newSelStart, newSelEnd, + settingsValues)) { mKeyboardSwitcher.requestUpdatingShiftState(getCurrentAutoCapsState(), getCurrentRecapitalizeState()); } diff --git a/java/src/com/android/inputmethod/latin/RichInputMethodManager.java b/java/src/com/android/inputmethod/latin/RichInputMethodManager.java index 3fcae58f1..b0c0725bb 100644 --- a/java/src/com/android/inputmethod/latin/RichInputMethodManager.java +++ b/java/src/com/android/inputmethod/latin/RichInputMethodManager.java @@ -20,6 +20,7 @@ import static com.android.inputmethod.latin.Constants.Subtype.KEYBOARD_MODE; import android.content.Context; import android.content.SharedPreferences; +import android.content.res.Resources; import android.os.Build; import android.os.IBinder; import android.preference.PreferenceManager; @@ -51,6 +52,7 @@ public class RichInputMethodManager { private static final RichInputMethodManager sInstance = new RichInputMethodManager(); + private Resources mResources; private InputMethodManagerCompatWrapper mImmWrapper; private InputMethodInfoCache mInputMethodInfoCache; final HashMap<InputMethodInfo, List<InputMethodSubtype>> @@ -84,6 +86,7 @@ public class RichInputMethodManager { return; } mImmWrapper = new InputMethodManagerCompatWrapper(context); + mResources = context.getResources(); mInputMethodInfoCache = new InputMethodInfoCache( mImmWrapper.mImm, context.getPackageName()); @@ -305,7 +308,8 @@ public class RichInputMethodManager { public RichInputMethodSubtype createCurrentRichInputMethodSubtype( final InputMethodSubtype rawSubtype) { - return AdditionalFeaturesSettingUtils.createRichInputMethodSubtype(this, rawSubtype); + return AdditionalFeaturesSettingUtils.createRichInputMethodSubtype(this, rawSubtype, + mResources); } public boolean hasMultipleEnabledIMEsOrSubtypes(final boolean shouldIncludeAuxiliarySubtypes) { diff --git a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java index 0d742e96d..13f79b49f 100644 --- a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java +++ b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java @@ -36,7 +36,6 @@ import com.android.inputmethod.compat.InputMethodSubtypeCompatUtils; import com.android.inputmethod.keyboard.KeyboardSwitcher; import com.android.inputmethod.keyboard.internal.LanguageOnSpacebarHelper; import com.android.inputmethod.latin.define.DebugFlags; -import com.android.inputmethod.latin.utils.LocaleUtils; import com.android.inputmethod.latin.utils.SubtypeLocaleUtils; import java.util.HashSet; diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java index f67b8de84..5cc61db79 100644 --- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java +++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java @@ -371,10 +371,11 @@ public final class InputLogic { * @param oldSelEnd old selection end * @param newSelStart new selection start * @param newSelEnd new selection end + * @param settingsValues the current values of the settings. * @return whether the cursor has moved as a result of user interaction. */ public boolean onUpdateSelection(final int oldSelStart, final int oldSelEnd, - final int newSelStart, final int newSelEnd) { + final int newSelStart, final int newSelEnd, final SettingsValues settingsValues) { if (mConnection.isBelatedExpectedUpdate(oldSelStart, newSelStart, oldSelEnd, newSelEnd)) { return false; } @@ -399,8 +400,9 @@ public final class InputLogic { // should be true, but that is if the framework had taken that wrong cursor position // into account, which means we have to reset the entire composing state whenever there // is or was a selection regardless of whether it changed or not. - if (hasOrHadSelection || (selectionChangedOrSafeToReset - && !mWordComposer.moveCursorByAndReturnIfInsideComposingWord(moveAmount))) { + if (hasOrHadSelection || !settingsValues.needsToLookupSuggestions() + || (selectionChangedOrSafeToReset + && !mWordComposer.moveCursorByAndReturnIfInsideComposingWord(moveAmount))) { // If we are composing a word and moving the cursor, we would want to set a // suggestion span for recorrection to work correctly. Unfortunately, that // would involve the keyboard committing some new text, which would move the diff --git a/java/src/com/android/inputmethod/latin/settings/AccountsSettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/AccountsSettingsFragment.java index 5e6521fc4..4bd15d037 100644 --- a/java/src/com/android/inputmethod/latin/settings/AccountsSettingsFragment.java +++ b/java/src/com/android/inputmethod/latin/settings/AccountsSettingsFragment.java @@ -19,9 +19,7 @@ package com.android.inputmethod.latin.settings; import static com.android.inputmethod.latin.settings.LocalSettingsConstants.PREF_ACCOUNT_NAME; import static com.android.inputmethod.latin.settings.LocalSettingsConstants.PREF_ENABLE_CLOUD_SYNC; -import android.accounts.Account; import android.app.AlertDialog; -import android.content.ContentResolver; import android.content.Context; import android.content.DialogInterface; import android.content.SharedPreferences; @@ -37,6 +35,7 @@ import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.SubtypeSwitcher; import com.android.inputmethod.latin.accounts.LoginAccountUtils; +import com.android.inputmethod.latin.accounts.AccountStateChangedListener; import com.android.inputmethod.latin.define.ProductionFlags; import javax.annotation.Nullable; @@ -52,7 +51,6 @@ import javax.annotation.Nullable; public final class AccountsSettingsFragment extends SubScreenFragment { private static final String PREF_SYNC_NOW = "pref_beanstalk"; - @UsedForTesting static final String AUTHORITY = "com.android.inputmethod.latin.provider"; static final String PREF_ACCCOUNT_SWITCHER = "account_switcher"; private final DialogInterface.OnClickListener mAccountChangedListener = @@ -111,7 +109,8 @@ public final class AccountsSettingsFragment extends SubScreenFragment { prefs.getString(PREF_ACCOUNT_NAME, null)); } else if (TextUtils.equals(key, PREF_ENABLE_CLOUD_SYNC)) { final boolean syncEnabled = prefs.getBoolean(PREF_ENABLE_CLOUD_SYNC, false); - updateSyncPolicy(syncEnabled, getSignedInAccountName()); + AccountStateChangedListener.onSyncPreferenceChanged( + getSignedInAccountName(), syncEnabled); } } @@ -177,36 +176,6 @@ public final class AccountsSettingsFragment extends SubScreenFragment { syncPreference.setSummary(R.string.cloud_sync_summary_disabled_signed_out); } - /** - * Given a non-null accountToUse, this method looks at the enabled value to either - * set or unset the syncable property of the sync authority. - * If the account is null, this method is a no-op currently, but we may want - * to perform some cleanup in the future. - * - * @param enabled indicates whether the sync preference is enabled or not. - * @param accountToUse indicaes the account to be used for sync, or null if the user - * is not logged in. - */ - @UsedForTesting - void updateSyncPolicy(boolean enabled, @Nullable String accountToUse) { - if (!ProductionFlags.ENABLE_PERSONAL_DICTIONARY_SYNC) { - return; - } - - if (accountToUse != null) { - final int syncable = enabled ? 1 : 0; - ContentResolver.setIsSyncable( - new Account(accountToUse, LoginAccountUtils.ACCOUNT_TYPE), - AUTHORITY, syncable); - // TODO: Also add a periodic sync here. - // See ContentResolver.addPeriodicSync - } else { - // Without an account, we cannot really set the sync to off. - // Hopefully the account sign-out listener would have taken care of that for us. - // But cases such as clear data are still not handled cleanly. - } - } - @Nullable String getSignedInAccountName() { return getSharedPreferences().getString(LocalSettingsConstants.PREF_ACCOUNT_NAME, null); @@ -261,22 +230,20 @@ public final class AccountsSettingsFragment extends SubScreenFragment { class AccountChangedListener implements DialogInterface.OnClickListener { @Override public void onClick(DialogInterface dialog, int which) { + final String oldAccount = getSignedInAccountName(); switch (which) { case DialogInterface.BUTTON_POSITIVE: // Signed in final ListView lv = ((AlertDialog)dialog).getListView(); - final Object selectedItem = lv.getItemAtPosition(lv.getCheckedItemPosition()); + final String newAccount = + (String) lv.getItemAtPosition(lv.getCheckedItemPosition()); getSharedPreferences() .edit() - .putString(PREF_ACCOUNT_NAME, (String) selectedItem) + .putString(PREF_ACCOUNT_NAME, newAccount) .apply(); - // Attempt starting sync for the new account if sync was - // previously enabled. - // If not, stop it. - updateSyncPolicy(isSyncEnabled(), getSignedInAccountName()); + AccountStateChangedListener.onAccountSignedIn(oldAccount, newAccount); break; case DialogInterface.BUTTON_NEUTRAL: // Signed out - // Stop sync for the account that's being signed out of. - updateSyncPolicy(false, getSignedInAccountName()); + AccountStateChangedListener.onAccountSignedOut(oldAccount); getSharedPreferences() .edit() .remove(PREF_ACCOUNT_NAME) @@ -292,9 +259,7 @@ public final class AccountsSettingsFragment extends SubScreenFragment { class SyncNowListener implements Preference.OnPreferenceClickListener { @Override public boolean onPreferenceClick(final Preference preference) { - ContentResolver.requestSync( - new Account(getSignedInAccountName(), LoginAccountUtils.ACCOUNT_TYPE), - AUTHORITY, Bundle.EMPTY); + AccountStateChangedListener.forceSync(getSignedInAccountName()); return true; } } diff --git a/java/src/com/android/inputmethod/latin/setup/SetupWizardActivity.java b/java/src/com/android/inputmethod/latin/setup/SetupWizardActivity.java index 54562f39d..c3b30dcb4 100644 --- a/java/src/com/android/inputmethod/latin/setup/SetupWizardActivity.java +++ b/java/src/com/android/inputmethod/latin/setup/SetupWizardActivity.java @@ -42,6 +42,8 @@ import com.android.inputmethod.latin.utils.UncachedInputMethodManagerUtils; import java.util.ArrayList; +import javax.annotation.Nonnull; + // TODO: Use Fragment to implement welcome screen and setup steps. public final class SetupWizardActivity extends Activity implements View.OnClickListener { static final String TAG = SetupWizardActivity.class.getSimpleName(); @@ -82,7 +84,7 @@ public final class SetupWizardActivity extends Activity implements View.OnClickL private final InputMethodManager mImmInHandler; - public SettingsPoolingHandler(final SetupWizardActivity ownerInstance, + public SettingsPoolingHandler(@Nonnull final SetupWizardActivity ownerInstance, final InputMethodManager imm) { super(ownerInstance); mImmInHandler = imm; diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java index 0c8441454..7b66bbb75 100644 --- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java +++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java @@ -321,18 +321,6 @@ final class SuggestionStripLayoutHelper { } else { color = mColorSuggested; } - if (DebugFlags.DEBUG_ENABLED && suggestedWords.size() > 1) { - // If we auto-correct, then the autocorrection is in slot 0 and the typed word - // is in slot 1. - if (indexInSuggestedWords == SuggestedWords.INDEX_OF_AUTO_CORRECTION - && suggestedWords.mWillAutoCorrect - && AutoCorrectionUtils.shouldBlockAutoCorrectionBySafetyNet( - suggestedWords.getLabel(SuggestedWords.INDEX_OF_AUTO_CORRECTION), - suggestedWords.getLabel(SuggestedWords.INDEX_OF_TYPED_WORD))) { - return 0xFFFF0000; - } - } - if (suggestedWords.mIsObsoleteSuggestions && !isTypedWord) { return applyAlpha(color, mAlphaObsoleted); } diff --git a/java/src/com/android/inputmethod/latin/utils/AutoCorrectionUtils.java b/java/src/com/android/inputmethod/latin/utils/AutoCorrectionUtils.java index 156fcf57c..cba769521 100644 --- a/java/src/com/android/inputmethod/latin/utils/AutoCorrectionUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/AutoCorrectionUtils.java @@ -52,41 +52,9 @@ public final class AutoCorrectionUtils { if (DBG) { Log.d(TAG, "Auto corrected by S-threshold."); } - return !shouldBlockAutoCorrectionBySafetyNet(consideredWord, suggestion.mWord); + return true; } } return false; } - - // TODO: Resolve the inconsistencies between the native auto correction algorithms and - // this safety net - public static boolean shouldBlockAutoCorrectionBySafetyNet(final String typedWord, - final String suggestion) { - // Safety net for auto correction. - // Actually if we hit this safety net, it's a bug. - // If user selected aggressive auto correction mode, there is no need to use the safety - // net. - // If the length of typed word is less than MINIMUM_SAFETY_NET_CHAR_LENGTH, - // we should not use net because relatively edit distance can be big. - final int typedWordLength = typedWord.length(); - if (typedWordLength < MINIMUM_SAFETY_NET_CHAR_LENGTH) { - return false; - } - final int maxEditDistanceOfNativeDictionary = (typedWordLength / 2) + 1; - final int distance = BinaryDictionaryUtils.editDistance(typedWord, suggestion); - if (DBG) { - Log.d(TAG, "Autocorrected edit distance = " + distance - + ", " + maxEditDistanceOfNativeDictionary); - } - if (distance > maxEditDistanceOfNativeDictionary) { - if (DBG) { - Log.e(TAG, "Safety net: before = " + typedWord + ", after = " + suggestion); - Log.e(TAG, "(Error) The edit distance of this correction exceeds limit. " - + "Turning off auto-correction."); - } - return true; - } else { - return false; - } - } } diff --git a/java/src/com/android/inputmethod/latin/utils/BinaryDictionaryUtils.java b/java/src/com/android/inputmethod/latin/utils/BinaryDictionaryUtils.java index 5d7deba15..ce25fe6a4 100644 --- a/java/src/com/android/inputmethod/latin/utils/BinaryDictionaryUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/BinaryDictionaryUtils.java @@ -43,7 +43,6 @@ public final class BinaryDictionaryUtils { private static native boolean createEmptyDictFileNative(String filePath, long dictVersion, String locale, String[] attributeKeyStringArray, String[] attributeValueStringArray); private static native float calcNormalizedScoreNative(int[] before, int[] after, int score); - private static native int editDistanceNative(int[] before, int[] after); private static native int setCurrentTimeForTestNative(int currentTime); public static DictionaryHeader getHeader(final File dictFile) @@ -112,14 +111,6 @@ public final class BinaryDictionaryUtils { StringUtils.toCodePointArray(after), score); } - public static int editDistance(final String before, final String after) { - if (before == null || after == null) { - throw new IllegalArgumentException(); - } - return editDistanceNative(StringUtils.toCodePointArray(before), - StringUtils.toCodePointArray(after)); - } - /** * Control the current time to be used in the native code. If currentTime >= 0, this method sets * the current time and gets into test mode. diff --git a/java/src/com/android/inputmethod/latin/utils/LeakGuardHandlerWrapper.java b/java/src/com/android/inputmethod/latin/utils/LeakGuardHandlerWrapper.java index dd6fac671..9a5be99b3 100644 --- a/java/src/com/android/inputmethod/latin/utils/LeakGuardHandlerWrapper.java +++ b/java/src/com/android/inputmethod/latin/utils/LeakGuardHandlerWrapper.java @@ -21,21 +21,22 @@ import android.os.Looper; import java.lang.ref.WeakReference; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + public class LeakGuardHandlerWrapper<T> extends Handler { private final WeakReference<T> mOwnerInstanceRef; - public LeakGuardHandlerWrapper(final T ownerInstance) { + public LeakGuardHandlerWrapper(@Nonnull final T ownerInstance) { this(ownerInstance, Looper.myLooper()); } - public LeakGuardHandlerWrapper(final T ownerInstance, final Looper looper) { + public LeakGuardHandlerWrapper(@Nonnull final T ownerInstance, final Looper looper) { super(looper); - if (ownerInstance == null) { - throw new NullPointerException("ownerInstance is null"); - } mOwnerInstanceRef = new WeakReference<>(ownerInstance); } + @Nullable public T getOwnerInstance() { return mOwnerInstanceRef.get(); } diff --git a/native/jni/com_android_inputmethod_latin_BinaryDictionaryUtils.cpp b/native/jni/com_android_inputmethod_latin_BinaryDictionaryUtils.cpp index 0a34b783a..68bf417e5 100644 --- a/native/jni/com_android_inputmethod_latin_BinaryDictionaryUtils.cpp +++ b/native/jni/com_android_inputmethod_latin_BinaryDictionaryUtils.cpp @@ -68,18 +68,6 @@ static jfloat latinime_BinaryDictionaryUtils_calcNormalizedScore(JNIEnv *env, jc afterCodePoints, afterLength, score); } -static jint latinime_BinaryDictionaryUtils_editDistance(JNIEnv *env, jclass clazz, jintArray before, - jintArray after) { - jsize beforeLength = env->GetArrayLength(before); - jsize afterLength = env->GetArrayLength(after); - int beforeCodePoints[beforeLength]; - int afterCodePoints[afterLength]; - env->GetIntArrayRegion(before, 0, beforeLength, beforeCodePoints); - env->GetIntArrayRegion(after, 0, afterLength, afterCodePoints); - return AutocorrectionThresholdUtils::editDistance(beforeCodePoints, beforeLength, - afterCodePoints, afterLength); -} - static int latinime_BinaryDictionaryUtils_setCurrentTimeForTest(JNIEnv *env, jclass clazz, jint currentTime) { if (currentTime >= 0) { @@ -104,11 +92,6 @@ static const JNINativeMethod sMethods[] = { reinterpret_cast<void *>(latinime_BinaryDictionaryUtils_calcNormalizedScore) }, { - const_cast<char *>("editDistanceNative"), - const_cast<char *>("([I[I)I"), - reinterpret_cast<void *>(latinime_BinaryDictionaryUtils_editDistance) - }, - { const_cast<char *>("setCurrentTimeForTestNative"), const_cast<char *>("(I)I"), reinterpret_cast<void *>(latinime_BinaryDictionaryUtils_setCurrentTimeForTest) diff --git a/native/jni/src/suggest/core/policy/weighting.cpp b/native/jni/src/suggest/core/policy/weighting.cpp index c202b81fe..a06e7d070 100644 --- a/native/jni/src/suggest/core/policy/weighting.cpp +++ b/native/jni/src/suggest/core/policy/weighting.cpp @@ -110,10 +110,14 @@ static inline void profile(const CorrectionType correctionType, DicNode *const n return weighting->getOmissionCost(parentDicNode, dicNode); case CT_ADDITIONAL_PROXIMITY: // only used for typing - return weighting->getAdditionalProximityCost(); + // TODO: Quit calling getMatchedCost(). + return weighting->getAdditionalProximityCost() + + weighting->getMatchedCost(traverseSession, dicNode, inputStateG); case CT_SUBSTITUTION: // only used for typing - return weighting->getSubstitutionCost(); + // TODO: Quit calling getMatchedCost(). + return weighting->getSubstitutionCost() + + weighting->getMatchedCost(traverseSession, dicNode, inputStateG); case CT_NEW_WORD_SPACE_OMISSION: return weighting->getNewWordSpatialCost(traverseSession, dicNode, inputStateG); case CT_MATCH: @@ -176,9 +180,9 @@ static inline void profile(const CorrectionType correctionType, DicNode *const n case CT_OMISSION: return 0; case CT_ADDITIONAL_PROXIMITY: - return 0; /* 0 because CT_MATCH will be called */ + return 1; case CT_SUBSTITUTION: - return 0; /* 0 because CT_MATCH will be called */ + return 1; case CT_NEW_WORD_SPACE_OMISSION: return 0; case CT_MATCH: diff --git a/native/jni/src/suggest/core/suggest.cpp b/native/jni/src/suggest/core/suggest.cpp index cf2df86a6..c71526293 100644 --- a/native/jni/src/suggest/core/suggest.cpp +++ b/native/jni/src/suggest/core/suggest.cpp @@ -284,7 +284,6 @@ void Suggest::processDicNodeAsAdditionalProximityChar(DicTraverseSession *traver // not treat the node as a terminal. There is no need to pass the bigram map in these cases. Weighting::addCostAndForwardInputIndex(WEIGHTING, CT_ADDITIONAL_PROXIMITY, traverseSession, dicNode, childDicNode, 0 /* multiBigramMap */); - weightChildNode(traverseSession, childDicNode); processExpandedDicNode(traverseSession, childDicNode); } @@ -292,7 +291,6 @@ void Suggest::processDicNodeAsSubstitution(DicTraverseSession *traverseSession, DicNode *dicNode, DicNode *childDicNode) const { Weighting::addCostAndForwardInputIndex(WEIGHTING, CT_SUBSTITUTION, traverseSession, dicNode, childDicNode, 0 /* multiBigramMap */); - weightChildNode(traverseSession, childDicNode); processExpandedDicNode(traverseSession, childDicNode); } @@ -403,7 +401,7 @@ void Suggest::weightChildNode(DicTraverseSession *traverseSession, DicNode *dicN if (dicNode->isCompletion(inputSize)) { Weighting::addCostAndForwardInputIndex(WEIGHTING, CT_COMPLETION, traverseSession, 0 /* parentDicNode */, dicNode, 0 /* multiBigramMap */); - } else { // completion + } else { Weighting::addCostAndForwardInputIndex(WEIGHTING, CT_MATCH, traverseSession, 0 /* parentDicNode */, dicNode, 0 /* multiBigramMap */); } diff --git a/native/jni/src/suggest/policyimpl/typing/scoring_params.cpp b/native/jni/src/suggest/policyimpl/typing/scoring_params.cpp index b621eef22..6a2db687d 100644 --- a/native/jni/src/suggest/policyimpl/typing/scoring_params.cpp +++ b/native/jni/src/suggest/policyimpl/typing/scoring_params.cpp @@ -49,7 +49,7 @@ const float ScoringParams::INSERTION_COST_PROXIMITY_CHAR = 0.674f; const float ScoringParams::INSERTION_COST_FIRST_CHAR = 0.639f; const float ScoringParams::TRANSPOSITION_COST = 0.5608f; const float ScoringParams::SPACE_SUBSTITUTION_COST = 0.334f; -const float ScoringParams::ADDITIONAL_PROXIMITY_COST = 0.4576f; +const float ScoringParams::ADDITIONAL_PROXIMITY_COST = 0.37972f; const float ScoringParams::SUBSTITUTION_COST = 0.3806f; const float ScoringParams::COST_NEW_WORD = 0.0314f; const float ScoringParams::COST_SECOND_OR_LATER_WORD_FIRST_CHAR_UPPERCASE = 0.3224f; diff --git a/native/jni/src/suggest/policyimpl/typing/typing_weighting.cpp b/native/jni/src/suggest/policyimpl/typing/typing_weighting.cpp index 1d590c353..db7a39efb 100644 --- a/native/jni/src/suggest/policyimpl/typing/typing_weighting.cpp +++ b/native/jni/src/suggest/policyimpl/typing/typing_weighting.cpp @@ -68,7 +68,8 @@ ErrorTypeUtils::ErrorType TypingWeighting::getErrorType(const CorrectionType cor } break; case CT_ADDITIONAL_PROXIMITY: - return ErrorTypeUtils::PROXIMITY_CORRECTION; + // TODO: Change to EDIT_CORRECTION. + return ErrorTypeUtils::PROXIMITY_CORRECTION; case CT_OMISSION: if (parentDicNode->canBeIntentionalOmission()) { return ErrorTypeUtils::INTENTIONAL_OMISSION; @@ -77,6 +78,8 @@ ErrorTypeUtils::ErrorType TypingWeighting::getErrorType(const CorrectionType cor } break; case CT_SUBSTITUTION: + // TODO: Quit settng PROXIMITY_CORRECTION. + return ErrorTypeUtils::EDIT_CORRECTION | ErrorTypeUtils::PROXIMITY_CORRECTION; case CT_INSERTION: case CT_TERMINAL_INSERTION: case CT_TRANSPOSITION: diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml index 26195d39f..c7a9e13a2 100644 --- a/tests/AndroidManifest.xml +++ b/tests/AndroidManifest.xml @@ -21,7 +21,8 @@ <uses-permission android:name="android.permission.READ_CONTACTS" /> - <application> + <application android:label="@string/app_name" + android:icon="@drawable/ic_app"> <uses-library android:name="android.test.runner" /> <!-- meta-data android:name="com.android.contacts.iconset" android:resource="@xml/iconset" /--> </application> diff --git a/tests/res/drawable-hdpi/ic_app.png b/tests/res/drawable-hdpi/ic_app.png Binary files differnew file mode 100644 index 000000000..345c23d2c --- /dev/null +++ b/tests/res/drawable-hdpi/ic_app.png diff --git a/tests/res/values/strings.xml b/tests/res/values/strings.xml new file mode 100644 index 000000000..5cc48b6bf --- /dev/null +++ b/tests/res/values/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2014, 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> + <string name="app_name" translatable="false">LatinIMETests</string> +</resources> diff --git a/tests/src/com/android/inputmethod/latin/BlueUnderlineTests.java b/tests/src/com/android/inputmethod/latin/BlueUnderlineTests.java index 30b088137..ae5cc5c73 100644 --- a/tests/src/com/android/inputmethod/latin/BlueUnderlineTests.java +++ b/tests/src/com/android/inputmethod/latin/BlueUnderlineTests.java @@ -28,7 +28,7 @@ public class BlueUnderlineTests extends InputTestsBase { final int EXPECTED_SPAN_START = 0; final int EXPECTED_SPAN_END = 4; type(STRING_TO_TYPE); - sleep(DELAY_TO_WAIT_FOR_UNDERLINE); + sleep(DELAY_TO_WAIT_FOR_UNDERLINE_MILLIS); runMessages(); final SpanGetter span = new SpanGetter(mEditText.getText(), SuggestionSpan.class); assertEquals("show blue underline, span start", EXPECTED_SPAN_START, span.mStart); @@ -42,7 +42,7 @@ public class BlueUnderlineTests extends InputTestsBase { final int EXPECTED_SPAN_START = 0; final int EXPECTED_SPAN_END = 5; type(STRING_1_TO_TYPE); - sleep(DELAY_TO_WAIT_FOR_UNDERLINE); + sleep(DELAY_TO_WAIT_FOR_UNDERLINE_MILLIS); runMessages(); type(STRING_2_TO_TYPE); // We haven't have time to look into the dictionary yet, so the line should still be @@ -51,7 +51,7 @@ public class BlueUnderlineTests extends InputTestsBase { assertEquals("extend blue underline, span start", EXPECTED_SPAN_START, spanBefore.mStart); assertEquals("extend blue underline, span end", EXPECTED_SPAN_END, spanBefore.mEnd); assertTrue("extend blue underline, span color", spanBefore.isAutoCorrectionIndicator()); - sleep(DELAY_TO_WAIT_FOR_UNDERLINE); + sleep(DELAY_TO_WAIT_FOR_UNDERLINE_MILLIS); runMessages(); // Now we have been able to re-evaluate the word, there shouldn't be an auto-correction span final SpanGetter spanAfter = new SpanGetter(mEditText.getText(), SuggestionSpan.class); @@ -65,18 +65,18 @@ public class BlueUnderlineTests extends InputTestsBase { final int EXPECTED_UNDERLINE_SPAN_START = 0; final int EXPECTED_UNDERLINE_SPAN_END = 3; type(STRING_TO_TYPE); - sleep(DELAY_TO_WAIT_FOR_UNDERLINE); + sleep(DELAY_TO_WAIT_FOR_UNDERLINE_MILLIS); runMessages(); type(Constants.CODE_SPACE); // typedLength + 1 because we also typed a space mLatinIME.onUpdateSelection(0, 0, typedLength + 1, typedLength + 1, -1, -1); - sleep(DELAY_TO_WAIT_FOR_UNDERLINE); + sleep(DELAY_TO_WAIT_FOR_UNDERLINE_MILLIS); runMessages(); type(Constants.CODE_DELETE); - sleep(DELAY_TO_WAIT_FOR_UNDERLINE); + sleep(DELAY_TO_WAIT_FOR_UNDERLINE_MILLIS); runMessages(); type(Constants.CODE_DELETE); - sleep(DELAY_TO_WAIT_FOR_UNDERLINE); + sleep(DELAY_TO_WAIT_FOR_UNDERLINE_MILLIS); runMessages(); final SpanGetter suggestionSpan = new SpanGetter(mEditText.getText(), SuggestionSpan.class); assertFalse("show no blue underline after backspace, span should not be the auto-" @@ -93,7 +93,7 @@ public class BlueUnderlineTests extends InputTestsBase { final int typedLength = STRING_TO_TYPE.length(); final int NEW_CURSOR_POSITION = 0; type(STRING_TO_TYPE); - sleep(DELAY_TO_WAIT_FOR_UNDERLINE); + sleep(DELAY_TO_WAIT_FOR_UNDERLINE_MILLIS); // Simulate the onUpdateSelection() event mLatinIME.onUpdateSelection(0, 0, typedLength, typedLength, -1, -1); runMessages(); @@ -103,7 +103,7 @@ public class BlueUnderlineTests extends InputTestsBase { mInputConnection.setSelection(NEW_CURSOR_POSITION, NEW_CURSOR_POSITION); mLatinIME.onUpdateSelection(typedLength, typedLength, NEW_CURSOR_POSITION, NEW_CURSOR_POSITION, -1, -1); - sleep(DELAY_TO_WAIT_FOR_UNDERLINE); + sleep(DELAY_TO_WAIT_FOR_UNDERLINE_MILLIS); runMessages(); final SpanGetter span = new SpanGetter(mEditText.getText(), SuggestionSpan.class); assertFalse("blue underline removed when cursor is moved", @@ -113,7 +113,7 @@ public class BlueUnderlineTests extends InputTestsBase { public void testComposingStopsOnSpace() { final String STRING_TO_TYPE = "this "; type(STRING_TO_TYPE); - sleep(DELAY_TO_WAIT_FOR_UNDERLINE); + sleep(DELAY_TO_WAIT_FOR_UNDERLINE_MILLIS); // Simulate the onUpdateSelection() event mLatinIME.onUpdateSelection(0, 0, STRING_TO_TYPE.length(), STRING_TO_TYPE.length(), -1, -1); runMessages(); diff --git a/tests/src/com/android/inputmethod/latin/InputLogicTests.java b/tests/src/com/android/inputmethod/latin/InputLogicTests.java index ec249dab3..99dc9a204 100644 --- a/tests/src/com/android/inputmethod/latin/InputLogicTests.java +++ b/tests/src/com/android/inputmethod/latin/InputLogicTests.java @@ -16,6 +16,7 @@ package com.android.inputmethod.latin; +import android.test.MoreAsserts; import android.test.suitebuilder.annotation.LargeTest; import android.text.TextUtils; import android.view.inputmethod.BaseInputConnection; @@ -487,7 +488,7 @@ public class InputLogicTests extends InputTestsBase { public void testPredictionsAfterSpace() { final String WORD_TO_TYPE = "Barack "; type(WORD_TO_TYPE); - sleep(DELAY_TO_WAIT_FOR_PREDICTIONS); + sleep(DELAY_TO_WAIT_FOR_PREDICTIONS_MILLIS); runMessages(); // Test the first prediction is displayed final SuggestedWords suggestedWords = mLatinIME.getSuggestedWordsForTest(); @@ -499,17 +500,17 @@ public class InputLogicTests extends InputTestsBase { mLatinIME.clearPersonalizedDictionariesForTest(); final String WORD_TO_TYPE = "Barack "; type(WORD_TO_TYPE); - sleep(DELAY_TO_WAIT_FOR_PREDICTIONS); + sleep(DELAY_TO_WAIT_FOR_PREDICTIONS_MILLIS); runMessages(); // No need to test here, testPredictionsAfterSpace is testing it already type(" "); - sleep(DELAY_TO_WAIT_FOR_PREDICTIONS); + sleep(DELAY_TO_WAIT_FOR_PREDICTIONS_MILLIS); runMessages(); // Test the predictions have been cleared SuggestedWords suggestedWords = mLatinIME.getSuggestedWordsForTest(); assertEquals("predictions cleared after double-space-to-period", suggestedWords.size(), 0); type(Constants.CODE_DELETE); - sleep(DELAY_TO_WAIT_FOR_PREDICTIONS); + sleep(DELAY_TO_WAIT_FOR_PREDICTIONS_MILLIS); runMessages(); // Test the first prediction is displayed suggestedWords = mLatinIME.getSuggestedWordsForTest(); @@ -522,7 +523,7 @@ public class InputLogicTests extends InputTestsBase { type(WORD_TO_TYPE); // Choose the auto-correction. For "Barack", the auto-correction should be "Barack". pickSuggestionManually(WORD_TO_TYPE); - sleep(DELAY_TO_WAIT_FOR_PREDICTIONS); + sleep(DELAY_TO_WAIT_FOR_PREDICTIONS_MILLIS); runMessages(); // Test the first prediction is displayed final SuggestedWords suggestedWords = mLatinIME.getSuggestedWordsForTest(); @@ -534,13 +535,13 @@ public class InputLogicTests extends InputTestsBase { mLatinIME.clearPersonalizedDictionariesForTest(); final String WORD_TO_TYPE = "Barack. "; type(WORD_TO_TYPE); - sleep(DELAY_TO_WAIT_FOR_PREDICTIONS); + sleep(DELAY_TO_WAIT_FOR_PREDICTIONS_MILLIS); runMessages(); SuggestedWords suggestedWords = mLatinIME.getSuggestedWordsForTest(); assertEquals("No prediction after period after inputting once.", 0, suggestedWords.size()); type(WORD_TO_TYPE); - sleep(DELAY_TO_WAIT_FOR_PREDICTIONS); + sleep(DELAY_TO_WAIT_FOR_PREDICTIONS_MILLIS); runMessages(); suggestedWords = mLatinIME.getSuggestedWordsForTest(); assertEquals("Beginning-of-Sentence prediction after inputting 2 times.", "Barack", @@ -565,18 +566,18 @@ public class InputLogicTests extends InputTestsBase { type(" "); mLatinIME.onUpdateSelection(endOfSuggestion, endOfSuggestion, endOfSuggestion + 1, endOfSuggestion + 1, -1, -1); - sleep(DELAY_TO_WAIT_FOR_PREDICTIONS); + sleep(DELAY_TO_WAIT_FOR_PREDICTIONS_MILLIS); runMessages(); // Simulate a manual cursor move mInputConnection.setSelection(indexForManualCursor, indexForManualCursor); mLatinIME.onUpdateSelection(endOfSuggestion + 1, endOfSuggestion + 1, indexForManualCursor, indexForManualCursor, -1, -1); - sleep(DELAY_TO_WAIT_FOR_PREDICTIONS); + sleep(DELAY_TO_WAIT_FOR_PREDICTIONS_MILLIS); runMessages(); pickSuggestionManually(WORD_TO_TYPE); mLatinIME.onUpdateSelection(indexForManualCursor, indexForManualCursor, endOfWord, endOfWord, -1, -1); - sleep(DELAY_TO_WAIT_FOR_PREDICTIONS); + sleep(DELAY_TO_WAIT_FOR_PREDICTIONS_MILLIS); runMessages(); // Test the first prediction is displayed final SuggestedWords suggestedWords = mLatinIME.getSuggestedWordsForTest(); @@ -624,7 +625,7 @@ public class InputLogicTests extends InputTestsBase { for (int i = 0; i < WORD_TO_TYPE.length(); ++i) { type(WORD_TO_TYPE.substring(i, i+1)); - sleep(DELAY_TO_WAIT_FOR_PREDICTIONS); + sleep(DELAY_TO_WAIT_FOR_PREDICTIONS_MILLIS); runMessages(); } assertEquals("type many trailing single quotes one by one", EXPECTED_RESULT, @@ -636,7 +637,7 @@ public class InputLogicTests extends InputTestsBase { final String EXPECTED_RESULT = WORD_TO_TYPE; for (int i = 0; i < WORD_TO_TYPE.length(); ++i) { type(WORD_TO_TYPE.substring(i, i+1)); - sleep(DELAY_TO_WAIT_FOR_PREDICTIONS); + sleep(DELAY_TO_WAIT_FOR_PREDICTIONS_MILLIS); runMessages(); } assertEquals("type words letter by letter", EXPECTED_RESULT, @@ -652,10 +653,30 @@ public class InputLogicTests extends InputTestsBase { changeLanguage("fr"); runMessages(); type(WORD_TO_TYPE_SECOND_PART); - sleep(DELAY_TO_WAIT_FOR_UNDERLINE); + sleep(DELAY_TO_WAIT_FOR_UNDERLINE_MILLIS); runMessages(); final SuggestedWords suggestedWords = mLatinIME.getSuggestedWordsForTest(); assertEquals("Suggestions updated after switching languages", EXPECTED_RESULT, suggestedWords.size() > 0 ? suggestedWords.getWord(1) : null); } + + public void testBasicGesture() { + gesture("this"); + assertEquals("gesture \"this\"", "this", mEditText.getText().toString()); + } + + public void testGestureGesture() { + gesture("this"); + gesture("is"); + assertEquals("gesture \"this is\"", "this is", mEditText.getText().toString()); + } + + public void testGestureBackspaceGestureAgain() { + gesture("this"); + type(Constants.CODE_DELETE); + assertEquals("gesture then backspace", "", mEditText.getText().toString()); + gesture("this"); + MoreAsserts.assertNotEqual("gesture twice the same thing", "this", + mEditText.getText().toString()); + } } diff --git a/tests/src/com/android/inputmethod/latin/InputLogicTestsLanguageWithoutSpaces.java b/tests/src/com/android/inputmethod/latin/InputLogicTestsLanguageWithoutSpaces.java index 2560407dc..c16372ab5 100644 --- a/tests/src/com/android/inputmethod/latin/InputLogicTestsLanguageWithoutSpaces.java +++ b/tests/src/com/android/inputmethod/latin/InputLogicTestsLanguageWithoutSpaces.java @@ -74,7 +74,7 @@ public class InputLogicTestsLanguageWithoutSpaces extends InputTestsBase { mInputConnection.setSelection(CURSOR_POS, CURSOR_POS); mLatinIME.onUpdateSelection(typedLength, typedLength, CURSOR_POS, CURSOR_POS, -1, -1); - sleep(DELAY_TO_WAIT_FOR_PREDICTIONS); + sleep(DELAY_TO_WAIT_FOR_PREDICTIONS_MILLIS); runMessages(); assertEquals("start composing inside text", -1, BaseInputConnection.getComposingSpanStart(mEditText.getText())); @@ -91,7 +91,7 @@ public class InputLogicTestsLanguageWithoutSpaces extends InputTestsBase { final String WORD_TO_TYPE = "Barack "; changeKeyboardLocaleAndDictLocale("th", "en_US"); type(WORD_TO_TYPE); - sleep(DELAY_TO_WAIT_FOR_PREDICTIONS); + sleep(DELAY_TO_WAIT_FOR_PREDICTIONS_MILLIS); runMessages(); // Make sure there is no space assertEquals("predictions in lang without spaces", "Barack", diff --git a/tests/src/com/android/inputmethod/latin/InputLogicTestsNonEnglish.java b/tests/src/com/android/inputmethod/latin/InputLogicTestsNonEnglish.java index 715d449a0..842b54fe1 100644 --- a/tests/src/com/android/inputmethod/latin/InputLogicTestsNonEnglish.java +++ b/tests/src/com/android/inputmethod/latin/InputLogicTestsNonEnglish.java @@ -70,7 +70,7 @@ public class InputLogicTestsNonEnglish extends InputTestsBase { try { changeLanguage("fr"); type(WORD_TO_TYPE); - sleep(DELAY_TO_WAIT_FOR_UNDERLINE); + sleep(DELAY_TO_WAIT_FOR_UNDERLINE_MILLIS); runMessages(); assertTrue("type word then type space should display punctuation strip", mLatinIME.getSuggestedWordsForTest().isPunctuationSuggestions()); @@ -95,7 +95,7 @@ public class InputLogicTestsNonEnglish extends InputTestsBase { try { changeLanguage("fr"); type(WORD_TO_TYPE); - sleep(DELAY_TO_WAIT_FOR_UNDERLINE); + sleep(DELAY_TO_WAIT_FOR_UNDERLINE_MILLIS); runMessages(); final SuggestedWords suggestedWords = mLatinIME.getSuggestedWordsForTest(); assertEquals("type word then type space yields predictions for French", diff --git a/tests/src/com/android/inputmethod/latin/InputTestsBase.java b/tests/src/com/android/inputmethod/latin/InputTestsBase.java index ee7942450..dd900a22c 100644 --- a/tests/src/com/android/inputmethod/latin/InputTestsBase.java +++ b/tests/src/com/android/inputmethod/latin/InputTestsBase.java @@ -18,6 +18,7 @@ package com.android.inputmethod.latin; import android.content.Context; import android.content.SharedPreferences; +import android.graphics.Point; import android.os.Looper; import android.preference.PreferenceManager; import android.test.ServiceTestCase; @@ -45,6 +46,7 @@ import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import com.android.inputmethod.latin.settings.DebugSettings; import com.android.inputmethod.latin.settings.Settings; import com.android.inputmethod.latin.utils.LocaleUtils; +import com.android.inputmethod.latin.utils.StringUtils; import com.android.inputmethod.latin.utils.SubtypeLocaleUtils; import java.util.Locale; @@ -58,9 +60,11 @@ public class InputTestsBase extends ServiceTestCase<LatinIMEForTests> { private static final String DEFAULT_AUTO_CORRECTION_THRESHOLD = "1"; // The message that sets the underline is posted with a 500 ms delay - protected static final int DELAY_TO_WAIT_FOR_UNDERLINE = 500; + protected static final int DELAY_TO_WAIT_FOR_UNDERLINE_MILLIS = 500; // The message that sets predictions is posted with a 200 ms delay - protected static final int DELAY_TO_WAIT_FOR_PREDICTIONS = 200; + protected static final int DELAY_TO_WAIT_FOR_PREDICTIONS_MILLIS = 200; + // We wait for gesture computation for this delay + protected static final int DELAY_TO_WAIT_FOR_GESTURE_MILLIS = 200; private final int TIMEOUT_TO_WAIT_FOR_LOADING_MAIN_DICTIONARY_IN_SECONDS = 60; // Type for a test phony dictionary @@ -217,7 +221,7 @@ public class InputTestsBase extends ServiceTestCase<LatinIMEForTests> { // Run messages to avoid the messages enqueued by startInputView() and its friends // to run on a later call and ruin things. We need to wait first because some of them // can be posted with a delay (notably, MSG_RESUME_SUGGESTIONS) - sleep(DELAY_TO_WAIT_FOR_PREDICTIONS); + sleep(DELAY_TO_WAIT_FOR_PREDICTIONS_MILLIS); runMessages(); } @@ -301,6 +305,47 @@ public class InputTestsBase extends ServiceTestCase<LatinIMEForTests> { } } + protected Point getXY(final int codePoint) { + final Key key = mKeyboard.getKey(codePoint); + if (key == null) { + throw new RuntimeException("Code point not on the keyboard"); + } else { + return new Point(key.getX() + key.getWidth() / 2, key.getY() + key.getHeight() / 2); + } + } + + protected void gesture(final String stringToGesture) { + if (StringUtils.codePointCount(stringToGesture) < 2) { + throw new RuntimeException("Can't gesture strings less than 2 chars long"); + } + + mLatinIME.onStartBatchInput(); + final int startCodePoint = stringToGesture.codePointAt(0); + Point oldPoint = getXY(startCodePoint); + int timestamp = 0; // In milliseconds since the start of the gesture + final InputPointers pointers = new InputPointers(Constants.DEFAULT_GESTURE_POINTS_CAPACITY); + pointers.addPointer(oldPoint.x, oldPoint.y, 0 /* pointerId */, timestamp); + + for (int i = Character.charCount(startCodePoint); i < stringToGesture.length(); + i = stringToGesture.offsetByCodePoints(i, 1)) { + final Point newPoint = getXY(stringToGesture.codePointAt(i)); + // Arbitrarily 0.5s between letters and 0.1 between events. Refine this later if needed. + final int STEPS = 5; + for (int j = 0; j < STEPS; ++j) { + timestamp += 100; + pointers.addPointer(oldPoint.x + ((newPoint.x - oldPoint.x) * j) / STEPS, + oldPoint.y + ((newPoint.y - oldPoint.y) * j) / STEPS, + 0 /* pointerId */, timestamp); + } + oldPoint.x = newPoint.x; + oldPoint.y = newPoint.y; + mLatinIME.onUpdateBatchInput(pointers); + } + mLatinIME.onEndBatchInput(pointers); + sleep(DELAY_TO_WAIT_FOR_GESTURE_MILLIS); + runMessages(); + } + protected void waitForDictionariesToBeLoaded() { try { mLatinIME.waitForLoadingDictionaries( diff --git a/tests/src/com/android/inputmethod/latin/PunctuationTests.java b/tests/src/com/android/inputmethod/latin/PunctuationTests.java index 64750fbda..3537918de 100644 --- a/tests/src/com/android/inputmethod/latin/PunctuationTests.java +++ b/tests/src/com/android/inputmethod/latin/PunctuationTests.java @@ -38,7 +38,7 @@ public class PunctuationTests extends InputTestsBase { try { mLatinIME.loadSettings(); type(WORD_TO_TYPE); - sleep(DELAY_TO_WAIT_FOR_UNDERLINE); + sleep(DELAY_TO_WAIT_FOR_UNDERLINE_MILLIS); runMessages(); assertTrue("type word then type space should display punctuation strip", mLatinIME.getSuggestedWordsForTest().isPunctuationSuggestions()); diff --git a/tests/src/com/android/inputmethod/latin/ShiftModeTests.java b/tests/src/com/android/inputmethod/latin/ShiftModeTests.java index db3c9baa9..8ba0174b5 100644 --- a/tests/src/com/android/inputmethod/latin/ShiftModeTests.java +++ b/tests/src/com/android/inputmethod/latin/ShiftModeTests.java @@ -75,7 +75,7 @@ public class ShiftModeTests extends InputTestsBase { repeatKey(Constants.CODE_DELETE); } assertFalse("Caps immediately after repeating Backspace a lot", isCapsModeAutoShifted()); - sleep(DELAY_TO_WAIT_FOR_PREDICTIONS); + sleep(DELAY_TO_WAIT_FOR_PREDICTIONS_MILLIS); runMessages(); assertTrue("Caps after a while after repeating Backspace a lot", isCapsModeAutoShifted()); } diff --git a/tests/src/com/android/inputmethod/latin/settings/AccountsSettingsFragmentTests.java b/tests/src/com/android/inputmethod/latin/settings/AccountsSettingsFragmentTests.java index 273d7faf7..2ef8b548f 100644 --- a/tests/src/com/android/inputmethod/latin/settings/AccountsSettingsFragmentTests.java +++ b/tests/src/com/android/inputmethod/latin/settings/AccountsSettingsFragmentTests.java @@ -16,21 +16,14 @@ package com.android.inputmethod.latin.settings; -import static com.android.inputmethod.latin.settings.AccountsSettingsFragment.AUTHORITY; - -import android.accounts.Account; import android.app.AlertDialog; import android.app.Dialog; -import android.content.ContentResolver; import android.content.Intent; import android.test.ActivityInstrumentationTestCase2; import android.test.suitebuilder.annotation.MediumTest; import android.view.View; import android.widget.ListView; -import com.android.inputmethod.latin.accounts.LoginAccountUtils; -import com.android.inputmethod.latin.define.ProductionFlags; - import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -39,9 +32,6 @@ public class AccountsSettingsFragmentTests extends ActivityInstrumentationTestCase2<TestFragmentActivity> { private static final String FRAG_NAME = AccountsSettingsFragment.class.getName(); private static final long TEST_TIMEOUT_MILLIS = 5000; - private static final String TEST_ACCOUNT_NAME = "AccountsSettingsFragmentTests"; - private static final Account TEST_ACCOUNT = - new Account(TEST_ACCOUNT_NAME, LoginAccountUtils.ACCOUNT_TYPE); private AlertDialog mDialog; @@ -57,13 +47,6 @@ public class AccountsSettingsFragmentTests setActivityIntent(intent); } - @Override - protected void tearDown() throws Exception { - super.tearDown(); - // reset the syncable state to unknown - ContentResolver.setIsSyncable(TEST_ACCOUNT, AUTHORITY, -1); - } - public void testEmptyAccounts() { final AccountsSettingsFragment fragment = (AccountsSettingsFragment) getActivity().mFragment; @@ -146,57 +129,4 @@ public class AccountsSettingsFragmentTests assertEquals(View.VISIBLE, mDialog.getButton(Dialog.BUTTON_NEGATIVE).getVisibility()); assertEquals(View.VISIBLE, mDialog.getButton(Dialog.BUTTON_POSITIVE).getVisibility()); } - - public void testUpdateSyncPolicy_enable() { - // This test is a no-op when ENABLE_PERSONAL_DICTIONARY_SYNC is not enabled - if (!ProductionFlags.ENABLE_PERSONAL_DICTIONARY_SYNC) { - return; - } - // Should be unknown by default. - assertTrue(ContentResolver.getIsSyncable(TEST_ACCOUNT, AUTHORITY) < 0); - - final AccountsSettingsFragment fragment = - (AccountsSettingsFragment) getActivity().mFragment; - fragment.updateSyncPolicy(true, TEST_ACCOUNT_NAME); - - // Should be syncable now. - assertEquals(1, ContentResolver.getIsSyncable(TEST_ACCOUNT, AUTHORITY)); - } - - public void testUpdateSyncPolicy_disable() { - // This test is a no-op when ENABLE_PERSONAL_DICTIONARY_SYNC is not enabled - if (!ProductionFlags.ENABLE_PERSONAL_DICTIONARY_SYNC) { - return; - } - // Should be unknown by default. - assertTrue(ContentResolver.getIsSyncable(TEST_ACCOUNT, AUTHORITY) < 0); - - final AccountsSettingsFragment fragment = - (AccountsSettingsFragment) getActivity().mFragment; - fragment.updateSyncPolicy(false, TEST_ACCOUNT_NAME); - - // Should not be syncable now. - assertEquals(0, ContentResolver.getIsSyncable(TEST_ACCOUNT, AUTHORITY)); - } - - public void testUpdateSyncPolicy_enableDisable() { - // This test is a no-op when ENABLE_PERSONAL_DICTIONARY_SYNC is not enabled - if (!ProductionFlags.ENABLE_PERSONAL_DICTIONARY_SYNC) { - return; - } - // Should be unknown by default. - assertTrue(ContentResolver.getIsSyncable(TEST_ACCOUNT, AUTHORITY) < 0); - - final AccountsSettingsFragment fragment = - (AccountsSettingsFragment) getActivity().mFragment; - fragment.updateSyncPolicy(true, TEST_ACCOUNT_NAME); - - // Should be syncable now. - assertEquals(1, ContentResolver.getIsSyncable(TEST_ACCOUNT, AUTHORITY)); - - fragment.updateSyncPolicy(false, TEST_ACCOUNT_NAME); - - // Should not be syncable now. - assertEquals(0, ContentResolver.getIsSyncable(TEST_ACCOUNT, AUTHORITY)); - } } diff --git a/tests/src/com/android/inputmethod/latin/utils/EditDistanceTests.java b/tests/src/com/android/inputmethod/latin/utils/EditDistanceTests.java deleted file mode 100644 index 58312264b..000000000 --- a/tests/src/com/android/inputmethod/latin/utils/EditDistanceTests.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.inputmethod.latin.utils; - -import android.test.AndroidTestCase; -import android.test.suitebuilder.annotation.SmallTest; - -@SmallTest -public class EditDistanceTests extends AndroidTestCase { - /* - * dist(kitten, sitting) == 3 - * - * kitten- - * .|||.| - * sitting - */ - public void testExample1() { - final int dist = BinaryDictionaryUtils.editDistance("kitten", "sitting"); - assertEquals("edit distance between 'kitten' and 'sitting' is 3", - 3, dist); - } - - /* - * dist(Sunday, Saturday) == 3 - * - * Saturday - * | |.||| - * S--unday - */ - public void testExample2() { - final int dist = BinaryDictionaryUtils.editDistance("Saturday", "Sunday"); - assertEquals("edit distance between 'Saturday' and 'Sunday' is 3", - 3, dist); - } - - public void testBothEmpty() { - final int dist = BinaryDictionaryUtils.editDistance("", ""); - assertEquals("when both string are empty, no edits are needed", - 0, dist); - } - - public void testFirstArgIsEmpty() { - final int dist = BinaryDictionaryUtils.editDistance("", "aaaa"); - assertEquals("when only one string of the arguments is empty," - + " the edit distance is the length of the other.", - 4, dist); - } - - public void testSecoondArgIsEmpty() { - final int dist = BinaryDictionaryUtils.editDistance("aaaa", ""); - assertEquals("when only one string of the arguments is empty," - + " the edit distance is the length of the other.", - 4, dist); - } - - public void testSameStrings() { - final String arg1 = "The quick brown fox jumps over the lazy dog."; - final String arg2 = "The quick brown fox jumps over the lazy dog."; - final int dist = BinaryDictionaryUtils.editDistance(arg1, arg2); - assertEquals("when same strings are passed, distance equals 0.", - 0, dist); - } - - public void testSameReference() { - final String arg = "The quick brown fox jumps over the lazy dog."; - final int dist = BinaryDictionaryUtils.editDistance(arg, arg); - assertEquals("when same string references are passed, the distance equals 0.", - 0, dist); - } - - public void testNullArg() { - try { - BinaryDictionaryUtils.editDistance(null, "aaa"); - fail("IllegalArgumentException should be thrown."); - } catch (Exception e) { - assertTrue(e instanceof IllegalArgumentException); - } - try { - BinaryDictionaryUtils.editDistance("aaa", null); - fail("IllegalArgumentException should be thrown."); - } catch (Exception e) { - assertTrue(e instanceof IllegalArgumentException); - } - } -} |