From 2c632bd4bdc8f01dfa1a080d5e6fbae8e9f68a44 Mon Sep 17 00:00:00 2001 From: Aleksandras Kostarevas Date: Mon, 23 Sep 2024 20:11:15 +0300 Subject: [PATCH] Support long-click for morekeys with Talkback --- .../KeyboardAccessibilityDelegate.java | 14 +++-- .../MainKeyboardAccessibilityDelegate.java | 34 +---------- ...MoreKeysKeyboardAccessibilityDelegate.java | 8 ++- java/src/org/futo/inputmethod/keyboard/Key.kt | 16 ------ .../keyboard/MoreKeysKeyboard.java | 8 +-- .../keyboard/MoreKeysKeyboardView.java | 56 +++++++++++++++++-- 6 files changed, 71 insertions(+), 65 deletions(-) diff --git a/java/src/org/futo/inputmethod/accessibility/KeyboardAccessibilityDelegate.java b/java/src/org/futo/inputmethod/accessibility/KeyboardAccessibilityDelegate.java index 08d05475d..a23870635 100644 --- a/java/src/org/futo/inputmethod/accessibility/KeyboardAccessibilityDelegate.java +++ b/java/src/org/futo/inputmethod/accessibility/KeyboardAccessibilityDelegate.java @@ -29,10 +29,8 @@ import android.view.inputmethod.EditorInfo; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.core.view.AccessibilityDelegateCompat; import androidx.core.view.ViewCompat; import androidx.core.view.accessibility.AccessibilityNodeInfoCompat; -import androidx.core.view.accessibility.AccessibilityNodeProviderCompat; import androidx.customview.widget.ExploreByTouchHelper; import org.futo.inputmethod.keyboard.Key; @@ -121,7 +119,7 @@ public class KeyboardAccessibilityDelegate */ protected void sendWindowStateChanged(final String text) { final AccessibilityEvent stateChange = AccessibilityEvent.obtain( - AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); + AccessibilityEvent.TYPE_ANNOUNCEMENT); mKeyboardView.onInitializeAccessibilityEvent(stateChange); stateChange.getText().add(text); stateChange.setContentDescription(null); @@ -183,10 +181,14 @@ public class KeyboardAccessibilityDelegate node.setFocusable(true); node.setScreenReaderFocusable(true); - if(k.isActionKey() || k.getCode() == Constants.CODE_SWITCH_ALPHA_SYMBOL || k.getCode() == Constants.CODE_EMOJI || k.getCode() == Constants.CODE_SYMBOL_SHIFT) { + if(k.isActionKey() || k.getCode() == Constants.CODE_SWITCH_ALPHA_SYMBOL || k.getCode() == Constants.CODE_EMOJI || k.getCode() == Constants.CODE_SYMBOL_SHIFT || (k.getCode() >= Constants.CODE_ACTION_0 && k.getCode() <= Constants.CODE_ACTION_MAX)) { node.addAction(AccessibilityNodeInfoCompat.ACTION_CLICK); + node.addAction(AccessibilityNodeInfoCompat.ACTION_LONG_CLICK); node.setClickable(true); } else { + if(k.isLongPressEnabled()) { + node.addAction(AccessibilityNodeInfoCompat.ACTION_LONG_CLICK); + } node.setTextEntryKey(true); } } @@ -200,6 +202,10 @@ public class KeyboardAccessibilityDelegate // Handle the click action for the virtual button performClickOn(k); return true; + } else if(action == AccessibilityNodeInfoCompat.ACTION_LONG_CLICK) { + // Show morekeys + performLongClickOn(k); + return true; } return false; } diff --git a/java/src/org/futo/inputmethod/accessibility/MainKeyboardAccessibilityDelegate.java b/java/src/org/futo/inputmethod/accessibility/MainKeyboardAccessibilityDelegate.java index bf6d1f340..560459fc9 100644 --- a/java/src/org/futo/inputmethod/accessibility/MainKeyboardAccessibilityDelegate.java +++ b/java/src/org/futo/inputmethod/accessibility/MainKeyboardAccessibilityDelegate.java @@ -227,38 +227,6 @@ public final class MainKeyboardAccessibilityDelegate Log.d(TAG, "performLongClickOn: key=" + key); } final PointerTracker tracker = PointerTracker.getPointerTracker(HOVER_EVENT_POINTER_ID); - final long eventTime = SystemClock.uptimeMillis(); - final int x = key.getHitBox().centerX(); - final int y = key.getHitBox().centerY(); - final MotionEvent downEvent = MotionEvent.obtain( - eventTime, eventTime, MotionEvent.ACTION_DOWN, x, y, 0 /* metaState */); - // Inject a fake down event to {@link PointerTracker} to handle a long press correctly. - tracker.processMotionEvent(downEvent, mKeyDetector); - downEvent.recycle(); - // Invoke {@link PointerTracker#onLongPressed()} as if a long press timeout has passed. - tracker.onLongPressed(); - // If {@link Key#hasNoPanelAutoMoreKeys()} is true (such as "0 +" key on the phone layout) - // or a key invokes IME switcher dialog, we should just ignore the next - // {@link #onRegisterHoverKey(Key,MotionEvent)}. It can be determined by whether - // {@link PointerTracker} is in operation or not. - if (tracker.isInOperation()) { - // This long press shows a more keys keyboard and further hover events should be - // handled. - mBoundsToIgnoreHoverEvent.setEmpty(); - return; - } - // This long press has handled at {@link MainKeyboardView#onLongPress(PointerTracker)}. - // We should ignore further hover events on this key. - mBoundsToIgnoreHoverEvent.set(key.getHitBox()); - if (key.getHasNoPanelAutoMoreKey()) { - // This long press has registered a code point without showing a more keys keyboard. - // We should talk back the code point if possible. - final int codePointOfNoPanelAutoMoreKey = key.getMoreKeys().get(0).mCode; - final String text = KeyCodeDescriptionMapper.getInstance().getDescriptionForCodePoint( - mKeyboardView.getContext(), codePointOfNoPanelAutoMoreKey); - if (text != null) { - sendWindowStateChanged(text); - } - } + mKeyboardView.showMoreKeysKeyboard(key, tracker); } } diff --git a/java/src/org/futo/inputmethod/accessibility/MoreKeysKeyboardAccessibilityDelegate.java b/java/src/org/futo/inputmethod/accessibility/MoreKeysKeyboardAccessibilityDelegate.java index b27c8169c..1bf2a17f4 100644 --- a/java/src/org/futo/inputmethod/accessibility/MoreKeysKeyboardAccessibilityDelegate.java +++ b/java/src/org/futo/inputmethod/accessibility/MoreKeysKeyboardAccessibilityDelegate.java @@ -17,8 +17,8 @@ package org.futo.inputmethod.accessibility; import android.graphics.Rect; -import android.util.Log; -import android.view.MotionEvent; +import android.view.HapticFeedbackConstants; +import android.view.SoundEffectConstants; import android.view.accessibility.AccessibilityEvent; import androidx.core.view.accessibility.AccessibilityEventCompat; @@ -27,7 +27,6 @@ import androidx.core.view.accessibility.AccessibilityRecordCompat; import org.futo.inputmethod.keyboard.Key; import org.futo.inputmethod.keyboard.KeyDetector; import org.futo.inputmethod.keyboard.MoreKeysKeyboardView; -import org.futo.inputmethod.keyboard.PointerTracker; /** * This class represents a delegate that can be registered in {@link MoreKeysKeyboardView} to @@ -57,6 +56,9 @@ public class MoreKeysKeyboardAccessibilityDelegate public void onShowMoreKeysKeyboard() { sendWindowStateChanged(mOpenAnnounceResId); + + mKeyboardView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); + mKeyboardView.playSoundEffect(SoundEffectConstants.CLICK); } public void onDismissMoreKeysKeyboard() { diff --git a/java/src/org/futo/inputmethod/keyboard/Key.kt b/java/src/org/futo/inputmethod/keyboard/Key.kt index c3cd75687..4a4ba038d 100644 --- a/java/src/org/futo/inputmethod/keyboard/Key.kt +++ b/java/src/org/futo/inputmethod/keyboard/Key.kt @@ -300,22 +300,6 @@ data class Key( ((actionFlags and KeyConsts.ACTION_FLAGS_ENABLE_LONG_PRESS) != 0 && (labelFlags and KeyConsts.LABEL_FLAGS_SHIFTED_LETTER_ACTIVATED) == 0) - fun markAsLeftEdge(params: KeyboardParams) { - hitBox.left = params.mLeftPadding - } - - fun markAsRightEdge(params: KeyboardParams) { - hitBox.right = params.mOccupiedWidth - params.mRightPadding - } - - fun markAsTopEdge(params: KeyboardParams) { - hitBox.top = params.mTopPadding - } - - fun markAsBottomEdge(params: KeyboardParams) { - hitBox.bottom = params.mOccupiedHeight + params.mBottomPadding - } - fun selectTypeface(params: KeyDrawParams): Typeface { return when (labelFlags and KeyConsts.LABEL_FLAGS_FONT_MASK) { KeyConsts.LABEL_FLAGS_FONT_NORMAL -> Typeface.DEFAULT diff --git a/java/src/org/futo/inputmethod/keyboard/MoreKeysKeyboard.java b/java/src/org/futo/inputmethod/keyboard/MoreKeysKeyboard.java index 3cbbd0453..dc117c4ae 100644 --- a/java/src/org/futo/inputmethod/keyboard/MoreKeysKeyboard.java +++ b/java/src/org/futo/inputmethod/keyboard/MoreKeysKeyboard.java @@ -248,10 +248,10 @@ public final class MoreKeysKeyboard extends Keyboard { } public void markAsEdgeKey(final Key key, final int row) { - if (row == 0) - key.markAsTopEdge(this); - if (isTopRow(row)) - key.markAsBottomEdge(this); + //if (row == 0) + // key.markAsTopEdge(this); + //if (isTopRow(row)) + // key.markAsBottomEdge(this); } private boolean isTopRow(final int rowCount) { diff --git a/java/src/org/futo/inputmethod/keyboard/MoreKeysKeyboardView.java b/java/src/org/futo/inputmethod/keyboard/MoreKeysKeyboardView.java index 56a0debe2..da0704076 100644 --- a/java/src/org/futo/inputmethod/keyboard/MoreKeysKeyboardView.java +++ b/java/src/org/futo/inputmethod/keyboard/MoreKeysKeyboardView.java @@ -25,9 +25,12 @@ import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.view.KeyEvent; import android.view.MotionEvent; +import android.view.TouchDelegate; import android.view.View; import android.view.ViewGroup; +import androidx.annotation.NonNull; + import org.futo.inputmethod.accessibility.AccessibilityUtils; import org.futo.inputmethod.accessibility.MoreKeysKeyboardAccessibilityDelegate; import org.futo.inputmethod.keyboard.internal.KeyDrawParams; @@ -43,7 +46,7 @@ public class MoreKeysKeyboardView extends KeyboardView implements MoreKeysPanel private final int[] mCoordinates = CoordinateUtils.newInstance(); private final Drawable mDivider; - protected final KeyDetector mKeyDetector; + protected final MoreKeysDetector mKeyDetector; private Controller mController = EMPTY_CONTROLLER; protected KeyboardActionListener mListener; private int mOriginX; @@ -104,8 +107,6 @@ public class MoreKeysKeyboardView extends KeyboardView implements MoreKeysPanel @Override public void setKeyboard(final Keyboard keyboard) { super.setKeyboard(keyboard); - mKeyDetector.setKeyboard( - keyboard, -getPaddingLeft(), -getPaddingTop() + getVerticalCorrection()); if (AccessibilityUtils.getInstance().isAccessibilityEnabled()) { if (mAccessibilityDelegate == null) { mAccessibilityDelegate = new MoreKeysKeyboardAccessibilityDelegate( @@ -114,8 +115,15 @@ public class MoreKeysKeyboardView extends KeyboardView implements MoreKeysPanel mAccessibilityDelegate.setCloseAnnounce(R.string.spoken_close_more_keys_keyboard); } mAccessibilityDelegate.setKeyboard(keyboard); + + // No vertical correction + mKeyDetector.setKeyboard(keyboard, -getPaddingLeft(), -getPaddingTop()); } else { mAccessibilityDelegate = null; + + // With vertical correction + mKeyDetector.setKeyboard( + keyboard, -getPaddingLeft(), -getPaddingTop() + getVerticalCorrection()); } } @@ -139,8 +147,13 @@ public class MoreKeysKeyboardView extends KeyboardView implements MoreKeysPanel container.setX(panelX); container.setY(panelY); - mOriginX = CoordinateUtils.x(touchOrigin) - getDefaultCoordX(); - mOriginY = CoordinateUtils.y(touchOrigin) - container.getMeasuredHeight(); + if(AccessibilityUtils.getInstance().isAccessibilityEnabled()) { + mOriginX = (int)container.getX() - CoordinateUtils.x(mCoordinates); + mOriginY = (int)container.getY() - CoordinateUtils.y(mCoordinates); + } else { + mOriginX = CoordinateUtils.x(touchOrigin) - getDefaultCoordX(); + mOriginY = CoordinateUtils.y(touchOrigin) - container.getMeasuredHeight(); + } controller.onShowMoreKeysPanel(this); final MoreKeysKeyboardAccessibilityDelegate accessibilityDelegate = mAccessibilityDelegate; if (accessibilityDelegate != null @@ -194,6 +207,8 @@ public class MoreKeysKeyboardView extends KeyboardView implements MoreKeysPanel * Performs the specific action for this panel when the user presses a key on the panel. */ protected void onKeyInput(final Key key, final int x, final int y) { + dismissMoreKeysPanel(); + final int code = key.getCode(); if (code == Constants.CODE_OUTPUT_TEXT) { mListener.onTextInput(mCurrentKey.getOutputText()); @@ -315,6 +330,36 @@ public class MoreKeysKeyboardView extends KeyboardView implements MoreKeysPanel public void showInParent(final ViewGroup parentView) { removeFromParent(); parentView.addView(getContainerView()); + + if(mAccessibilityDelegate != null && AccessibilityUtils.getInstance().isTouchExplorationEnabled()) { + parentView.setTouchDelegate(new MyTouchDelegate(parentView, getContainerView())); + } + } + + class MyTouchDelegate extends TouchDelegate { + public MyTouchDelegate(View parentView, View delegateView) { + super(new Rect(Integer.MIN_VALUE / 2, Integer.MIN_VALUE / 2, Integer.MAX_VALUE / 2 - 1, Integer.MAX_VALUE / 2 - 1), delegateView); + } + + @Override + public boolean onTouchExplorationHoverEvent(@NonNull MotionEvent event) { + MotionEvent copy = MotionEvent.obtain(event); + copy.offsetLocation(-getContainerView().getX(), -getContainerView().getY()); + + // Dismiss the panel if we exit outside of the range + if(copy.getAction() == MotionEvent.ACTION_HOVER_EXIT) { + if (mKeyDetector.detectHitKey( + (int)copy.getX(), + (int)copy.getY() + ) == null) { + dismissMoreKeysPanel(); + } + + return true; + } else { + return MoreKeysKeyboardView.this.dispatchHoverEvent(copy); + } + } } @Override @@ -323,6 +368,7 @@ public class MoreKeysKeyboardView extends KeyboardView implements MoreKeysPanel final ViewGroup currentParent = (ViewGroup)containerView.getParent(); if (currentParent != null) { currentParent.removeView(containerView); + currentParent.setTouchDelegate(null); } }