Store pending connected view when requesting focus
Before this change, HandwritingInitiator would request focus for a handwriting view when it detected stylus movement on the view, and expect an InputConnection to be created for that view. However, in some apps, an InputConnection could be created for a different view, before the InputConnection for the focus requested view is created (for example, see b/286289041). This would cause handwriting initiation to fail. To handle this case, HandwritingInitiator stores a reference to the view when it requests focus, then waits for the InputConnection to be created for that view, and ignores any other InputConnections created in the meantime. Bug: 286287855 Test: atest HandwritingInitiatorTest Change-Id: I63ca718e1ac7221f28aa6f4c6dd1d66974e12118
This commit is contained in:
@@ -208,6 +208,7 @@ public class HandwritingInitiator {
|
||||
candidateView.getHandwritingDelegatorCallback().run();
|
||||
mState.mHasPreparedHandwritingDelegation = true;
|
||||
} else {
|
||||
mState.mPendingConnectedView = new WeakReference<>(candidateView);
|
||||
requestFocusWithoutReveal(candidateView);
|
||||
}
|
||||
}
|
||||
@@ -269,8 +270,9 @@ public class HandwritingInitiator {
|
||||
mShowHoverIconForConnectedView = false;
|
||||
return;
|
||||
}
|
||||
if (mState != null && mState.mShouldInitHandwriting) {
|
||||
tryStartHandwriting();
|
||||
if (mState != null && mState.mPendingConnectedView != null
|
||||
&& mState.mPendingConnectedView.get() == view) {
|
||||
startHandwriting(view);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -295,40 +297,6 @@ public class HandwritingInitiator {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to initiate handwriting. For this method to successfully send startHandwriting signal,
|
||||
* the following 3 conditions should meet:
|
||||
* a) The stylus movement exceeds the touchSlop.
|
||||
* b) A View has built InputConnection with IME.
|
||||
* c) The stylus event lands into the connected View's boundary.
|
||||
* This method will immediately fail without any side effect if condition a or b is not met.
|
||||
* However, if both condition a and b are met but the condition c is not met, it will reset the
|
||||
* internal states. And HandwritingInitiator won't attempt to call startHandwriting until the
|
||||
* next ACTION_DOWN.
|
||||
*/
|
||||
private void tryStartHandwriting() {
|
||||
if (!mState.mExceedHandwritingSlop) {
|
||||
return;
|
||||
}
|
||||
final View connectedView = getConnectedView();
|
||||
if (connectedView == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!connectedView.isAutoHandwritingEnabled()) {
|
||||
clearConnectedView();
|
||||
return;
|
||||
}
|
||||
|
||||
final Rect handwritingArea = getViewHandwritingArea(connectedView);
|
||||
if (isInHandwritingArea(
|
||||
handwritingArea, mState.mStylusDownX, mState.mStylusDownY, connectedView)) {
|
||||
startHandwriting(connectedView);
|
||||
} else {
|
||||
mState.mShouldInitHandwriting = false;
|
||||
}
|
||||
}
|
||||
|
||||
/** Starts a stylus handwriting session for the view. */
|
||||
@VisibleForTesting
|
||||
public void startHandwriting(@NonNull View view) {
|
||||
@@ -631,6 +599,7 @@ public class HandwritingInitiator {
|
||||
private boolean mHasInitiatedHandwriting;
|
||||
|
||||
private boolean mHasPreparedHandwritingDelegation;
|
||||
|
||||
/**
|
||||
* Whether the current ongoing stylus MotionEvent sequence already exceeds the
|
||||
* handwriting slop.
|
||||
@@ -639,6 +608,12 @@ public class HandwritingInitiator {
|
||||
*/
|
||||
private boolean mExceedHandwritingSlop;
|
||||
|
||||
/**
|
||||
* A view which has requested focus and is pending input connection creation. When an input
|
||||
* connection is created for the view, a handwriting session should be started for the view.
|
||||
*/
|
||||
private WeakReference<View> mPendingConnectedView = null;
|
||||
|
||||
/** The pointer id of the stylus pointer that is being tracked. */
|
||||
private final int mStylusPointerId;
|
||||
/** The time stamp when the stylus pointer goes down. */
|
||||
|
||||
@@ -221,8 +221,10 @@ public class HandwritingInitiatorTest {
|
||||
|
||||
@Test
|
||||
public void onTouchEvent_startHandwriting_inputConnectionBuilt_stylusMoveInExtendedHWArea() {
|
||||
// The stylus down point is between mTestView1 and mTestView2, but it is within the
|
||||
// extended handwriting area of both views. It is closer to mTestView1.
|
||||
final int x1 = sHwArea1.right + HW_BOUNDS_OFFSETS_RIGHT_PX / 2;
|
||||
final int y1 = sHwArea1.bottom + HW_BOUNDS_OFFSETS_BOTTOM_PX / 2;
|
||||
final int y1 = sHwArea1.bottom + (sHwArea2.top - sHwArea1.bottom) / 3;
|
||||
MotionEvent stylusEvent1 = createStylusEvent(ACTION_DOWN, x1, y1, 0);
|
||||
mHandwritingInitiator.onTouchEvent(stylusEvent1);
|
||||
|
||||
@@ -231,10 +233,14 @@ public class HandwritingInitiatorTest {
|
||||
MotionEvent stylusEvent2 = createStylusEvent(ACTION_MOVE, x2, y2, 0);
|
||||
mHandwritingInitiator.onTouchEvent(stylusEvent2);
|
||||
|
||||
// InputConnection is created after stylus movement.
|
||||
mHandwritingInitiator.onInputConnectionCreated(mTestView1);
|
||||
// First create InputConnection for mTestView2 and verify that handwriting is not started.
|
||||
mHandwritingInitiator.onInputConnectionCreated(mTestView2);
|
||||
verify(mHandwritingInitiator, never()).startHandwriting(mTestView2);
|
||||
|
||||
verify(mHandwritingInitiator, times(1)).startHandwriting(mTestView1);
|
||||
// Next create InputConnection for mTextView1. Handwriting is started for this view since
|
||||
// the stylus down point is closest to this view.
|
||||
mHandwritingInitiator.onInputConnectionCreated(mTestView1);
|
||||
verify(mHandwritingInitiator).startHandwriting(mTestView1);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user