Merge "Simplify CursorAnchorInfoCompatWrapper"

main
Yohei Yukawa 2014-10-09 08:13:50 +00:00 committed by Android (Google) Code Review
commit 6547311865
4 changed files with 145 additions and 100 deletions

View File

@ -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);
private CursorAnchorInfoCompatWrapper() {
// This class is not publicly instantiable.
}
@UsedForTesting
public boolean isAvailable() {
return sCursorAnchorInfoClass.exists() && mInstance != null;
}
private Object mInstance;
private CursorAnchorInfoCompatWrapper(final Object instance) {
mInstance = instance;
}
@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();
}
}
}

View File

@ -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;
}

View File

@ -795,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.
@ -1094,7 +1088,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
if (isFullscreenMode()) {
return;
}
mInputLogic.onUpdateCursorAnchorInfo(CursorAnchorInfoCompatWrapper.fromObject(info));
mInputLogic.onUpdateCursorAnchorInfo(CursorAnchorInfoCompatWrapper.wrap(info));
}
/**

View File

@ -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
@ -76,14 +84,33 @@ public final class CursorAnchorInfoUtils {
return true;
}
/**
* 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;
}