diff options
Diffstat (limited to 'java/src/com/android/inputmethod')
18 files changed, 349 insertions, 238 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..743b570ac 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; @@ -1627,7 +1627,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } @Override - public void showAddToDictionaryHint(final String word) { + public void suggestAddingToDictionary(final String word, final boolean isFromSuggestionStrip) { if (!hasSuggestionStripView()) { return; } @@ -1637,7 +1637,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } else { wordToShow = word; } - mSuggestionStripView.showAddToDictionaryHint(wordToShow); + mSuggestionStripView.showAddToDictionaryHint(wordToShow, + isFromSuggestionStrip /* shouldShowWordToSave */); } // This will show either an empty suggestion strip (if prediction is enabled) or 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/SuggestedWords.java b/java/src/com/android/inputmethod/latin/SuggestedWords.java index 466576465..e0c06925c 100644 --- a/java/src/com/android/inputmethod/latin/SuggestedWords.java +++ b/java/src/com/android/inputmethod/latin/SuggestedWords.java @@ -413,28 +413,6 @@ public class SuggestedWords { return isPrediction(mInputStyle); } - // SuggestedWords is an immutable object, as much as possible. We must not just remove - // words from the member ArrayList as some other parties may expect the object to never change. - // This is only ever called by recorrection at the moment, hence the ForRecorrection moniker. - public SuggestedWords getSuggestedWordsExcludingTypedWordForRecorrection() { - final ArrayList<SuggestedWordInfo> newSuggestions = new ArrayList<>(); - String typedWord = null; - for (int i = 0; i < mSuggestedWordInfoList.size(); ++i) { - final SuggestedWordInfo info = mSuggestedWordInfoList.get(i); - if (!info.isKindOf(SuggestedWordInfo.KIND_TYPED)) { - newSuggestions.add(info); - } else { - assert(null == typedWord); - typedWord = info.mWord; - } - } - // We should never autocorrect, so we say the typed word is valid. Also, in this case, - // no auto-correction should take place hence willAutoCorrect = false. - return new SuggestedWords(newSuggestions, null /* rawSuggestions */, typedWord, - true /* typedWordValid */, false /* willAutoCorrect */, mIsObsoleteSuggestions, - SuggestedWords.INPUT_STYLE_RECORRECTION, NOT_A_SEQUENCE_NUMBER); - } - // Creates a new SuggestedWordInfo from the currently suggested words that removes all but the // last word of all suggestions, separated by a space. This is necessary because when we commit // a multiple-word suggestion, the IME only retains the last word as the composing word, and 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..f67b8de84 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); @@ -348,7 +349,8 @@ public final class InputLogic { inputTransaction.requireShiftUpdate(InputTransaction.SHIFT_UPDATE_NOW); if (shouldShowAddToDictionaryHint) { - mSuggestionStripViewAccessor.showAddToDictionaryHint(suggestion); + mSuggestionStripViewAccessor.suggestAddingToDictionary(suggestion, + true /* isFromSuggestionStrip */); } else { // If we're not showing the "Touch again to save", then update the suggestion strip. // That's going to be predictions (or punctuation suggestions), so INPUT_STYLE_NONE. @@ -607,25 +609,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; @@ -1488,6 +1486,11 @@ public final class InputLogic { if (numberOfCharsInWordBeforeCursor > expectedCursorPosition) return; final ArrayList<SuggestedWordInfo> suggestions = new ArrayList<>(); final String typedWord = range.mWord.toString(); + suggestions.add(new SuggestedWordInfo(typedWord, + SuggestedWords.MAX_SUGGESTIONS + 1, + SuggestedWordInfo.KIND_TYPED, Dictionary.DICTIONARY_USER_TYPED, + SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */, + SuggestedWordInfo.NOT_A_CONFIDENCE /* autoCommitFirstWordConfidence */)); if (!isResumableWord(settingsValues, typedWord)) { mSuggestionStripViewAccessor.setNeutralSuggestionStrip(); return; @@ -1520,30 +1523,14 @@ public final class InputLogic { mConnection.maybeMoveTheCursorAroundAndRestoreToWorkaroundABug(); mConnection.setComposingRegion(expectedCursorPosition - numberOfCharsInWordBeforeCursor, expectedCursorPosition + range.getNumberOfCharsInWordAfterCursor()); - if (suggestions.size() <= 0) { + if (suggestions.size() <= 1) { // If there weren't any suggestion spans on this word, suggestions#size() will be 1 // if shouldIncludeResumedWordInSuggestions is true, 0 otherwise. In this case, we // have no useful suggestions, so we will try to compute some for it instead. mInputLogicHandler.getSuggestedWords(Suggest.SESSION_ID_TYPING, SuggestedWords.NOT_A_SEQUENCE_NUMBER, new OnGetSuggestedWordsCallback() { @Override - public void onGetSuggestedWords( - final SuggestedWords suggestedWordsIncludingTypedWord) { - final SuggestedWords suggestedWords; - if (suggestedWordsIncludingTypedWord.size() > 1) { - // We were able to compute new suggestions for this word. - // Remove the typed word, since we don't want to display it in this - // case. The #getSuggestedWordsExcludingTypedWordForRecorrection() - // method sets willAutoCorrect to false. - suggestedWords = suggestedWordsIncludingTypedWord - .getSuggestedWordsExcludingTypedWordForRecorrection(); - } else { - // No saved suggestions, and we were unable to compute any good one - // either. Rather than displaying an empty suggestion strip, we'll - // display the original word alone in the middle. - // Since there is only one word, willAutoCorrect is false. - suggestedWords = suggestedWordsIncludingTypedWord; - } + public void onGetSuggestedWords(final SuggestedWords suggestedWords) { mIsAutoCorrectionIndicatorOn = false; mLatinIME.mHandler.showSuggestionStrip(suggestedWords); }}); @@ -1687,7 +1674,8 @@ public final class InputLogic { mConnection.getExpectedSelectionStart(), mConnection.getExpectedSelectionEnd()); } - mSuggestionStripViewAccessor.showAddToDictionaryHint(originallyTypedWordString); + mSuggestionStripViewAccessor.suggestAddingToDictionary(originallyTypedWordString, + false /* isFromSuggestionStrip */); } else { // We have a separator between the word and the cursor: we should show predictions. inputTransaction.setRequiresUpdateSuggestions(); @@ -2092,19 +2080,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 +2104,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/suggestions/SuggestionStripLayoutHelper.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java index d55939971..0c8441454 100644 --- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java +++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java @@ -553,12 +553,12 @@ final class SuggestionStripLayoutHelper { return countInStrip; } - public void layoutAddToDictionaryHint(final String word, final ViewGroup addToDictionaryStrip) { - final boolean shouldShowUiToAcceptTypedWord = Settings.getInstance().getCurrent() - .mShouldShowLxxSuggestionUi; + public void layoutAddToDictionaryHint(final String word, final ViewGroup addToDictionaryStrip, + final boolean shouldShowWordToSave) { + final boolean showsHintWithWord = shouldShowWordToSave + || !Settings.getInstance().getCurrent().mShouldShowLxxSuggestionUi; final int stripWidth = addToDictionaryStrip.getWidth(); - final int width = shouldShowUiToAcceptTypedWord ? stripWidth - : stripWidth - mDividerWidth - mPadding * 2; + final int width = stripWidth - (showsHintWithWord ? mDividerWidth + mPadding * 2 : 0); final TextView wordView = (TextView)addToDictionaryStrip.findViewById(R.id.word_to_save); wordView.setTextColor(mColorTypedWord); @@ -569,7 +569,7 @@ final class SuggestionStripLayoutHelper { wordView.setText(wordToSave); wordView.setTextScaleX(wordScaleX); setLayoutWeight(wordView, mCenterSuggestionWeight, ViewGroup.LayoutParams.MATCH_PARENT); - final int wordVisibility = shouldShowUiToAcceptTypedWord ? View.GONE : View.VISIBLE; + final int wordVisibility = showsHintWithWord ? View.VISIBLE : View.GONE; wordView.setVisibility(wordVisibility); addToDictionaryStrip.findViewById(R.id.word_to_save_divider).setVisibility(wordVisibility); @@ -579,12 +579,7 @@ final class SuggestionStripLayoutHelper { final float hintWeight; final TextView hintView = (TextView)addToDictionaryStrip.findViewById( R.id.hint_add_to_dictionary); - if (shouldShowUiToAcceptTypedWord) { - hintText = res.getText(R.string.hint_add_to_dictionary_without_word); - hintWidth = width; - hintWeight = 1.0f; - hintView.setGravity(Gravity.CENTER); - } else { + if (showsHintWithWord) { final boolean isRtlLanguage = (ViewCompat.getLayoutDirection(addToDictionaryStrip) == ViewCompat.LAYOUT_DIRECTION_RTL); final String arrow = isRtlLanguage ? RIGHTWARDS_ARROW : LEFTWARDS_ARROW; @@ -595,6 +590,11 @@ final class SuggestionStripLayoutHelper { hintWidth = width - wordWidth; hintWeight = 1.0f - mCenterSuggestionWeight; hintView.setGravity(Gravity.CENTER_VERTICAL | Gravity.START); + } else { + hintText = res.getText(R.string.hint_add_to_dictionary_without_word); + hintWidth = width; + hintWeight = 1.0f; + hintView.setGravity(Gravity.CENTER); } hintView.setTextColor(mColorAutoCorrect); final float hintScaleX = getTextScaleX(hintText, hintWidth, hintView.getPaint()); diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java index e40fd8800..789d549d7 100644 --- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java +++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java @@ -231,8 +231,8 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick return mStripVisibilityGroup.isShowingAddToDictionaryStrip(); } - public void showAddToDictionaryHint(final String word) { - mLayoutHelper.layoutAddToDictionaryHint(word, mAddToDictionaryStrip); + public void showAddToDictionaryHint(final String word, final boolean shouldShowWordToSave) { + mLayoutHelper.layoutAddToDictionaryHint(word, mAddToDictionaryStrip, shouldShowWordToSave); // {@link TextView#setTag()} is used to hold the word to be added to dictionary. The word // will be extracted at {@link #onClick(View)}. mAddToDictionaryStrip.setTag(word); @@ -501,7 +501,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick return; } final Object tag = view.getTag(); - // {@link String} tag is set at {@link #showAddToDictionaryHint(String,CharSequence)}. + // {@link String} tag is set at {@link #suggestAddingToDictionary(String,CharSequence)}. if (tag instanceof String) { final String wordToSave = (String)tag; mListener.addWordToUserDictionary(wordToSave); diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripViewAccessor.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripViewAccessor.java index 52708455e..5c86a02af 100644 --- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripViewAccessor.java +++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripViewAccessor.java @@ -22,7 +22,7 @@ import com.android.inputmethod.latin.SuggestedWords; * An object that gives basic control of a suggestion strip and some info on it. */ public interface SuggestionStripViewAccessor { - public void showAddToDictionaryHint(final String word); + public void suggestAddingToDictionary(final String word, final boolean isFromSuggestionStrip); public boolean isShowingAddToDictionaryHint(); public void dismissAddToDictionaryHint(); public void setNeutralSuggestionStrip(); 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; } |