aboutsummaryrefslogtreecommitdiffstats
path: root/java/src/org/kelar/inputmethod/latin/InputView.java
diff options
context:
space:
mode:
authorAmin Bandali <bandali@kelar.org>2024-12-16 21:45:41 -0500
committerAmin Bandali <bandali@kelar.org>2025-01-11 14:17:35 -0500
commite9a0e66716dab4dd3184d009d8920de1961efdfa (patch)
tree02dcc096643d74645bf28459c2834c3d4a2ad7f2 /java/src/org/kelar/inputmethod/latin/InputView.java
parentfb3b9360d70596d7e921de8bf7d3ca99564a077e (diff)
downloadlatinime-e9a0e66716dab4dd3184d009d8920de1961efdfa.tar.gz
latinime-e9a0e66716dab4dd3184d009d8920de1961efdfa.tar.xz
latinime-e9a0e66716dab4dd3184d009d8920de1961efdfa.zip
Rename to Kelar Keyboard (org.kelar.inputmethod.latin)
Diffstat (limited to 'java/src/org/kelar/inputmethod/latin/InputView.java')
-rw-r--r--java/src/org/kelar/inputmethod/latin/InputView.java252
1 files changed, 252 insertions, 0 deletions
diff --git a/java/src/org/kelar/inputmethod/latin/InputView.java b/java/src/org/kelar/inputmethod/latin/InputView.java
new file mode 100644
index 000000000..9aab0e7c7
--- /dev/null
+++ b/java/src/org/kelar/inputmethod/latin/InputView.java
@@ -0,0 +1,252 @@
+/*
+ * Copyright (C) 2011 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 org.kelar.inputmethod.latin;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+import android.widget.FrameLayout;
+
+import org.kelar.inputmethod.accessibility.AccessibilityUtils;
+import org.kelar.inputmethod.keyboard.MainKeyboardView;
+import org.kelar.inputmethod.latin.suggestions.MoreSuggestionsView;
+import org.kelar.inputmethod.latin.suggestions.SuggestionStripView;
+
+public final class InputView extends FrameLayout {
+ private final Rect mInputViewRect = new Rect();
+ private MainKeyboardView mMainKeyboardView;
+ private KeyboardTopPaddingForwarder mKeyboardTopPaddingForwarder;
+ private MoreSuggestionsViewCanceler mMoreSuggestionsViewCanceler;
+ private MotionEventForwarder<?, ?> mActiveForwarder;
+
+ public InputView(final Context context, final AttributeSet attrs) {
+ super(context, attrs, 0);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ final SuggestionStripView suggestionStripView =
+ (SuggestionStripView)findViewById(R.id.suggestion_strip_view);
+ mMainKeyboardView = (MainKeyboardView)findViewById(R.id.keyboard_view);
+ mKeyboardTopPaddingForwarder = new KeyboardTopPaddingForwarder(
+ mMainKeyboardView, suggestionStripView);
+ mMoreSuggestionsViewCanceler = new MoreSuggestionsViewCanceler(
+ mMainKeyboardView, suggestionStripView);
+ }
+
+ public void setKeyboardTopPadding(final int keyboardTopPadding) {
+ mKeyboardTopPaddingForwarder.setKeyboardTopPadding(keyboardTopPadding);
+ }
+
+ @Override
+ protected boolean dispatchHoverEvent(final MotionEvent event) {
+ if (AccessibilityUtils.getInstance().isTouchExplorationEnabled()
+ && mMainKeyboardView.isShowingMoreKeysPanel()) {
+ // With accessibility mode on, discard hover events while a more keys keyboard is shown.
+ // The {@link MoreKeysKeyboard} receives hover events directly from the platform.
+ return true;
+ }
+ return super.dispatchHoverEvent(event);
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(final MotionEvent me) {
+ final Rect rect = mInputViewRect;
+ getGlobalVisibleRect(rect);
+ final int index = me.getActionIndex();
+ final int x = (int)me.getX(index) + rect.left;
+ final int y = (int)me.getY(index) + rect.top;
+
+ // The touch events that hit the top padding of keyboard should be forwarded to
+ // {@link SuggestionStripView}.
+ if (mKeyboardTopPaddingForwarder.onInterceptTouchEvent(x, y, me)) {
+ mActiveForwarder = mKeyboardTopPaddingForwarder;
+ return true;
+ }
+
+ // To cancel {@link MoreSuggestionsView}, we should intercept a touch event to
+ // {@link MainKeyboardView} and dismiss the {@link MoreSuggestionsView}.
+ if (mMoreSuggestionsViewCanceler.onInterceptTouchEvent(x, y, me)) {
+ mActiveForwarder = mMoreSuggestionsViewCanceler;
+ return true;
+ }
+
+ mActiveForwarder = null;
+ return false;
+ }
+
+ @Override
+ public boolean onTouchEvent(final MotionEvent me) {
+ if (mActiveForwarder == null) {
+ return super.onTouchEvent(me);
+ }
+
+ final Rect rect = mInputViewRect;
+ getGlobalVisibleRect(rect);
+ final int index = me.getActionIndex();
+ final int x = (int)me.getX(index) + rect.left;
+ final int y = (int)me.getY(index) + rect.top;
+ return mActiveForwarder.onTouchEvent(x, y, me);
+ }
+
+ /**
+ * This class forwards series of {@link MotionEvent}s from <code>SenderView</code> to
+ * <code>ReceiverView</code>.
+ *
+ * @param <SenderView> a {@link View} that may send a {@link MotionEvent} to <ReceiverView>.
+ * @param <ReceiverView> a {@link View} that receives forwarded {@link MotionEvent} from
+ * <SenderView>.
+ */
+ private static abstract class
+ MotionEventForwarder<SenderView extends View, ReceiverView extends View> {
+ protected final SenderView mSenderView;
+ protected final ReceiverView mReceiverView;
+
+ protected final Rect mEventSendingRect = new Rect();
+ protected final Rect mEventReceivingRect = new Rect();
+
+ public MotionEventForwarder(final SenderView senderView, final ReceiverView receiverView) {
+ mSenderView = senderView;
+ mReceiverView = receiverView;
+ }
+
+ // Return true if a touch event of global coordinate x, y needs to be forwarded.
+ protected abstract boolean needsToForward(final int x, final int y);
+
+ // Translate global x-coordinate to <code>ReceiverView</code> local coordinate.
+ protected int translateX(final int x) {
+ return x - mEventReceivingRect.left;
+ }
+
+ // Translate global y-coordinate to <code>ReceiverView</code> local coordinate.
+ protected int translateY(final int y) {
+ return y - mEventReceivingRect.top;
+ }
+
+ /**
+ * Callback when a {@link MotionEvent} is forwarded.
+ * @param me the motion event to be forwarded.
+ */
+ protected void onForwardingEvent(final MotionEvent me) {}
+
+ // Returns true if a {@link MotionEvent} is needed to be forwarded to
+ // <code>ReceiverView</code>. Otherwise returns false.
+ public boolean onInterceptTouchEvent(final int x, final int y, final MotionEvent me) {
+ // Forwards a {link MotionEvent} only if both <code>SenderView</code> and
+ // <code>ReceiverView</code> are visible.
+ if (mSenderView.getVisibility() != View.VISIBLE ||
+ mReceiverView.getVisibility() != View.VISIBLE) {
+ return false;
+ }
+ mSenderView.getGlobalVisibleRect(mEventSendingRect);
+ if (!mEventSendingRect.contains(x, y)) {
+ return false;
+ }
+
+ if (me.getActionMasked() == MotionEvent.ACTION_DOWN) {
+ // If the down event happens in the forwarding area, successive
+ // {@link MotionEvent}s should be forwarded to <code>ReceiverView</code>.
+ if (needsToForward(x, y)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ // Returns true if a {@link MotionEvent} is forwarded to <code>ReceiverView</code>.
+ // Otherwise returns false.
+ public boolean onTouchEvent(final int x, final int y, final MotionEvent me) {
+ mReceiverView.getGlobalVisibleRect(mEventReceivingRect);
+ // Translate global coordinates to <code>ReceiverView</code> local coordinates.
+ me.setLocation(translateX(x), translateY(y));
+ mReceiverView.dispatchTouchEvent(me);
+ onForwardingEvent(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);
+ }
+
+ public void setKeyboardTopPadding(final int keyboardTopPadding) {
+ mKeyboardTopPadding = keyboardTopPadding;
+ }
+
+ private boolean isInKeyboardTopPadding(final int y) {
+ return y < mEventSendingRect.top + mKeyboardTopPadding;
+ }
+
+ @Override
+ protected boolean needsToForward(final int x, final int y) {
+ // Forwarding an event only when {@link MainKeyboardView} is visible.
+ // Because the visibility of {@link MainKeyboardView} is controlled by its parent
+ // view in {@link KeyboardSwitcher#setMainKeyboardFrame()}, we should check the
+ // visibility of the parent view.
+ final View mainKeyboardFrame = (View)mSenderView.getParent();
+ return mainKeyboardFrame.getVisibility() == View.VISIBLE && isInKeyboardTopPadding(y);
+ }
+
+ @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;
+ }
+ }
+
+ /**
+ * This class forwards {@link MotionEvent}s happened in the {@link MainKeyboardView} to
+ * {@link SuggestionStripView} when the {@link MoreSuggestionsView} is showing.
+ * {@link SuggestionStripView} dismisses {@link MoreSuggestionsView} when it receives any event
+ * outside of it.
+ */
+ private static class MoreSuggestionsViewCanceler
+ extends MotionEventForwarder<MainKeyboardView, SuggestionStripView> {
+ public MoreSuggestionsViewCanceler(final MainKeyboardView mainKeyboardView,
+ final SuggestionStripView suggestionStripView) {
+ super(mainKeyboardView, suggestionStripView);
+ }
+
+ @Override
+ protected boolean needsToForward(final int x, final int y) {
+ return mReceiverView.isShowingMoreSuggestionPanel() && mEventSendingRect.contains(x, y);
+ }
+
+ @Override
+ protected void onForwardingEvent(final MotionEvent me) {
+ if (me.getActionMasked() == MotionEvent.ACTION_DOWN) {
+ mReceiverView.dismissMoreSuggestionsPanel();
+ }
+ }
+ }
+}