Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0023d0e0 authored by Yohei Yukawa's avatar Yohei Yukawa
Browse files

Polish new IME API for L part 2: CursorAnchorInfo

This CL addresses feedbacks from internal customers for new
input method APIs that are mainly used for physical keyboard
support in L.

For performance reasons, #onUpdateCursorAnchorInfo is not called
back by default and each input method has to enable this
event notification explicitly whenever fine-grained character
locations are needed.

In L-preview, InputMethodSession#setCursorAnchorMonitorMode was
introduced for this purpose.  However, we got several feedbacks
to be addressed.
- The effect of #setCursorAnchorMonitorMode is not preserved
  during focus change.  IMEs need to call
  #setCursorAnchorMonitorMode every time when #onStartInput is
  called.  This is tricky and hard to understand.
- As #onUpdateCursorAnchorInfo is a new API, not all
  applications/text editors have supported it. Therefore IMEs
  can't always rely on it. However, there is no way to query
  if the attached target is supporting this new API or not.
  It would helpful for IME authors if we can provide a
  reliable way to query if the attached input target is
  supporting the new API or not.

In order to address these issues, the triggering method has
moved from InputMethodSession to InputConnection in this CL,
as an analogy of existing InputConnection#getExtractedText API,
which has provided similar functionality including optional
reactive event callbacks from the application to the IME.

BUG: 15812658
BUG: 16118603
Change-Id: I3c6b69bd9d79b199afe68d838f25effa6048e5cc
parent caebf6ff
Loading
Loading
Loading
Loading
+22 −3
Original line number Diff line number Diff line
@@ -13323,7 +13323,6 @@ package android.inputmethodservice {
    method public void setBackDisposition(int);
    method public void setCandidatesView(android.view.View);
    method public void setCandidatesViewShown(boolean);
    method public void setCursorAnchorMonitorMode(int);
    method public void setExtractView(android.view.View);
    method public void setExtractViewShown(boolean);
    method public void setInputView(android.view.View);
@@ -13335,8 +13334,6 @@ package android.inputmethodservice {
    field public static final int BACK_DISPOSITION_DEFAULT = 0; // 0x0
    field public static final int BACK_DISPOSITION_WILL_DISMISS = 2; // 0x2
    field public static final int BACK_DISPOSITION_WILL_NOT_DISMISS = 1; // 0x1
    field public static final int CURSOR_ANCHOR_MONITOR_MODE_CURSOR_RECT = 1; // 0x1
    field public static final int CURSOR_ANCHOR_MONITOR_MODE_NONE = 0; // 0x0
  }
  public class InputMethodService.InputMethodImpl extends android.inputmethodservice.AbstractInputMethodService.AbstractInputMethodImpl {
@@ -34934,6 +34931,7 @@ package android.view.inputmethod {
    method public boolean performPrivateCommand(java.lang.String, android.os.Bundle);
    method public static final void removeComposingSpans(android.text.Spannable);
    method public boolean reportFullscreenMode(boolean);
    method public int requestCursorAnchorInfo(android.view.inputmethod.CursorAnchorInfoRequest);
    method public boolean sendKeyEvent(android.view.KeyEvent);
    method public boolean setComposingRegion(int, int);
    method public static void setComposingSpans(android.text.Spannable);
@@ -34999,6 +34997,25 @@ package android.view.inputmethod {
    method public android.view.inputmethod.CursorAnchorInfo.Builder setSelectionRange(int, int);
  }
  public final class CursorAnchorInfoRequest implements android.os.Parcelable {
    ctor public CursorAnchorInfoRequest(int, int);
    ctor public CursorAnchorInfoRequest(android.os.Parcel);
    method public int describeContents();
    method public int getRequestFlags();
    method public int getRequestType();
    method public void writeToParcel(android.os.Parcel, int);
    field public static final android.os.Parcelable.Creator CREATOR;
    field public static final int FLAG_CURSOR_ANCHOR_INFO_IMMEDIATE = 2; // 0x2
    field public static final int FLAG_CURSOR_ANCHOR_INFO_MONITOR = 1; // 0x1
    field public static final int FLAG_CURSOR_RECT_IN_SCREEN_COORDINATES = 2; // 0x2
    field public static final int FLAG_CURSOR_RECT_MONITOR = 1; // 0x1
    field public static final int FLAG_CURSOR_RECT_WITH_VIEW_MATRIX = 4; // 0x4
    field public static final int RESULT_NOT_HANDLED = 0; // 0x0
    field public static final int RESULT_SCHEDULED = 1; // 0x1
    field public static final int TYPE_CURSOR_ANCHOR_INFO = 1; // 0x1
    field public static final int TYPE_CURSOR_RECT = 2; // 0x2
  }
  public class EditorInfo implements android.text.InputType android.os.Parcelable {
    ctor public EditorInfo();
    method public int describeContents();
@@ -35096,6 +35113,7 @@ package android.view.inputmethod {
    method public abstract boolean performEditorAction(int);
    method public abstract boolean performPrivateCommand(java.lang.String, android.os.Bundle);
    method public abstract boolean reportFullscreenMode(boolean);
    method public abstract int requestCursorAnchorInfo(android.view.inputmethod.CursorAnchorInfoRequest);
    method public abstract boolean sendKeyEvent(android.view.KeyEvent);
    method public abstract boolean setComposingRegion(int, int);
    method public abstract boolean setComposingText(java.lang.CharSequence, int);
@@ -35123,6 +35141,7 @@ package android.view.inputmethod {
    method public boolean performEditorAction(int);
    method public boolean performPrivateCommand(java.lang.String, android.os.Bundle);
    method public boolean reportFullscreenMode(boolean);
    method public int requestCursorAnchorInfo(android.view.inputmethod.CursorAnchorInfoRequest);
    method public boolean sendKeyEvent(android.view.KeyEvent);
    method public boolean setComposingRegion(int, int);
    method public boolean setComposingText(java.lang.CharSequence, int);
+4 −21
Original line number Diff line number Diff line
@@ -53,6 +53,7 @@ import android.view.WindowManager.BadTokenException;
import android.view.animation.AnimationUtils;
import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.CursorAnchorInfo;
import android.view.inputmethod.CursorAnchorInfoRequest;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.ExtractedText;
import android.view.inputmethod.ExtractedTextRequest;
@@ -251,18 +252,6 @@ public class InputMethodService extends AbstractInputMethodService {
     */
    public static final int IME_VISIBLE = 0x2;

    /**
     * The IME does not require cursor/anchor position.
     */
    public static final int CURSOR_ANCHOR_MONITOR_MODE_NONE = 0x0;

    /**
     * Passing this flag into a call to {@link #setCursorAnchorMonitorMode(int)} will result in
     * the cursor rectangle being provided in screen coordinates to subsequent
     * {@link #onUpdateCursor(Rect)} callbacks.
     */
    public static final int CURSOR_ANCHOR_MONITOR_MODE_CURSOR_RECT = 0x1;

    InputMethodManager mImm;
    
    int mTheme = 0;
@@ -1722,8 +1711,9 @@ public class InputMethodService extends AbstractInputMethodService {
     * Called when the application has reported a new location of its text cursor.  This is only
     * called if explicitly requested by the input method.  The default implementation does nothing.
     * @param newCursor The new cursor position, in screen coordinates if the input method calls
     * {@link #setCursorAnchorMonitorMode} with {@link #CURSOR_ANCHOR_MONITOR_MODE_CURSOR_RECT}.
     * Otherwise, this is in local coordinates.
     * {@link InputConnection#requestCursorAnchorInfo(CursorAnchorInfoRequest)} with
     * {@link CursorAnchorInfoRequest#FLAG_CURSOR_RECT_IN_SCREEN_COORDINATES}. Otherwise,
     * this is in local coordinates.
     */
    public void onUpdateCursor(Rect newCursor) {
        // Intentionally empty
@@ -1740,13 +1730,6 @@ public class InputMethodService extends AbstractInputMethodService {
        // Intentionally empty
    }

    /**
     * Update the cursor/anthor monitor mode.
     */
    public void setCursorAnchorMonitorMode(int monitorMode) {
        mImm.setCursorAnchorMonitorMode(mToken, monitorMode);
    }

    /**
     * Close this input method's soft input area, removing it from the display.
     * The input method will continue running, but the user can no longer use
+15 −0
Original line number Diff line number Diff line
@@ -428,6 +428,21 @@ public class BaseInputConnection implements InputConnection {
        return false;
    }

    /**
     * The default implementation is responsible for handling
     * {@link CursorAnchorInfoRequest#TYPE_CURSOR_RECT}. In fact, for derived classes, calling
     * {@code super.requestCursorAnchorInfo(request)} is the only way to handle
     * {@link CursorAnchorInfoRequest#TYPE_CURSOR_RECT}.
     */
    public int requestCursorAnchorInfo(CursorAnchorInfoRequest request) {
        if (request != null && mIMM != null &&
                request.getRequestType() == CursorAnchorInfoRequest.TYPE_CURSOR_RECT) {
            mIMM.setCursorRectMonitorMode(request.getRequestFlags());
            return CursorAnchorInfoRequest.RESULT_SCHEDULED;
        }
        return CursorAnchorInfoRequest.RESULT_NOT_HANDLED;
    }

    /**
     * The default implementation places the given text into the editable,
     * replacing any existing composing text.  The new text is marked as
+19 −0
Original line number Diff line number Diff line
/*
 * 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 android.view.inputmethod;

parcelable CursorAnchorInfoRequest;
+203 −0
Original line number Diff line number Diff line
/*
 * 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 android.view.inputmethod;

import android.inputmethodservice.InputMethodService;
import android.os.Parcel;
import android.os.Parcelable;
import android.view.View;

/**
 * Used to enable or disable event notification for
 * {@link InputMethodService#onUpdateCursorAnchorInfo(CursorAnchorInfo)}. This class is also used to
 * enable {@link InputMethodService#onUpdateCursor(android.graphics.Rect)} for existing editors
 * that have not supported {@link InputMethodService#onUpdateCursorAnchorInfo(CursorAnchorInfo)}.
 */
public final class CursorAnchorInfoRequest implements Parcelable {
    private final int mRequestType;
    private final int mRequestFlags;

    /**
     * Not handled by the editor.
     */
    public static final int RESULT_NOT_HANDLED = 0x00;
    /**
     * Request is scheduled in the editor task queue.
     */
    public static final int RESULT_SCHEDULED = 0x01;

    /**
     * The request is for {@link InputMethodService#onUpdateCursorAnchorInfo(CursorAnchorInfo)}.
     * This mechanism is powerful enough to retrieve fine-grained positional information of
     * characters in the editor.
     */
    public static final int TYPE_CURSOR_ANCHOR_INFO = 0x01;
    /**
     * The editor is requested to call
     * {@link InputMethodManager#updateCursorAnchorInfo(android.view.View, CursorAnchorInfo)}
     * whenever cursor/anchor position is changed. To disable monitoring, call
     * {@link InputConnection#requestCursorAnchorInfo(CursorAnchorInfoRequest)} again with
     * {@link #TYPE_CURSOR_ANCHOR_INFO} and this flag off.
     * <p>
     * This flag can be used together with {@link #FLAG_CURSOR_ANCHOR_INFO_IMMEDIATE}.
     * </p>
     */
    public static final int FLAG_CURSOR_ANCHOR_INFO_MONITOR = 0x01;
    /**
     * The editor is requested to call
     * {@link InputMethodManager#updateCursorAnchorInfo(android.view.View, CursorAnchorInfo)} at
     * once, as soon as possible, regardless of cursor/anchor position changes. This flag can be
     * used together with {@link #FLAG_CURSOR_ANCHOR_INFO_MONITOR}.
     */
    public static final int FLAG_CURSOR_ANCHOR_INFO_IMMEDIATE = 0x02;

    /**
     * The request is for {@link InputMethodService#onUpdateCursor(android.graphics.Rect)}. This
     * mechanism has been available since API Level 3 (CUPCAKE) but only the cursor rectangle can
     * be retrieved with this mechanism.
     */
    public static final int TYPE_CURSOR_RECT = 0x02;
    /**
     * The editor is requested to call
     * {@link InputMethodManager#updateCursor(android.view.View, int, int, int, int)}
     * whenever the cursor position is changed. To disable monitoring, call
     * {@link InputConnection#requestCursorAnchorInfo(CursorAnchorInfoRequest)} again with
     * {@link #TYPE_CURSOR_RECT} and this flag off.
     * <p>
     * This flag can be used together with {@link #FLAG_CURSOR_RECT_IN_SCREEN_COORDINATES}.
     * </p>
     */
    public static final int FLAG_CURSOR_RECT_MONITOR = 0x01;
    /**
     * {@link InputMethodManager#updateCursor(android.view.View, int, int, int, int)} should be
     * called back in screen coordinates. To receive cursor position in local coordinates, call
     * {@link InputConnection#requestCursorAnchorInfo(CursorAnchorInfoRequest)} again with
     * {@link #TYPE_CURSOR_RECT} and this flag off.
     */
    public static final int FLAG_CURSOR_RECT_IN_SCREEN_COORDINATES = 0x02;
    /**
     * {@link InputMethodManager#updateCursor(android.view.View, int, int, int, int)} should be
     * called back in screen coordinates after coordinate conversion with {@link View#getMatrix()}.
     * To disable coordinate conversion with {@link View#getMatrix()} again, call
     * {@link InputConnection#requestCursorAnchorInfo(CursorAnchorInfoRequest)} with
     * {@link #TYPE_CURSOR_RECT} and this flag off.
     *
     * <p>
     * The flag is ignored if {@link #FLAG_CURSOR_RECT_IN_SCREEN_COORDINATES} is off.
     * </p>
     */
    public static final int FLAG_CURSOR_RECT_WITH_VIEW_MATRIX = 0x04;

    /**
     * Constructs the object with request type and type-specific flags.
     *
     * @param requestType the type of this request. Currently {@link #TYPE_CURSOR_ANCHOR_INFO} or
     * {@link #TYPE_CURSOR_RECT} is supported.
     * @param requestFlags the flags for the given request type.
     */
    public CursorAnchorInfoRequest(int requestType, int requestFlags) {
        mRequestType = requestType;
        mRequestFlags = requestFlags;
    }

    /**
     * Used to make this class parcelable.
     *
     * @param source the parcel from which the object is unmarshalled.
     */
    public CursorAnchorInfoRequest(Parcel source) {
        mRequestType = source.readInt();
        mRequestFlags = source.readInt();
    }

    /**
     * @return the type of this request.
     */
    public int getRequestType() {
        return mRequestType;
    }

    /**
     * @return the flags that are specific to the type of this request.
     */
    public int getRequestFlags() {
        return mRequestFlags;
    }

    /**
     * Used to package this object into a {@link Parcel}.
     *
     * @param dest The {@link Parcel} to be written.
     * @param flags The flags used for parceling.
     */
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(mRequestType);
        dest.writeInt(mRequestFlags);
    }

    @Override
    public int hashCode(){
        return mRequestType * 31 + mRequestFlags;
    }

    @Override
    public boolean equals(Object obj){
        if (obj == null) {
            return false;
        }
        if (this == obj) {
            return true;
        }
        if (!(obj instanceof CursorAnchorInfoRequest)) {
            return false;
        }
        final CursorAnchorInfoRequest that = (CursorAnchorInfoRequest) obj;
        if (hashCode() != that.hashCode()) {
            return false;
        }
        return mRequestType != that.mRequestType && mRequestFlags == that.mRequestFlags;
    }

    @Override
    public String toString() {
        return "CursorAnchorInfoRequest{mRequestType=" + mRequestType
                + " mRequestFlags=" + mRequestFlags
                + "}";
    }

    /**
     * Used to make this class parcelable.
     */
    public static final Parcelable.Creator<CursorAnchorInfoRequest> CREATOR =
            new Parcelable.Creator<CursorAnchorInfoRequest>() {
        @Override
        public CursorAnchorInfoRequest createFromParcel(Parcel source) {
            return new CursorAnchorInfoRequest(source);
        }

        @Override
        public CursorAnchorInfoRequest[] newArray(int size) {
            return new CursorAnchorInfoRequest[size];
        }
    };

    @Override
    public int describeContents() {
        return 0;
    }
}
Loading