Merge "Introduce new IMS method for Stylus events and dispatch" into tm-dev

This commit is contained in:
Taran Singh
2022-03-10 15:24:29 +00:00
committed by Android (Google) Code Review
3 changed files with 124 additions and 4 deletions

View File

@@ -18820,6 +18820,7 @@ package android.inputmethodservice {
method public void onStartInput(android.view.inputmethod.EditorInfo, boolean);
method public void onStartInputView(android.view.inputmethod.EditorInfo, boolean);
method public boolean onStartStylusHandwriting();
method public void onStylusHandwritingMotionEvent(@NonNull android.view.MotionEvent);
method public void onUnbindInput();
method @Deprecated public void onUpdateCursor(android.graphics.Rect);
method public void onUpdateCursorAnchorInfo(android.view.inputmethod.CursorAnchorInfo);

View File

@@ -27,6 +27,8 @@ import android.content.Context;
import android.os.IBinder;
import android.util.Slog;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.WindowManager;
import com.android.internal.policy.PhoneWindow;
@@ -40,6 +42,9 @@ final class InkWindow extends PhoneWindow {
private final WindowManager mWindowManager;
private boolean mIsViewAdded;
private View mInkView;
private InkVisibilityListener mInkViewVisibilityListener;
private ViewTreeObserver.OnGlobalLayoutListener mGlobalLayoutListener;
public InkWindow(@NonNull Context context) {
super(context);
@@ -102,4 +107,77 @@ final class InkWindow extends PhoneWindow {
lp.token = token;
setAttributes(lp);
}
@Override
public void addContentView(View view, ViewGroup.LayoutParams params) {
if (mInkView == null) {
mInkView = view;
} else if (mInkView != view) {
throw new IllegalStateException("Only one Child Inking view is permitted.");
}
super.addContentView(view, params);
initInkViewVisibilityListener();
}
@Override
public void setContentView(View view, ViewGroup.LayoutParams params) {
mInkView = view;
super.setContentView(view, params);
initInkViewVisibilityListener();
}
@Override
public void setContentView(View view) {
mInkView = view;
super.setContentView(view);
initInkViewVisibilityListener();
}
@Override
public void clearContentView() {
if (mGlobalLayoutListener != null && mInkView != null) {
mInkView.getViewTreeObserver().removeOnGlobalLayoutListener(mGlobalLayoutListener);
}
mGlobalLayoutListener = null;
mInkView = null;
super.clearContentView();
}
/**
* Listener used by InkWindow to time the dispatching of {@link MotionEvent}s to Ink view, once
* it is visible to user.
*/
interface InkVisibilityListener {
void onInkViewVisible();
}
void setInkViewVisibilityListener(InkVisibilityListener listener) {
mInkViewVisibilityListener = listener;
initInkViewVisibilityListener();
}
void initInkViewVisibilityListener() {
if (mInkView == null || mInkViewVisibilityListener == null
|| mGlobalLayoutListener != null) {
return;
}
mGlobalLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
if (mInkView.isVisibleToUser()) {
if (mInkViewVisibilityListener != null) {
mInkViewVisibilityListener.onInkViewVisible();
}
mInkView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
mGlobalLayoutListener = null;
}
}
};
mInkView.getViewTreeObserver().addOnGlobalLayoutListener(mGlobalLayoutListener);
}
boolean isInkViewVisible() {
return getDecorView().getVisibility() == View.VISIBLE
&& mInkView != null && mInkView.isVisibleToUser();
}
}

View File

@@ -143,6 +143,7 @@ import com.android.internal.inputmethod.ImeTracing;
import com.android.internal.inputmethod.InputMethodNavButtonFlags;
import com.android.internal.inputmethod.InputMethodPrivilegedOperations;
import com.android.internal.inputmethod.InputMethodPrivilegedOperationsRegistry;
import com.android.internal.util.RingBuffer;
import com.android.internal.view.IInlineSuggestionsRequestCallback;
import com.android.internal.view.IInputContext;
import com.android.internal.view.InlineSuggestionsRequestInfo;
@@ -333,6 +334,17 @@ public class InputMethodService extends AbstractInputMethodService {
private static final String PROP_CAN_RENDER_GESTURAL_NAV_BUTTONS =
"persist.sys.ime.can_render_gestural_nav_buttons";
/**
* Number of {@link MotionEvent} to buffer if IME is not ready with Ink view.
* This number may be configured eventually based on device's touch sampling frequency.
*/
private static final int MAX_EVENTS_BUFFER = 500;
/**
* A circular buffer of size MAX_EVENTS_BUFFER in case IME is taking too long to add ink view.
**/
private RingBuffer<MotionEvent> mPendingEvents;
/**
* Returns whether {@link InputMethodService} is responsible for rendering the back button and
* the IME switcher button or not when the gestural navigation is enabled.
@@ -954,7 +966,8 @@ public class InputMethodService extends AbstractInputMethodService {
mInkWindow.show();
// deliver previous @param stylusEvents
stylusEvents.forEach(mInkWindow.getDecorView()::dispatchTouchEvent);
stylusEvents.forEach(InputMethodService.this::onStylusHandwritingMotionEvent);
// create receiver for channel
mHandwritingEventReceiver = new SimpleBatchedInputEventReceiver(
channel,
@@ -963,11 +976,11 @@ public class InputMethodService extends AbstractInputMethodService {
if (!(event instanceof MotionEvent)) {
return false;
}
return mInkWindow.getDecorView().dispatchTouchEvent((MotionEvent) event);
onStylusHandwritingMotionEvent((MotionEvent) event);
return true;
});
}
/**
* {@inheritDoc}
* @hide
@@ -2357,7 +2370,8 @@ public class InputMethodService extends AbstractInputMethodService {
*
* If the IME supports handwriting for the current input, it should return {@code true},
* ensure its inking views are attached to the {@link #getStylusHandwritingWindow()}, and handle
* stylus input received on the ink window via {@link #getCurrentInputConnection()}.
* stylus input received from {@link #onStylusHandwritingMotionEvent(MotionEvent)} on the
* {@link #getStylusHandwritingWindow()} via {@link #getCurrentInputConnection()}.
* @return {@code true} if IME can honor the request, {@code false} if IME cannot at this time.
*/
public boolean onStartStylusHandwriting() {
@@ -2365,6 +2379,33 @@ public class InputMethodService extends AbstractInputMethodService {
return false;
}
/**
* Called after {@link #onStartStylusHandwriting()} returns {@code true} for every Stylus
* {@link MotionEvent}.
* By default, this method forwards all {@link MotionEvent}s to the
* {@link #getStylusHandwritingWindow()} once its visible, however IME can override it to
* receive them sooner.
* @param motionEvent {@link MotionEvent} from stylus.
*/
public void onStylusHandwritingMotionEvent(@NonNull MotionEvent motionEvent) {
if (mInkWindow.isInkViewVisible()) {
mInkWindow.getDecorView().dispatchTouchEvent(motionEvent);
} else {
if (mPendingEvents == null) {
mPendingEvents = new RingBuffer(MotionEvent.class, MAX_EVENTS_BUFFER);
}
mPendingEvents.append(motionEvent);
mInkWindow.setInkViewVisibilityListener(() -> {
if (mPendingEvents != null && !mPendingEvents.isEmpty()) {
for (MotionEvent event : mPendingEvents.toArray()) {
mInkWindow.getDecorView().dispatchTouchEvent(event);
}
mPendingEvents.clear();
}
});
}
}
/**
* Called when the current stylus handwriting session was finished (either by the system or
* via {@link #finishStylusHandwriting()}.