diff options
Diffstat (limited to 'java/src')
14 files changed, 319 insertions, 178 deletions
diff --git a/java/src/com/android/inputmethod/compat/CursorAnchorInfoCompatWrapper.java b/java/src/com/android/inputmethod/compat/CursorAnchorInfoCompatWrapper.java index 5af31795c..f4f54b624 100644 --- a/java/src/com/android/inputmethod/compat/CursorAnchorInfoCompatWrapper.java +++ b/java/src/com/android/inputmethod/compat/CursorAnchorInfoCompatWrapper.java @@ -16,13 +16,20 @@ package com.android.inputmethod.compat; +import android.annotation.TargetApi; import android.graphics.Matrix; import android.graphics.RectF; +import android.os.Build; +import android.view.inputmethod.CursorAnchorInfo; -import com.android.inputmethod.annotations.UsedForTesting; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; -@UsedForTesting -public final class CursorAnchorInfoCompatWrapper { +/** + * A wrapper for {@link CursorAnchorInfo}, which has been introduced in API Level 21. You can use + * this wrapper to avoid direct dependency on newly introduced types. + */ +public class CursorAnchorInfoCompatWrapper { /** * The insertion marker or character bounds have at least one visible region. @@ -39,123 +46,138 @@ public final class CursorAnchorInfoCompatWrapper { */ public static final int FLAG_IS_RTL = 0x04; - // Note that CursorAnchorInfo has been introduced in API level XX (Build.VERSION_CODE.LXX). - private static final CompatUtils.ClassWrapper sCursorAnchorInfoClass; - private static final CompatUtils.ToIntMethodWrapper sGetSelectionStartMethod; - private static final CompatUtils.ToIntMethodWrapper sGetSelectionEndMethod; - private static final CompatUtils.ToObjectMethodWrapper<RectF> sGetCharacterBoundsMethod; - private static final CompatUtils.ToIntMethodWrapper sGetCharacterBoundsFlagsMethod; - private static final CompatUtils.ToObjectMethodWrapper<CharSequence> sGetComposingTextMethod; - private static final CompatUtils.ToIntMethodWrapper sGetComposingTextStartMethod; - private static final CompatUtils.ToFloatMethodWrapper sGetInsertionMarkerBaselineMethod; - private static final CompatUtils.ToFloatMethodWrapper sGetInsertionMarkerBottomMethod; - private static final CompatUtils.ToFloatMethodWrapper sGetInsertionMarkerHorizontalMethod; - private static final CompatUtils.ToFloatMethodWrapper sGetInsertionMarkerTopMethod; - private static final CompatUtils.ToObjectMethodWrapper<Matrix> sGetMatrixMethod; - private static final CompatUtils.ToIntMethodWrapper sGetInsertionMarkerFlagsMethod; - - private static int INVALID_TEXT_INDEX = -1; - static { - sCursorAnchorInfoClass = CompatUtils.getClassWrapper( - "android.view.inputmethod.CursorAnchorInfo"); - sGetSelectionStartMethod = sCursorAnchorInfoClass.getPrimitiveMethod( - "getSelectionStart", INVALID_TEXT_INDEX); - sGetSelectionEndMethod = sCursorAnchorInfoClass.getPrimitiveMethod( - "getSelectionEnd", INVALID_TEXT_INDEX); - sGetCharacterBoundsMethod = sCursorAnchorInfoClass.getMethod( - "getCharacterBounds", (RectF)null, int.class); - sGetCharacterBoundsFlagsMethod = sCursorAnchorInfoClass.getPrimitiveMethod( - "getCharacterBoundsFlags", 0, int.class); - sGetComposingTextMethod = sCursorAnchorInfoClass.getMethod( - "getComposingText", (CharSequence)null); - sGetComposingTextStartMethod = sCursorAnchorInfoClass.getPrimitiveMethod( - "getComposingTextStart", INVALID_TEXT_INDEX); - sGetInsertionMarkerBaselineMethod = sCursorAnchorInfoClass.getPrimitiveMethod( - "getInsertionMarkerBaseline", 0.0f); - sGetInsertionMarkerBottomMethod = sCursorAnchorInfoClass.getPrimitiveMethod( - "getInsertionMarkerBottom", 0.0f); - sGetInsertionMarkerHorizontalMethod = sCursorAnchorInfoClass.getPrimitiveMethod( - "getInsertionMarkerHorizontal", 0.0f); - sGetInsertionMarkerTopMethod = sCursorAnchorInfoClass.getPrimitiveMethod( - "getInsertionMarkerTop", 0.0f); - sGetMatrixMethod = sCursorAnchorInfoClass.getMethod("getMatrix", (Matrix)null); - sGetInsertionMarkerFlagsMethod = sCursorAnchorInfoClass.getPrimitiveMethod( - "getInsertionMarkerFlags", 0); - } - - @UsedForTesting - public boolean isAvailable() { - return sCursorAnchorInfoClass.exists() && mInstance != null; - } - - private Object mInstance; - - private CursorAnchorInfoCompatWrapper(final Object instance) { - mInstance = instance; + private CursorAnchorInfoCompatWrapper() { + // This class is not publicly instantiable. } - @UsedForTesting - public static CursorAnchorInfoCompatWrapper fromObject(final Object instance) { - if (!sCursorAnchorInfoClass.exists()) { - return new CursorAnchorInfoCompatWrapper(null); + @TargetApi(BuildCompatUtils.VERSION_CODES_LXX) + @Nullable + public static CursorAnchorInfoCompatWrapper wrap(@Nullable final CursorAnchorInfo instance) { + if (Build.VERSION.SDK_INT < BuildCompatUtils.VERSION_CODES_LXX) { + return null; } - return new CursorAnchorInfoCompatWrapper(instance); - } - - private static final class FakeHolder { - static CursorAnchorInfoCompatWrapper sInstance = new CursorAnchorInfoCompatWrapper(null); - } - - @UsedForTesting - public static CursorAnchorInfoCompatWrapper getFake() { - return FakeHolder.sInstance; + if (instance == null) { + return null; + } + return new RealWrapper(instance); } public int getSelectionStart() { - return sGetSelectionStartMethod.invoke(mInstance); + throw new UnsupportedOperationException("not supported."); } public int getSelectionEnd() { - return sGetSelectionEndMethod.invoke(mInstance); + throw new UnsupportedOperationException("not supported."); } public CharSequence getComposingText() { - return sGetComposingTextMethod.invoke(mInstance); + throw new UnsupportedOperationException("not supported."); } public int getComposingTextStart() { - return sGetComposingTextStartMethod.invoke(mInstance); + throw new UnsupportedOperationException("not supported."); } public Matrix getMatrix() { - return sGetMatrixMethod.invoke(mInstance); + throw new UnsupportedOperationException("not supported."); } public RectF getCharacterBounds(final int index) { - return sGetCharacterBoundsMethod.invoke(mInstance, index); + throw new UnsupportedOperationException("not supported."); } public int getCharacterBoundsFlags(final int index) { - return sGetCharacterBoundsFlagsMethod.invoke(mInstance, index); + throw new UnsupportedOperationException("not supported."); } public float getInsertionMarkerBaseline() { - return sGetInsertionMarkerBaselineMethod.invoke(mInstance); + throw new UnsupportedOperationException("not supported."); } public float getInsertionMarkerBottom() { - return sGetInsertionMarkerBottomMethod.invoke(mInstance); + throw new UnsupportedOperationException("not supported."); } public float getInsertionMarkerHorizontal() { - return sGetInsertionMarkerHorizontalMethod.invoke(mInstance); + throw new UnsupportedOperationException("not supported."); } public float getInsertionMarkerTop() { - return sGetInsertionMarkerTopMethod.invoke(mInstance); + throw new UnsupportedOperationException("not supported."); } public int getInsertionMarkerFlags() { - return sGetInsertionMarkerFlagsMethod.invoke(mInstance); + throw new UnsupportedOperationException("not supported."); + } + + @TargetApi(BuildCompatUtils.VERSION_CODES_LXX) + private static final class RealWrapper extends CursorAnchorInfoCompatWrapper { + + @Nonnull + private final CursorAnchorInfo mInstance; + + public RealWrapper(@Nonnull final CursorAnchorInfo info) { + mInstance = info; + } + + @Override + public int getSelectionStart() { + return mInstance.getSelectionStart(); + } + + @Override + public int getSelectionEnd() { + return mInstance.getSelectionEnd(); + } + + @Override + public CharSequence getComposingText() { + return mInstance.getComposingText(); + } + + @Override + public int getComposingTextStart() { + return mInstance.getComposingTextStart(); + } + + @Override + public Matrix getMatrix() { + return mInstance.getMatrix(); + } + + @Override + public RectF getCharacterBounds(final int index) { + return mInstance.getCharacterBounds(index); + } + + @Override + public int getCharacterBoundsFlags(final int index) { + return mInstance.getCharacterBoundsFlags(index); + } + + @Override + public float getInsertionMarkerBaseline() { + return mInstance.getInsertionMarkerBaseline(); + } + + @Override + public float getInsertionMarkerBottom() { + return mInstance.getInsertionMarkerBottom(); + } + + @Override + public float getInsertionMarkerHorizontal() { + return mInstance.getInsertionMarkerHorizontal(); + } + + @Override + public float getInsertionMarkerTop() { + return mInstance.getInsertionMarkerTop(); + } + + @Override + public int getInsertionMarkerFlags() { + return mInstance.getInsertionMarkerFlags(); + } } } diff --git a/java/src/com/android/inputmethod/compat/ViewCompatUtils.java b/java/src/com/android/inputmethod/compat/ViewCompatUtils.java index 0f00be133..16260ab6a 100644 --- a/java/src/com/android/inputmethod/compat/ViewCompatUtils.java +++ b/java/src/com/android/inputmethod/compat/ViewCompatUtils.java @@ -31,9 +31,6 @@ public final class ViewCompatUtils { private static final Method METHOD_setPaddingRelative = CompatUtils.getMethod( View.class, "setPaddingRelative", int.class, int.class, int.class, int.class); - // Note that View.setElevation(float) has been introduced in API level 21. - private static final Method METHOD_setElevation = CompatUtils.getMethod( - View.class, "setElevation", float.class); // Note that View.setTextAlignment(int) has been introduced in API level 17. private static final Method METHOD_setTextAlignment = CompatUtils.getMethod( View.class, "setTextAlignment", int.class); @@ -58,10 +55,6 @@ public final class ViewCompatUtils { CompatUtils.invoke(view, null, METHOD_setPaddingRelative, start, top, end, bottom); } - public static void setElevation(final View view, final float elevation) { - CompatUtils.invoke(view, null, METHOD_setElevation, elevation); - } - // These TEXT_ALIGNMENT_* constants have been introduced in API 17. public static final int TEXT_ALIGNMENT_INHERIT = 0; public static final int TEXT_ALIGNMENT_GRAVITY = 1; diff --git a/java/src/com/android/inputmethod/compat/ViewOutlineProviderCompatUtils.java b/java/src/com/android/inputmethod/compat/ViewOutlineProviderCompatUtils.java new file mode 100644 index 000000000..52b8b74e8 --- /dev/null +++ b/java/src/com/android/inputmethod/compat/ViewOutlineProviderCompatUtils.java @@ -0,0 +1,42 @@ +/* + * 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.compat; + +import android.inputmethodservice.InputMethodService; +import android.view.View; + +public class ViewOutlineProviderCompatUtils { + private ViewOutlineProviderCompatUtils() { + // This utility class is not publicly instantiable. + } + + public interface InsetsUpdater { + public void setInsets(final InputMethodService.Insets insets); + } + + private static final InsetsUpdater EMPTY_INSETS_UPDATER = new InsetsUpdater() { + @Override + public void setInsets(final InputMethodService.Insets insets) {} + }; + + public static InsetsUpdater setInsetsOutlineProvider(final View view) { + if (BuildCompatUtils.EFFECTIVE_SDK_INT < BuildCompatUtils.VERSION_CODES_LXX) { + return EMPTY_INSETS_UPDATER; + } + return ViewOutlineProviderCompatUtilsLXX.setInsetsOutlineProvider(view); + } +} diff --git a/java/src/com/android/inputmethod/compat/ViewOutlineProviderCompatUtilsLXX.java b/java/src/com/android/inputmethod/compat/ViewOutlineProviderCompatUtilsLXX.java new file mode 100644 index 000000000..f9917ac11 --- /dev/null +++ b/java/src/com/android/inputmethod/compat/ViewOutlineProviderCompatUtilsLXX.java @@ -0,0 +1,72 @@ +/* + * 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.compat; + +import android.annotation.TargetApi; +import android.graphics.Outline; +import android.inputmethodservice.InputMethodService; +import android.os.Build; +import android.view.View; +import android.view.ViewOutlineProvider; + +import com.android.inputmethod.compat.ViewOutlineProviderCompatUtils.InsetsUpdater; + +@TargetApi(Build.VERSION_CODES.L) +class ViewOutlineProviderCompatUtilsLXX { + private ViewOutlineProviderCompatUtilsLXX() { + // This utility class is not publicly instantiable. + } + + static InsetsUpdater setInsetsOutlineProvider(final View view) { + final InsetsOutlineProvider provider = new InsetsOutlineProvider(view); + view.setOutlineProvider(provider); + return provider; + } + + private static class InsetsOutlineProvider extends ViewOutlineProvider + implements InsetsUpdater { + private final View mView; + private static final int NO_DATA = -1; + private int mLastVisibleTopInsets = NO_DATA; + + public InsetsOutlineProvider(final View view) { + mView = view; + view.setOutlineProvider(this); + } + + @Override + public void setInsets(final InputMethodService.Insets insets) { + final int visibleTopInsets = insets.visibleTopInsets; + if (mLastVisibleTopInsets != visibleTopInsets) { + mLastVisibleTopInsets = visibleTopInsets; + mView.invalidateOutline(); + } + } + + @Override + public void getOutline(final View view, final Outline outline) { + if (mLastVisibleTopInsets == NO_DATA) { + // Call default implementation. + ViewOutlineProvider.BACKGROUND.getOutline(view, outline); + return; + } + // TODO: Revisit this when floating/resize keyboard is supported. + outline.setRect( + view.getLeft(), mLastVisibleTopInsets, view.getRight(), view.getBottom()); + } + } +} diff --git a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java index ad30b746e..e7be6de4c 100644 --- a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java @@ -146,7 +146,6 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack // More keys keyboard private final Paint mBackgroundDimAlphaPaint = new Paint(); - private boolean mNeedsToDimEntireKeyboard; private final View mMoreKeysKeyboardContainer; private final View mMoreKeysKeyboardForActionContainer; private final WeakHashMap<Key, Keyboard> mMoreKeysKeyboardCache = new WeakHashMap<>(); @@ -673,7 +672,6 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack locatePreviewPlacerView(); panel.showInParent(mDrawingPreviewPlacerView); mMoreKeysPanel = panel; - dimEntireKeyboard(true /* dimmed */); } public boolean isShowingMoreKeysPanel() { @@ -687,7 +685,6 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack @Override public void onDismissMoreKeysPanel() { - dimEntireKeyboard(false /* dimmed */); if (isShowingMoreKeysPanel()) { mMoreKeysPanel.removeFromParent(); mMoreKeysPanel = null; @@ -815,24 +812,6 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack invalidateKey(mSpaceKey); } - private void dimEntireKeyboard(final boolean dimmed) { - final boolean needsRedrawing = mNeedsToDimEntireKeyboard != dimmed; - mNeedsToDimEntireKeyboard = dimmed; - if (needsRedrawing) { - invalidateAllKeys(); - } - } - - @Override - protected void onDraw(final Canvas canvas) { - super.onDraw(canvas); - - // Overlay a dark rectangle to dim. - if (mNeedsToDimEntireKeyboard) { - canvas.drawRect(0.0f, 0.0f, getWidth(), getHeight(), mBackgroundDimAlphaPaint); - } - } - @Override protected void onDrawKeyTopVisuals(final Key key, final Canvas canvas, final Paint paint, final KeyDrawParams params) { diff --git a/java/src/com/android/inputmethod/keyboard/TextDecorator.java b/java/src/com/android/inputmethod/keyboard/TextDecorator.java index c22717f95..ddc65bf36 100644 --- a/java/src/com/android/inputmethod/keyboard/TextDecorator.java +++ b/java/src/com/android/inputmethod/keyboard/TextDecorator.java @@ -30,6 +30,7 @@ import com.android.inputmethod.compat.CursorAnchorInfoCompatWrapper; import com.android.inputmethod.latin.utils.LeakGuardHandlerWrapper; import javax.annotation.Nonnull; +import javax.annotation.Nullable; /** * A controller class of the add-to-dictionary indicator (a.k.a. TextDecorator). This class @@ -56,6 +57,7 @@ public class TextDecorator { private String mWaitingWord = null; private int mWaitingCursorStart = INVALID_CURSOR_INDEX; private int mWaitingCursorEnd = INVALID_CURSOR_INDEX; + @Nullable private CursorAnchorInfoCompatWrapper mCursorAnchorInfoWrapper = null; @Nonnull @@ -150,7 +152,7 @@ public class TextDecorator { * mode.</p> * @param info the compatibility wrapper object for the received {@link CursorAnchorInfo}. */ - public void onUpdateCursorAnchorInfo(final CursorAnchorInfoCompatWrapper info) { + public void onUpdateCursorAnchorInfo(@Nullable final CursorAnchorInfoCompatWrapper info) { mCursorAnchorInfoWrapper = info; // Do not use layoutLater() to minimize the latency. layoutImmediately(); @@ -182,7 +184,7 @@ public class TextDecorator { private void layoutMain() { final CursorAnchorInfoCompatWrapper info = mCursorAnchorInfoWrapper; - if (info == null || !info.isAvailable()) { + if (info == null) { cancelLayoutInternalExpectedly("CursorAnchorInfo isn't available."); return; } diff --git a/java/src/com/android/inputmethod/latin/Dictionary.java b/java/src/com/android/inputmethod/latin/Dictionary.java index 43561ba4b..e66847b56 100644 --- a/java/src/com/android/inputmethod/latin/Dictionary.java +++ b/java/src/com/android/inputmethod/latin/Dictionary.java @@ -16,6 +16,7 @@ package com.android.inputmethod.latin; +import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.keyboard.ProximityInfo; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import com.android.inputmethod.latin.settings.SettingsValuesForSuggestion; @@ -36,19 +37,19 @@ public abstract class Dictionary { // The following types do not actually come from real dictionary instances, so we create // corresponding instances. public static final String TYPE_USER_TYPED = "user_typed"; - public static final Dictionary DICTIONARY_USER_TYPED = new PhonyDictionary(TYPE_USER_TYPED); + public static final PhonyDictionary DICTIONARY_USER_TYPED = new PhonyDictionary(TYPE_USER_TYPED); public static final String TYPE_APPLICATION_DEFINED = "application_defined"; - public static final Dictionary DICTIONARY_APPLICATION_DEFINED = + public static final PhonyDictionary DICTIONARY_APPLICATION_DEFINED = new PhonyDictionary(TYPE_APPLICATION_DEFINED); public static final String TYPE_HARDCODED = "hardcoded"; // punctuation signs and such - public static final Dictionary DICTIONARY_HARDCODED = + public static final PhonyDictionary DICTIONARY_HARDCODED = new PhonyDictionary(TYPE_HARDCODED); // Spawned by resuming suggestions. Comes from a span that was in the TextView. public static final String TYPE_RESUMED = "resumed"; - public static final Dictionary DICTIONARY_RESUMED = + public static final PhonyDictionary DICTIONARY_RESUMED = new PhonyDictionary(TYPE_RESUMED); // The following types of dictionary have actual functional instances. We don't need final @@ -182,9 +183,10 @@ public abstract class Dictionary { * Not a true dictionary. A placeholder used to indicate suggestions that don't come from any * real dictionary. */ - private static class PhonyDictionary extends Dictionary { - // This class is not publicly instantiable. - private PhonyDictionary(final String type) { + @UsedForTesting + static class PhonyDictionary extends Dictionary { + @UsedForTesting + PhonyDictionary(final String type) { super(type, null); } diff --git a/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java b/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java index 6a63bfda7..08035dfd6 100644 --- a/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java +++ b/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java @@ -257,6 +257,12 @@ public class DictionaryFacilitator { } public void switchMostProbableLanguage(final Locale locale) { + if (null == locale) { + // In many cases, there is no locale to a committed word. For example, a typed word + // that does not auto-correct has no locale. In this case we simply do not change + // the most probable language. + return; + } final DictionaryGroup newMostProbableDictionaryGroup = findDictionaryGroupWithLocale(mDictionaryGroups, locale); mMostProbableDictionaryGroup.mWeightForTypingInLocale = diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 77477d2d3..d21fa167a 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -60,6 +60,8 @@ import com.android.inputmethod.accessibility.AccessibilityUtils; import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.compat.CursorAnchorInfoCompatWrapper; import com.android.inputmethod.compat.InputMethodServiceCompatUtils; +import com.android.inputmethod.compat.ViewOutlineProviderCompatUtils; +import com.android.inputmethod.compat.ViewOutlineProviderCompatUtils.InsetsUpdater; import com.android.inputmethod.dictionarypack.DictionaryPackConstants; import com.android.inputmethod.event.Event; import com.android.inputmethod.event.HardwareEventDecoder; @@ -154,6 +156,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // TODO: Move these {@link View}s to {@link KeyboardSwitcher}. private View mInputView; + private InsetsUpdater mInsetsUpdater; private SuggestionStripView mSuggestionStripView; private TextView mExtractEditText; @@ -754,6 +757,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen public void setInputView(final View view) { super.setInputView(view); mInputView = view; + mInsetsUpdater = ViewOutlineProviderCompatUtils.setInsetsOutlineProvider(view); updateSoftInputWindowLayoutParameters(); mSuggestionStripView = (SuggestionStripView)view.findViewById(R.id.suggestion_strip_view); if (hasSuggestionStripView()) { @@ -791,23 +795,17 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen new ViewTreeObserver.OnPreDrawListener() { @Override public boolean onPreDraw() { - onExtractTextViewPreDraw(); + // CursorAnchorInfo is used on L and later. + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.L) { + if (isFullscreenMode() && mExtractEditText != null) { + mInputLogic.onUpdateCursorAnchorInfo( + CursorAnchorInfoUtils.extractFromTextView(mExtractEditText)); + } + } return true; } }; - private void onExtractTextViewPreDraw() { - // CursorAnchorInfo is available on L and later. - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.L) { - return; - } - if (!isFullscreenMode() || mExtractEditText == null) { - return; - } - final CursorAnchorInfo info = CursorAnchorInfoUtils.getCursorAnchorInfo(mExtractEditText); - mInputLogic.onUpdateCursorAnchorInfo(CursorAnchorInfoCompatWrapper.fromObject(info)); - } - @Override public void setCandidatesView(final View view) { // To ensure that CandidatesView will never be set. @@ -1090,7 +1088,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen if (isFullscreenMode()) { return; } - mInputLogic.onUpdateCursorAnchorInfo(CursorAnchorInfoCompatWrapper.fromObject(info)); + mInputLogic.onUpdateCursorAnchorInfo(CursorAnchorInfoCompatWrapper.wrap(info)); } /** @@ -1191,6 +1189,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // no visual element will be shown on the screen. outInsets.touchableInsets = inputHeight; outInsets.visibleTopInsets = inputHeight; + mInsetsUpdater.setInsets(outInsets); return; } final int suggestionsHeight = (!mKeyboardSwitcher.isShowingEmojiPalettes() @@ -1211,6 +1210,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } outInsets.contentTopInsets = visibleTopY; outInsets.visibleTopInsets = visibleTopY; + mInsetsUpdater.setInsets(outInsets); } public void startShowingInputView(final boolean needsToLoadKeyboard) { @@ -1539,7 +1539,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private void setSuggestedWords(final SuggestedWords suggestedWords) { final SettingsValues currentSettingsValues = mSettings.getCurrent(); - mInputLogic.setSuggestedWords(suggestedWords, currentSettingsValues, mHandler); + mInputLogic.setSuggestedWords(suggestedWords); // TODO: Modify this when we support suggestions with hard keyboard if (!hasSuggestionStripView()) { return; diff --git a/java/src/com/android/inputmethod/latin/RichInputMethodManager.java b/java/src/com/android/inputmethod/latin/RichInputMethodManager.java index 0d5ce7d6d..b4ec8d674 100644 --- a/java/src/com/android/inputmethod/latin/RichInputMethodManager.java +++ b/java/src/com/android/inputmethod/latin/RichInputMethodManager.java @@ -29,6 +29,7 @@ import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodSubtype; import com.android.inputmethod.compat.InputMethodManagerCompatWrapper; +import com.android.inputmethod.latin.settings.AdditionalFeaturesSettingUtils; import com.android.inputmethod.latin.settings.Settings; import com.android.inputmethod.latin.utils.AdditionalSubtypeUtils; import com.android.inputmethod.latin.utils.SubtypeLocaleUtils; @@ -304,8 +305,7 @@ public class RichInputMethodManager { if (currentSubtype == null) { return defaultSubtype; } - // TODO: Determine locales to use for multi-lingual use. - return new RichInputMethodSubtype(currentSubtype); + return AdditionalFeaturesSettingUtils.getRichInputMethodSubtype(this, currentSubtype); } public boolean hasMultipleEnabledIMEsOrSubtypes(final boolean shouldIncludeAuxiliarySubtypes) { diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java index 157bd1565..5eb338eb3 100644 --- a/java/src/com/android/inputmethod/latin/WordComposer.java +++ b/java/src/com/android/inputmethod/latin/WordComposer.java @@ -18,6 +18,7 @@ package com.android.inputmethod.latin; import com.android.inputmethod.event.CombinerChain; import com.android.inputmethod.event.Event; +import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import com.android.inputmethod.latin.define.DebugFlags; import com.android.inputmethod.latin.utils.CoordinateUtils; import com.android.inputmethod.latin.utils.StringUtils; @@ -48,8 +49,7 @@ public final class WordComposer { // The list of events that served to compose this string. private final ArrayList<Event> mEvents; private final InputPointers mInputPointers = new InputPointers(MAX_WORD_LENGTH); - private String mAutoCorrection; - private String mAutoCorrectionDictionaryType; + private SuggestedWordInfo mAutoCorrection; private boolean mIsResumed; private boolean mIsBatchMode; // A memory of the last rejected batch mode suggestion, if any. This goes like this: the user @@ -418,26 +418,18 @@ public final class WordComposer { /** * Sets the auto-correction for this word. */ - public void setAutoCorrection(final String correction, String dictType) { - mAutoCorrection = correction; - mAutoCorrectionDictionaryType = dictType; + public void setAutoCorrection(final SuggestedWordInfo autoCorrection) { + mAutoCorrection = autoCorrection; } /** * @return the auto-correction for this word, or null if none. */ - public String getAutoCorrectionOrNull() { + public SuggestedWordInfo getAutoCorrectionOrNull() { return mAutoCorrection; } /** - * @return the auto-correction dictionary type or null if none. - */ - public String getAutoCorrectionDictionaryTypeOrNull() { - return mAutoCorrectionDictionaryType; - } - - /** * @return whether we started composing this word by resuming suggestion on an existing string */ public boolean isResumed() { diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java index 07bfd0dee..f23ce7f34 100644 --- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java +++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java @@ -305,6 +305,7 @@ public final class InputLogic { currentKeyboardScriptId, handler); } + mDictionaryFacilitator.switchMostProbableLanguage(suggestionInfo.mSourceDict.mLocale); final Event event = Event.createSuggestionPickedEvent(suggestionInfo); final InputTransaction inputTransaction = new InputTransaction(settingsValues, event, SystemClock.uptimeMillis(), mSpaceState, keyboardShiftState); @@ -607,25 +608,21 @@ public final class InputLogic { // TODO: on the long term, this method should become private, but it will be difficult. // Especially, how do we deal with InputMethodService.onDisplayCompletions? - public void setSuggestedWords(final SuggestedWords suggestedWords, - final SettingsValues settingsValues, final LatinIME.UIHandler handler) { + public void setSuggestedWords(final SuggestedWords suggestedWords) { if (!suggestedWords.isEmpty()) { - final String autoCorrection; - final String dictType; + final SuggestedWordInfo suggestedWordInfo; if (suggestedWords.mWillAutoCorrect) { - SuggestedWordInfo info = suggestedWords.getInfo( - SuggestedWords.INDEX_OF_AUTO_CORRECTION); - autoCorrection = info.mWord; - dictType = info.mSourceDict.mDictType; + suggestedWordInfo = suggestedWords.getInfo(SuggestedWords.INDEX_OF_AUTO_CORRECTION); } else { // We can't use suggestedWords.getWord(SuggestedWords.INDEX_OF_TYPED_WORD) // because it may differ from mWordComposer.mTypedWord. - autoCorrection = suggestedWords.mTypedWord; - dictType = Dictionary.TYPE_USER_TYPED; + suggestedWordInfo = new SuggestedWordInfo(suggestedWords.mTypedWord, + SuggestedWordInfo.MAX_SCORE, + SuggestedWordInfo.KIND_TYPED, Dictionary.DICTIONARY_USER_TYPED, + SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */, + SuggestedWordInfo.NOT_A_CONFIDENCE /* autoCommitFirstWordConfidence */); } - // TODO: Use the SuggestedWordInfo to set the auto correction when - // user typed word is available via SuggestedWordInfo. - mWordComposer.setAutoCorrection(autoCorrection, dictType); + mWordComposer.setAutoCorrection(suggestedWordInfo); } mSuggestedWords = suggestedWords; final boolean newAutoCorrectionIndicator = suggestedWords.mWillAutoCorrect; @@ -2092,19 +2089,23 @@ public final class InputLogic { // INPUT_STYLE_TYPING. performUpdateSuggestionStripSync(settingsValues, SuggestedWords.INPUT_STYLE_TYPING); } - final String typedAutoCorrection = mWordComposer.getAutoCorrectionOrNull(); + final SuggestedWordInfo autoCorrectionOrNull = mWordComposer.getAutoCorrectionOrNull(); final String typedWord = mWordComposer.getTypedWord(); - final String autoCorrection = (typedAutoCorrection != null) - ? typedAutoCorrection : typedWord; - if (autoCorrection != null) { + final String stringToCommit = (autoCorrectionOrNull != null) + ? autoCorrectionOrNull.mWord : typedWord; + if (stringToCommit != null) { if (TextUtils.isEmpty(typedWord)) { throw new RuntimeException("We have an auto-correction but the typed word " + "is empty? Impossible! I must commit suicide."); } final boolean isBatchMode = mWordComposer.isBatchMode(); - commitChosenWord(settingsValues, autoCorrection, + commitChosenWord(settingsValues, stringToCommit, LastComposedWord.COMMIT_TYPE_DECIDED_WORD, separator); - if (!typedWord.equals(autoCorrection)) { + if (null != autoCorrectionOrNull) { + mDictionaryFacilitator.switchMostProbableLanguage( + autoCorrectionOrNull.mSourceDict.mLocale); + } + if (!typedWord.equals(stringToCommit)) { // This will make the correction flash for a short while as a visual clue // to the user that auto-correction happened. It has no other effect; in particular // note that this won't affect the text inside the text field AT ALL: it only makes @@ -2112,13 +2113,14 @@ public final class InputLogic { // of the auto-correction flash. At this moment, the "typedWord" argument is // ignored by TextView. mConnection.commitCorrection(new CorrectionInfo( - mConnection.getExpectedSelectionEnd() - autoCorrection.length(), - typedWord, autoCorrection)); - StatsUtils.onAutoCorrection(typedWord, autoCorrection, isBatchMode, - mWordComposer.getAutoCorrectionDictionaryTypeOrNull()); - StatsUtils.onWordCommitAutoCorrect(autoCorrection, isBatchMode); + mConnection.getExpectedSelectionEnd() - stringToCommit.length(), + typedWord, stringToCommit)); + StatsUtils.onAutoCorrection(typedWord, stringToCommit, isBatchMode, + null == autoCorrectionOrNull + ? null : autoCorrectionOrNull.mSourceDict.mDictType); + StatsUtils.onWordCommitAutoCorrect(stringToCommit, isBatchMode); } else { - StatsUtils.onWordCommitUserTyped(autoCorrection, isBatchMode); + StatsUtils.onWordCommitUserTyped(stringToCommit, isBatchMode); } } } diff --git a/java/src/com/android/inputmethod/latin/settings/AccountsSettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/AccountsSettingsFragment.java index a02cb5539..45792fe0e 100644 --- a/java/src/com/android/inputmethod/latin/settings/AccountsSettingsFragment.java +++ b/java/src/com/android/inputmethod/latin/settings/AccountsSettingsFragment.java @@ -28,6 +28,7 @@ import android.text.TextUtils; import android.widget.ListView; import android.widget.Toast; +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; @@ -157,10 +158,10 @@ public final class AccountsSettingsFragment extends SubScreenFragment { final String currentAccount = getCurrentlySelectedAccount(); if (currentAccount == null) { syncNowPreference.setEnabled(false); - syncNowPreference.setSummary(R.string.sync_now_summary); + syncNowPreference.setSummary(R.string.sync_now_summary_disabled_signed_out); } else { syncNowPreference.setEnabled(true); - syncNowPreference.setSummary(R.string.sync_now_summary_disabled_signed_out); + syncNowPreference.setSummary(R.string.sync_now_summary); } } @@ -176,6 +177,7 @@ public final class AccountsSettingsFragment extends SubScreenFragment { * * Package-private for testing. */ + @UsedForTesting AlertDialog createAccountPicker(final String[] accounts, final String selectedAccount) { if (accounts == null || accounts.length == 0) { diff --git a/java/src/com/android/inputmethod/latin/utils/CursorAnchorInfoUtils.java b/java/src/com/android/inputmethod/latin/utils/CursorAnchorInfoUtils.java index 9dc0524a2..e05618901 100644 --- a/java/src/com/android/inputmethod/latin/utils/CursorAnchorInfoUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/CursorAnchorInfoUtils.java @@ -16,10 +16,12 @@ package com.android.inputmethod.latin.utils; +import android.annotation.TargetApi; import android.graphics.Matrix; import android.graphics.Rect; import android.inputmethodservice.ExtractEditText; import android.inputmethodservice.InputMethodService; +import android.os.Build; import android.text.Layout; import android.text.Spannable; import android.view.View; @@ -27,6 +29,12 @@ import android.view.ViewParent; import android.view.inputmethod.CursorAnchorInfo; import android.widget.TextView; +import com.android.inputmethod.compat.BuildCompatUtils; +import com.android.inputmethod.compat.CursorAnchorInfoCompatWrapper; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + /** * This class allows input methods to extract {@link CursorAnchorInfo} directly from the given * {@link TextView}. This is useful and even necessary to support full-screen mode where the default @@ -77,13 +85,32 @@ public final class CursorAnchorInfoUtils { } /** + * Extracts {@link CursorAnchorInfoCompatWrapper} from the given {@link TextView}. + * @param textView the target text view from which {@link CursorAnchorInfoCompatWrapper} is to + * be extracted. + * @return the {@link CursorAnchorInfoCompatWrapper} object based on the current layout. + * {@code null} if {@code Build.VERSION.SDK_INT} is 20 or prior or {@link TextView} is not + * ready to provide layout information. + */ + @Nullable + public static CursorAnchorInfoCompatWrapper extractFromTextView( + @Nonnull final TextView textView) { + if (Build.VERSION.SDK_INT < BuildCompatUtils.VERSION_CODES_LXX) { + return null; + } + return CursorAnchorInfoCompatWrapper.wrap(extractFromTextViewInternal(textView)); + } + + /** * Returns {@link CursorAnchorInfo} from the given {@link TextView}. * @param textView the target text view from which {@link CursorAnchorInfo} is to be extracted. * @return the {@link CursorAnchorInfo} object based on the current layout. {@code null} if it * is not feasible. */ - public static CursorAnchorInfo getCursorAnchorInfo(final TextView textView) { - Layout layout = textView.getLayout(); + @TargetApi(BuildCompatUtils.VERSION_CODES_LXX) + @Nullable + private static CursorAnchorInfo extractFromTextViewInternal(@Nonnull final TextView textView) { + final Layout layout = textView.getLayout(); if (layout == null) { return null; } |