Refactor touch event forwarding in InputView

Bug: 10010128
Change-Id: I82aa4ce847e66cb1241678f39b47b9e27a0f555a
This commit is contained in:
Tadashi G. Takaoka 2013-12-16 17:05:07 +09:00
parent 1b5b0a3ebc
commit 27fab2cc2a
2 changed files with 134 additions and 57 deletions

View file

@ -187,7 +187,7 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
final MainKeyboardView keyboardView = mKeyboardView; final MainKeyboardView keyboardView = mKeyboardView;
final Keyboard oldKeyboard = keyboardView.getKeyboard(); final Keyboard oldKeyboard = keyboardView.getKeyboard();
keyboardView.setKeyboard(keyboard); keyboardView.setKeyboard(keyboard);
mCurrentInputView.setKeyboardGeometry(keyboard.mTopPadding); mCurrentInputView.setKeyboardTopPadding(keyboard.mTopPadding);
keyboardView.setKeyPreviewPopupEnabled( keyboardView.setKeyPreviewPopupEnabled(
Settings.readKeyPreviewPopupEnabled(mPrefs, mResources), Settings.readKeyPreviewPopupEnabled(mPrefs, mResources),
Settings.readKeyPreviewPopupDismissDelay(mPrefs, mResources)); Settings.readKeyPreviewPopupDismissDelay(mPrefs, mResources));

View file

@ -23,87 +23,164 @@ import android.view.MotionEvent;
import android.view.View; import android.view.View;
import android.widget.LinearLayout; import android.widget.LinearLayout;
public final class InputView extends LinearLayout { import com.android.inputmethod.keyboard.MainKeyboardView;
private View mSuggestionStripView; import com.android.inputmethod.latin.suggestions.SuggestionStripView;
private View mKeyboardView;
private int mKeyboardTopPadding;
private boolean mIsForwardingEvent; public final class InputView extends LinearLayout {
private final Rect mInputViewRect = new Rect(); private final Rect mInputViewRect = new Rect();
private final Rect mEventForwardingRect = new Rect(); private KeyboardTopPaddingForwarder mKeyboardTopPaddingForwarder;
private final Rect mEventReceivingRect = new Rect();
public InputView(final Context context, final AttributeSet attrs) { public InputView(final Context context, final AttributeSet attrs) {
super(context, attrs, 0); super(context, attrs, 0);
} }
public void setKeyboardGeometry(final int keyboardTopPadding) {
mKeyboardTopPadding = keyboardTopPadding;
}
@Override @Override
protected void onFinishInflate() { protected void onFinishInflate() {
mSuggestionStripView = findViewById(R.id.suggestion_strip_view); final SuggestionStripView suggestionStripView =
mKeyboardView = findViewById(R.id.keyboard_view); (SuggestionStripView)findViewById(R.id.suggestion_strip_view);
final MainKeyboardView mainKeyboardView =
(MainKeyboardView)findViewById(R.id.keyboard_view);
mKeyboardTopPaddingForwarder = new KeyboardTopPaddingForwarder(
mainKeyboardView, suggestionStripView);
}
public void setKeyboardTopPadding(final int keyboardTopPadding) {
mKeyboardTopPaddingForwarder.setKeyboardTopPadding(keyboardTopPadding);
} }
@Override @Override
public boolean dispatchTouchEvent(final MotionEvent me) { public boolean dispatchTouchEvent(final MotionEvent me) {
if (mSuggestionStripView.getVisibility() != VISIBLE
|| mKeyboardView.getVisibility() != VISIBLE) {
return super.dispatchTouchEvent(me);
}
// The touch events that hit the top padding of keyboard should be forwarded to
// {@link SuggestionStripView}.
final Rect rect = mInputViewRect; final Rect rect = mInputViewRect;
this.getGlobalVisibleRect(rect); getGlobalVisibleRect(rect);
final int x = (int)me.getX() + rect.left; final int x = (int)me.getX() + rect.left;
final int y = (int)me.getY() + rect.top; final int y = (int)me.getY() + rect.top;
final Rect forwardingRect = mEventForwardingRect; // The touch events that hit the top padding of keyboard should be
mKeyboardView.getGlobalVisibleRect(forwardingRect); // forwarded to {@link SuggestionStripView}.
if (!mIsForwardingEvent && !forwardingRect.contains(x, y)) { if (mKeyboardTopPaddingForwarder.dispatchTouchEvent(x, y, me)) {
return super.dispatchTouchEvent(me); return true;
}
return super.dispatchTouchEvent(me);
}
/**
* This class forwards series of {@link MotionEvent}s from <code>Forwarder</code> view to
* <code>Receiver</code> view.
*
* @param <Sender> a {@link View} that may send a {@link MotionEvent} to <Receiver>.
* @param <Receiver> a {@link View} that receives forwarded {@link MotionEvent} from
* <Forwarder>.
*/
private static abstract class MotionEventForwarder<Sender extends View, Receiver extends View> {
protected final Sender mSenderView;
protected final Receiver mReceiverView;
private boolean mIsForwardingEvent;
protected final Rect mEventSendingRect = new Rect();
protected final Rect mEventReceivingRect = new Rect();
public MotionEventForwarder(final Sender senderView, final Receiver receiverView) {
mSenderView = senderView;
mReceiverView = receiverView;
} }
final int forwardingLimitY = forwardingRect.top + mKeyboardTopPadding; // Return true if a touch event of global coordinate x, y needs to be forwarded.
boolean sendToTarget = false; protected abstract boolean needsToForward(final int x, final int y);
switch (me.getAction()) { // Translate global x-coordinate to <code>Receiver</code> local coordinate.
case MotionEvent.ACTION_DOWN: protected int translateX(final int x) {
if (y < forwardingLimitY) { return x - mEventReceivingRect.left;
// This down event and further move and up events should be forwarded to the target. }
mIsForwardingEvent = true;
sendToTarget = true; // Translate global y-coordinate to <code>Receiver</code> local coordinate.
protected int translateY(final int y) {
return y - mEventReceivingRect.top;
}
// Dispatches a {@link MotioneEvent} to <code>Receiver</code> if needed and returns true.
// Otherwise returns false.
public boolean dispatchTouchEvent(final int x, final int y, final MotionEvent me) {
// Forwards a {link MotionEvent} only if both <code>Sender</code> and
// <code>Receiver</code> are visible.
if (mSenderView.getVisibility() != View.VISIBLE ||
mReceiverView.getVisibility() != View.VISIBLE) {
return false;
} }
break; final Rect sendingRect = mEventSendingRect;
case MotionEvent.ACTION_MOVE: mSenderView.getGlobalVisibleRect(sendingRect);
sendToTarget = mIsForwardingEvent; if (!mIsForwardingEvent && !sendingRect.contains(x, y)) {
break; return false;
case MotionEvent.ACTION_UP: }
case MotionEvent.ACTION_CANCEL:
sendToTarget = mIsForwardingEvent; boolean shouldForwardToReceiver = false;
mIsForwardingEvent = false;
break; switch (me.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
// If the down event happens in the forwarding area, successive {@link MotionEvent}s
// should be forwarded.
if (needsToForward(x, y)) {
mIsForwardingEvent = true;
shouldForwardToReceiver = true;
}
break;
case MotionEvent.ACTION_MOVE:
shouldForwardToReceiver = mIsForwardingEvent;
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
shouldForwardToReceiver = mIsForwardingEvent;
mIsForwardingEvent = false;
break;
}
if (!shouldForwardToReceiver) {
return false;
}
final Rect receivingRect = mEventReceivingRect;
mReceiverView.getGlobalVisibleRect(receivingRect);
// Translate global coordinates to <code>Receiver</code> local coordinates.
me.setLocation(translateX(x), translateY(y));
mReceiverView.dispatchTouchEvent(me);
return true;
}
}
/**
* This class forwards {@link MotionEvent}s happened in the top padding of
* {@link MainKeyboardView} to {@link SuggestionStripView}.
*/
private static class KeyboardTopPaddingForwarder
extends MotionEventForwarder<MainKeyboardView, SuggestionStripView> {
private int mKeyboardTopPadding;
public KeyboardTopPaddingForwarder(final MainKeyboardView mainKeyboardView,
final SuggestionStripView suggestionStripView) {
super(mainKeyboardView, suggestionStripView);
} }
if (!sendToTarget) { public void setKeyboardTopPadding(final int keyboardTopPadding) {
return super.dispatchTouchEvent(me); mKeyboardTopPadding = keyboardTopPadding;
} }
final Rect receivingRect = mEventReceivingRect; private boolean isInKeyboardTopPadding(final int y) {
mSuggestionStripView.getGlobalVisibleRect(receivingRect); return y < mEventSendingRect.top + mKeyboardTopPadding;
final int translatedX = x - receivingRect.left; }
final int translatedY;
if (y < forwardingLimitY) { @Override
// The forwarded event should have coordinates that are inside of the target. protected boolean needsToForward(final int x, final int y) {
translatedY = Math.min(y - receivingRect.top, receivingRect.height() - 1); return isInKeyboardTopPadding(y);
} else { }
translatedY = y - receivingRect.top;
@Override
protected int translateY(final int y) {
final int translatedY = super.translateY(y);
if (isInKeyboardTopPadding(y)) {
// The forwarded event should have coordinates that are inside of
// the target.
return Math.min(translatedY, mEventReceivingRect.height() - 1);
}
return translatedY;
} }
me.setLocation(translatedX, translatedY);
mSuggestionStripView.dispatchTouchEvent(me);
return true;
} }
} }