Merge "Introduce API to support Stylus Companion widget"

This commit is contained in:
TreeHugger Robot
2022-08-03 06:35:01 +00:00
committed by Android (Google) Code Review
13 changed files with 151 additions and 11 deletions

View File

@@ -18904,6 +18904,7 @@ package android.inputmethodservice {
method public void onUnbindInput();
method @Deprecated public void onUpdateCursor(android.graphics.Rect);
method public void onUpdateCursorAnchorInfo(android.view.inputmethod.CursorAnchorInfo);
method public void onUpdateEditorToolType(int);
method public void onUpdateExtractedText(int, android.view.inputmethod.ExtractedText);
method public void onUpdateExtractingViews(android.view.inputmethod.EditorInfo);
method public void onUpdateExtractingVisibility(android.view.inputmethod.EditorInfo);
@@ -52968,9 +52969,11 @@ package android.view.inputmethod {
method @Nullable public android.view.inputmethod.SurroundingText getInitialSurroundingText(@IntRange(from=0) int, @IntRange(from=0) int, int);
method @Nullable public CharSequence getInitialTextAfterCursor(@IntRange(from=0) int, int);
method @Nullable public CharSequence getInitialTextBeforeCursor(@IntRange(from=0) int, int);
method public int getInitialToolType();
method public final void makeCompatible(int);
method public void setInitialSurroundingSubText(@NonNull CharSequence, int);
method public void setInitialSurroundingText(@NonNull CharSequence);
method public void setInitialToolType(int);
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.view.inputmethod.EditorInfo> CREATOR;
field public static final int IME_ACTION_DONE = 6; // 0x6

View File

@@ -80,6 +80,7 @@ class IInputMethodWrapper extends IInputMethod.Stub
private static final int DO_START_STYLUS_HANDWRITING = 110;
private static final int DO_INIT_INK_WINDOW = 120;
private static final int DO_FINISH_STYLUS_HANDWRITING = 130;
private static final int DO_UPDATE_TOOL_TYPE = 140;
final WeakReference<InputMethodServiceInternal> mTarget;
final Context mContext;
@@ -234,6 +235,10 @@ class IInputMethodWrapper extends IInputMethod.Stub
inputMethod.canStartStylusHandwriting(msg.arg1);
return;
}
case DO_UPDATE_TOOL_TYPE: {
inputMethod.updateEditorToolType(msg.arg1);
return;
}
case DO_START_STYLUS_HANDWRITING: {
final SomeArgs args = (SomeArgs) msg.obj;
inputMethod.startStylusHandwriting(msg.arg1, (InputChannel) args.arg1,
@@ -400,6 +405,14 @@ class IInputMethodWrapper extends IInputMethod.Stub
mCaller.obtainMessageI(DO_CAN_START_STYLUS_HANDWRITING, requestId));
}
@BinderThread
@Override
public void updateEditorToolType(int toolType)
throws RemoteException {
mCaller.executeOrSendMessage(
mCaller.obtainMessageI(DO_UPDATE_TOOL_TYPE, toolType));
}
@BinderThread
@Override
public void startStylusHandwriting(int requestId, @NonNull InputChannel channel,

View File

@@ -948,6 +948,15 @@ public class InputMethodService extends AbstractInputMethodService {
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
/**
* {@inheritDoc}
* @hide
*/
@Override
public void updateEditorToolType(int toolType) {
onUpdateEditorToolType(toolType);
}
/**
* {@inheritDoc}
* @hide
@@ -3012,17 +3021,33 @@ public class InputMethodService extends AbstractInputMethodService {
* not call to inform the IME of this interaction.
* @param focusChanged true if the user changed the focused view by this click.
* @see InputMethodManager#viewClicked(View)
* @see #onUpdateEditorToolType(int)
* @deprecated The method may not be called for composite {@link View} that works as a giant
* "Canvas", which can host its own UI hierarchy and sub focus state.
* {@link android.webkit.WebView} is a good example. Application / IME developers
* should not rely on this method. If your goal is just being notified when an
* on-going input is interrupted, simply monitor {@link #onFinishInput()}.
* If your goal is to know what {@link MotionEvent#getToolType(int)} clicked on
* editor, use {@link #onUpdateEditorToolType(int)} instead.
*/
@Deprecated
public void onViewClicked(boolean focusChanged) {
// Intentionally empty
}
/**
* Called when the user tapped or clicked an {@link android.widget.Editor}.
* This can be useful when IME makes a decision of showing Virtual keyboard based on what
* {@link MotionEvent#getToolType(int)} was used to click the editor.
* e.g. when toolType is {@link MotionEvent#TOOL_TYPE_STYLUS}, IME may choose to show a
* companion widget instead of normal virtual keyboard.
* <p> Default implementation does nothing. </p>
* @param toolType what {@link MotionEvent#getToolType(int)} was used to click on editor.
*/
public void onUpdateEditorToolType(int toolType) {
// Intentionally empty
}
/**
* Called when the application has reported a new location of its text
* cursor. This is only called if explicitly requested by the input method.

View File

@@ -734,6 +734,8 @@ public final class ViewRootImpl implements ViewParent,
final PointF mDragPoint = new PointF();
final PointF mLastTouchPoint = new PointF();
int mLastTouchSource;
/** Tracks last {@link MotionEvent#getToolType(int)} with {@link MotionEvent#ACTION_UP}. **/
private int mLastClickToolType;
private boolean mProfileRendering;
private Choreographer.FrameCallback mRenderProfiler;
@@ -6417,11 +6419,16 @@ public final class ViewRootImpl implements ViewParent,
event.offsetLocation(0, mCurScrollY);
}
// Remember the touch position for possible drag-initiation.
if (event.isTouchEvent()) {
// Remember the touch position for possible drag-initiation.
mLastTouchPoint.x = event.getRawX();
mLastTouchPoint.y = event.getRawY();
mLastTouchSource = event.getSource();
// Register last ACTION_UP. This will be propagated to IME.
if (event.getActionMasked() == MotionEvent.ACTION_UP) {
mLastClickToolType = event.getToolType(event.getActionIndex());
}
}
return FORWARD;
}
@@ -8017,6 +8024,14 @@ public final class ViewRootImpl implements ViewParent,
return mLastTouchSource;
}
/**
* Used by InputMethodManager.
* @hide
*/
public int getLastClickToolType() {
return mLastClickToolType;
}
public void setDragFocus(View newDragTarget, DragEvent event) {
if (mCurrentDragView != newDragTarget && !View.sCascadedDragDrop) {
// Send EXITED and ENTERED notifications to the old and new drag focus views.

View File

@@ -30,6 +30,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.content.res.Configuration;
import android.inputmethodservice.InputMethodService;
import android.os.Build.VERSION_CODES;
import android.os.Bundle;
import android.os.LocaleList;
@@ -40,6 +41,7 @@ import android.text.InputType;
import android.text.TextUtils;
import android.util.Printer;
import android.util.proto.ProtoOutputStream;
import android.view.MotionEvent;
import android.view.View;
import android.view.autofill.AutofillId;
@@ -564,6 +566,12 @@ public class EditorInfo implements InputType, Parcelable {
@Nullable
private SurroundingText mInitialSurroundingText = null;
/**
* Initial {@link MotionEvent#ACTION_UP} tool type {@link MotionEvent#getToolType(int)} that
* was used to focus this editor.
*/
private int mInitialToolType = MotionEvent.TOOL_TYPE_UNKNOWN;
/**
* Editors may use this method to provide initial input text to IMEs. As the surrounding text
@@ -936,6 +944,31 @@ public class EditorInfo implements InputType, Parcelable {
}
}
/**
* Returns the initial {@link MotionEvent#ACTION_UP} tool type
* {@link MotionEvent#getToolType(int)} responsible for focus on the current editor.
*
* @see #setInitialToolType(int)
* @see MotionEvent#getToolType(int)
* @see InputMethodService#onUpdateEditorToolType(int)
* @return toolType {@link MotionEvent#getToolType(int)}.
*/
public int getInitialToolType() {
return mInitialToolType;
}
/**
* Set the initial {@link MotionEvent#ACTION_UP} tool type {@link MotionEvent#getToolType(int)}.
* that brought focus to the view.
*
* @see #getInitialToolType()
* @see MotionEvent#getToolType(int)
* @see InputMethodService#onUpdateEditorToolType(int)
*/
public void setInitialToolType(int toolType) {
mInitialToolType = toolType;
}
/**
* Export the state of {@link EditorInfo} into a protocol buffer output stream.
*
@@ -972,6 +1005,7 @@ public class EditorInfo implements InputType, Parcelable {
+ " actionId=" + actionId);
pw.println(prefix + "initialSelStart=" + initialSelStart
+ " initialSelEnd=" + initialSelEnd
+ " initialToolType=" + mInitialToolType
+ " initialCapsMode=0x"
+ Integer.toHexString(initialCapsMode));
pw.println(prefix + "hintText=" + hintText
@@ -1006,6 +1040,7 @@ public class EditorInfo implements InputType, Parcelable {
newEditorInfo.initialSelStart = initialSelStart;
newEditorInfo.initialSelEnd = initialSelEnd;
newEditorInfo.initialCapsMode = initialCapsMode;
newEditorInfo.mInitialToolType = mInitialToolType;
newEditorInfo.hintText = TextUtils.stringOrSpannedString(hintText);
newEditorInfo.label = TextUtils.stringOrSpannedString(label);
newEditorInfo.packageName = packageName;
@@ -1036,6 +1071,7 @@ public class EditorInfo implements InputType, Parcelable {
dest.writeInt(initialSelStart);
dest.writeInt(initialSelEnd);
dest.writeInt(initialCapsMode);
dest.writeInt(mInitialToolType);
TextUtils.writeToParcel(hintText, dest, flags);
TextUtils.writeToParcel(label, dest, flags);
dest.writeString(packageName);
@@ -1072,6 +1108,7 @@ public class EditorInfo implements InputType, Parcelable {
res.initialSelStart = source.readInt();
res.initialSelEnd = source.readInt();
res.initialCapsMode = source.readInt();
res.mInitialToolType = source.readInt();
res.hintText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
res.label = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
res.packageName = source.readString();

View File

@@ -111,10 +111,11 @@ final class IInputMethodManagerInvoker {
@AnyThread
boolean showSoftInput(@NonNull IInputMethodClient client, @Nullable IBinder windowToken,
int flags, @Nullable ResultReceiver resultReceiver,
int flags, int lastClickToolType, @Nullable ResultReceiver resultReceiver,
@SoftInputShowHideReason int reason) {
try {
return mTarget.showSoftInput(client, windowToken, flags, resultReceiver, reason);
return mTarget.showSoftInput(
client, windowToken, flags, lastClickToolType, resultReceiver, reason);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}

View File

@@ -376,6 +376,15 @@ public interface InputMethod {
// intentionally empty
}
/**
* This method is called when the user tapped or clicked an {@link android.widget.Editor}.
* @param toolType {@link android.view.MotionEvent#getToolType(int)} used for clicking editor.
* @hide
*/
default void updateEditorToolType(int toolType) {
// intentionally empty
}
/**
* Start stylus handwriting session.
* @hide

View File

@@ -1929,6 +1929,7 @@ public final class InputMethodManager {
mClient,
view.getWindowToken(),
flags,
mCurRootView.getLastClickToolType(),
resultReceiver,
reason);
}
@@ -1960,6 +1961,7 @@ public final class InputMethodManager {
mClient,
mCurRootView.getView().getWindowToken(),
flags,
mCurRootView.getLastClickToolType(),
resultReceiver,
SoftInputShowHideReason.SHOW_SOFT_INPUT);
}
@@ -2339,6 +2341,9 @@ public final class InputMethodManager {
editorInfo.packageName = view.getContext().getOpPackageName();
editorInfo.autofillId = view.getAutofillId();
editorInfo.fieldId = view.getId();
synchronized (mH) {
editorInfo.setInitialToolType(mCurRootView.getLastClickToolType());
}
InputConnection ic = view.onCreateInputConnection(editorInfo);
if (DEBUG) Log.v(TAG, "Starting input: editorInfo=" + editorInfo + " ic=" + ic);

View File

@@ -74,6 +74,8 @@ oneway interface IInputMethod {
void hideSoftInput(in IBinder hideInputToken, int flags, in ResultReceiver resultReceiver);
void updateEditorToolType(int toolType);
void changeInputMethodSubtype(in InputMethodSubtype subtype);
void canStartStylusHandwriting(int requestId);

View File

@@ -55,7 +55,7 @@ interface IInputMethodManager {
InputMethodSubtype getLastInputMethodSubtype(int userId);
boolean showSoftInput(in IInputMethodClient client, @nullable IBinder windowToken, int flags,
in @nullable ResultReceiver resultReceiver, int reason);
int lastClickToolType, in @nullable ResultReceiver resultReceiver, int reason);
boolean hideSoftInput(in IInputMethodClient client, @nullable IBinder windowToken, int flags,
in @nullable ResultReceiver resultReceiver, int reason);

View File

@@ -42,6 +42,7 @@ import android.text.TextUtils;
import android.text.style.MaskFilterSpan;
import android.text.style.UnderlineSpan;
import android.util.StringBuilderPrinter;
import android.view.MotionEvent;
import android.view.autofill.AutofillId;
import androidx.test.filters.SmallTest;
@@ -490,7 +491,8 @@ public class EditorInfoTest {
assertThat(sb.toString()).isEqualTo(
"prefix: inputType=0x0 imeOptions=0x0 privateImeOptions=null\n"
+ "prefix: actionLabel=null actionId=0\n"
+ "prefix: initialSelStart=-1 initialSelEnd=-1 initialCapsMode=0x0\n"
+ "prefix: initialSelStart=-1 initialSelEnd=-1 initialToolType=0"
+ " initialCapsMode=0x0\n"
+ "prefix: hintText=null label=null\n"
+ "prefix: packageName=null autofillId=null fieldId=0 fieldName=null\n"
+ "prefix: extras=null\n"
@@ -509,6 +511,7 @@ public class EditorInfoTest {
info.initialCapsMode = TextUtils.CAP_MODE_CHARACTERS; // 0x1000
info.hintText = "testHintText";
info.label = "testLabel";
info.setInitialToolType(MotionEvent.TOOL_TYPE_STYLUS);
info.packageName = "android.view.inputmethod";
info.autofillId = new AutofillId(123);
info.fieldId = 456;
@@ -523,7 +526,8 @@ public class EditorInfoTest {
assertThat(sb.toString()).isEqualTo(
"prefix2: inputType=0x1 imeOptions=0x2 privateImeOptions=testOptions\n"
+ "prefix2: actionLabel=null actionId=0\n"
+ "prefix2: initialSelStart=0 initialSelEnd=1 initialCapsMode=0x1000\n"
+ "prefix2: initialSelStart=0 initialSelEnd=1 initialToolType=2"
+ " initialCapsMode=0x1000\n"
+ "prefix2: hintText=testHintText label=testLabel\n"
+ "prefix2: packageName=android.view.inputmethod autofillId=123"
+ " fieldId=456 fieldName=testField\n"
@@ -543,7 +547,8 @@ public class EditorInfoTest {
assertThat(sb.toString()).isEqualTo(
"prefix: inputType=0x0 imeOptions=0x0 privateImeOptions=null\n"
+ "prefix: actionLabel=null actionId=0\n"
+ "prefix: initialSelStart=-1 initialSelEnd=-1 initialCapsMode=0x0\n"
+ "prefix: initialSelStart=-1 initialSelEnd=-1 initialToolType=0"
+ " initialCapsMode=0x0\n"
+ "prefix: hintText=null label=null\n"
+ "prefix: packageName=null autofillId=null fieldId=0 fieldName=null\n"
+ "prefix: hintLocales=null\n"

View File

@@ -222,6 +222,17 @@ final class IInputMethodInvoker {
return true;
}
@AnyThread
boolean updateEditorToolType(int toolType) {
try {
mTarget.updateEditorToolType(toolType);
} catch (RemoteException e) {
logRemoteException(e);
return false;
}
return true;
}
@AnyThread
void changeInputMethodSubtype(InputMethodSubtype subtype) {
try {

View File

@@ -132,6 +132,7 @@ import android.view.DisplayInfo;
import android.view.IWindowManager;
import android.view.InputChannel;
import android.view.InputDevice;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.view.WindowManager.DisplayImePolicy;
@@ -3320,7 +3321,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
@Override
public boolean showSoftInput(IInputMethodClient client, IBinder windowToken, int flags,
ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
int lastClickTooType, ResultReceiver resultReceiver,
@SoftInputShowHideReason int reason) {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.showSoftInput");
int uid = Binder.getCallingUid();
ImeTracing.getInstance().triggerManagerServiceDump(
@@ -3332,7 +3334,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
final long ident = Binder.clearCallingIdentity();
try {
if (DEBUG) Slog.v(TAG, "Client requesting input be shown");
return showCurrentInputLocked(windowToken, flags, resultReceiver, reason);
return showCurrentInputLocked(
windowToken, flags, lastClickTooType, resultReceiver, reason);
} finally {
Binder.restoreCallingIdentity(ident);
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
@@ -3404,8 +3407,15 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
}
@GuardedBy("ImfLock.class")
boolean showCurrentInputLocked(IBinder windowToken, int flags, ResultReceiver resultReceiver,
@SoftInputShowHideReason int reason) {
boolean showCurrentInputLocked(IBinder windowToken, int flags,
ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
return showCurrentInputLocked(
windowToken, flags, MotionEvent.TOOL_TYPE_UNKNOWN, resultReceiver, reason);
}
@GuardedBy("ImfLock.class")
private boolean showCurrentInputLocked(IBinder windowToken, int flags, int lastClickToolType,
ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
mShowRequested = true;
if (mAccessibilityRequestingNoSoftKeyboard || mImeHiddenByDisplayPolicy) {
return false;
@@ -3434,6 +3444,10 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
+ ", " + showFlags + ", " + resultReceiver + ") for reason: "
+ InputMethodDebug.softInputDisplayReasonToString(reason));
}
if (lastClickToolType != MotionEvent.TOOL_TYPE_UNKNOWN) {
curMethod.updateEditorToolType(lastClickToolType);
}
// TODO(b/192412909): Check if we can always call onShowHideSoftInputRequested() or not.
if (curMethod.showSoftInput(showInputToken, showFlags, resultReceiver)) {
onShowHideSoftInputRequested(true /* show */, windowToken, reason);