aboutsummaryrefslogtreecommitdiffstats
path: root/java/src/org/kelar/inputmethod/accessibility/MainKeyboardAccessibilityDelegate.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/accessibility/MainKeyboardAccessibilityDelegate.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/accessibility/MainKeyboardAccessibilityDelegate.java')
-rw-r--r--java/src/org/kelar/inputmethod/accessibility/MainKeyboardAccessibilityDelegate.java293
1 files changed, 293 insertions, 0 deletions
diff --git a/java/src/org/kelar/inputmethod/accessibility/MainKeyboardAccessibilityDelegate.java b/java/src/org/kelar/inputmethod/accessibility/MainKeyboardAccessibilityDelegate.java
new file mode 100644
index 000000000..0275162b6
--- /dev/null
+++ b/java/src/org/kelar/inputmethod/accessibility/MainKeyboardAccessibilityDelegate.java
@@ -0,0 +1,293 @@
+/*
+ * Copyright (C) 2014 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.accessibility;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.os.SystemClock;
+import android.util.Log;
+import android.util.SparseIntArray;
+import android.view.MotionEvent;
+
+import org.kelar.inputmethod.keyboard.Key;
+import org.kelar.inputmethod.keyboard.KeyDetector;
+import org.kelar.inputmethod.keyboard.Keyboard;
+import org.kelar.inputmethod.keyboard.KeyboardId;
+import org.kelar.inputmethod.keyboard.MainKeyboardView;
+import org.kelar.inputmethod.keyboard.PointerTracker;
+import org.kelar.inputmethod.latin.R;
+import org.kelar.inputmethod.latin.utils.SubtypeLocaleUtils;
+
+/**
+ * This class represents a delegate that can be registered in {@link MainKeyboardView} to enhance
+ * accessibility support via composition rather via inheritance.
+ */
+public final class MainKeyboardAccessibilityDelegate
+ extends KeyboardAccessibilityDelegate<MainKeyboardView>
+ implements AccessibilityLongPressTimer.LongPressTimerCallback {
+ private static final String TAG = MainKeyboardAccessibilityDelegate.class.getSimpleName();
+
+ /** Map of keyboard modes to resource IDs. */
+ private static final SparseIntArray KEYBOARD_MODE_RES_IDS = new SparseIntArray();
+
+ static {
+ KEYBOARD_MODE_RES_IDS.put(KeyboardId.MODE_DATE, R.string.keyboard_mode_date);
+ KEYBOARD_MODE_RES_IDS.put(KeyboardId.MODE_DATETIME, R.string.keyboard_mode_date_time);
+ KEYBOARD_MODE_RES_IDS.put(KeyboardId.MODE_EMAIL, R.string.keyboard_mode_email);
+ KEYBOARD_MODE_RES_IDS.put(KeyboardId.MODE_IM, R.string.keyboard_mode_im);
+ KEYBOARD_MODE_RES_IDS.put(KeyboardId.MODE_NUMBER, R.string.keyboard_mode_number);
+ KEYBOARD_MODE_RES_IDS.put(KeyboardId.MODE_PHONE, R.string.keyboard_mode_phone);
+ KEYBOARD_MODE_RES_IDS.put(KeyboardId.MODE_TEXT, R.string.keyboard_mode_text);
+ KEYBOARD_MODE_RES_IDS.put(KeyboardId.MODE_TIME, R.string.keyboard_mode_time);
+ KEYBOARD_MODE_RES_IDS.put(KeyboardId.MODE_URL, R.string.keyboard_mode_url);
+ }
+
+ /** The most recently set keyboard mode. */
+ private int mLastKeyboardMode = KEYBOARD_IS_HIDDEN;
+ private static final int KEYBOARD_IS_HIDDEN = -1;
+ // The rectangle region to ignore hover events.
+ private final Rect mBoundsToIgnoreHoverEvent = new Rect();
+
+
+ public MainKeyboardAccessibilityDelegate(final MainKeyboardView mainKeyboardView,
+ final KeyDetector keyDetector) {
+ super(mainKeyboardView, keyDetector);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setKeyboard(final Keyboard keyboard) {
+ if (keyboard == null) {
+ return;
+ }
+ final Keyboard lastKeyboard = getKeyboard();
+ super.setKeyboard(keyboard);
+ final int lastKeyboardMode = mLastKeyboardMode;
+ mLastKeyboardMode = keyboard.mId.mMode;
+
+ // Since this method is called even when accessibility is off, make sure
+ // to check the state before announcing anything.
+ if (!AccessibilityUtils.getInstance().isAccessibilityEnabled()) {
+ return;
+ }
+ // Announce the language name only when the language is changed.
+ if (lastKeyboard == null || !keyboard.mId.mSubtype.equals(lastKeyboard.mId.mSubtype)) {
+ announceKeyboardLanguage(keyboard);
+ return;
+ }
+ // Announce the mode only when the mode is changed.
+ if (keyboard.mId.mMode != lastKeyboardMode) {
+ announceKeyboardMode(keyboard);
+ return;
+ }
+ // Announce the keyboard type only when the type is changed.
+ if (keyboard.mId.mElementId != lastKeyboard.mId.mElementId) {
+ announceKeyboardType(keyboard, lastKeyboard);
+ return;
+ }
+ }
+
+ /**
+ * Called when the keyboard is hidden and accessibility is enabled.
+ */
+ public void onHideWindow() {
+ if (mLastKeyboardMode != KEYBOARD_IS_HIDDEN) {
+ announceKeyboardHidden();
+ }
+ mLastKeyboardMode = KEYBOARD_IS_HIDDEN;
+ }
+
+ /**
+ * Announces which language of keyboard is being displayed.
+ *
+ * @param keyboard The new keyboard.
+ */
+ private void announceKeyboardLanguage(final Keyboard keyboard) {
+ final String languageText = SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(
+ keyboard.mId.mSubtype.getRawSubtype());
+ sendWindowStateChanged(languageText);
+ }
+
+ /**
+ * Announces which type of keyboard is being displayed.
+ * If the keyboard type is unknown, no announcement is made.
+ *
+ * @param keyboard The new keyboard.
+ */
+ private void announceKeyboardMode(final Keyboard keyboard) {
+ final Context context = mKeyboardView.getContext();
+ final int modeTextResId = KEYBOARD_MODE_RES_IDS.get(keyboard.mId.mMode);
+ if (modeTextResId == 0) {
+ return;
+ }
+ final String modeText = context.getString(modeTextResId);
+ final String text = context.getString(R.string.announce_keyboard_mode, modeText);
+ sendWindowStateChanged(text);
+ }
+
+ /**
+ * Announces which type of keyboard is being displayed.
+ *
+ * @param keyboard The new keyboard.
+ * @param lastKeyboard The last keyboard.
+ */
+ private void announceKeyboardType(final Keyboard keyboard, final Keyboard lastKeyboard) {
+ final int lastElementId = lastKeyboard.mId.mElementId;
+ final int resId;
+ switch (keyboard.mId.mElementId) {
+ case KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED:
+ case KeyboardId.ELEMENT_ALPHABET:
+ if (lastElementId == KeyboardId.ELEMENT_ALPHABET
+ || lastElementId == KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED) {
+ // Transition between alphabet mode and automatic shifted mode should be silently
+ // ignored because it can be determined by each key's talk back announce.
+ return;
+ }
+ resId = R.string.spoken_description_mode_alpha;
+ break;
+ case KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED:
+ if (lastElementId == KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED) {
+ // Resetting automatic shifted mode by pressing the shift key causes the transition
+ // from automatic shifted to manual shifted that should be silently ignored.
+ return;
+ }
+ resId = R.string.spoken_description_shiftmode_on;
+ break;
+ case KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED:
+ if (lastElementId == KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED) {
+ // Resetting caps locked mode by pressing the shift key causes the transition
+ // from shift locked to shift lock shifted that should be silently ignored.
+ return;
+ }
+ resId = R.string.spoken_description_shiftmode_locked;
+ break;
+ case KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED:
+ resId = R.string.spoken_description_shiftmode_locked;
+ break;
+ case KeyboardId.ELEMENT_SYMBOLS:
+ resId = R.string.spoken_description_mode_symbol;
+ break;
+ case KeyboardId.ELEMENT_SYMBOLS_SHIFTED:
+ resId = R.string.spoken_description_mode_symbol_shift;
+ break;
+ case KeyboardId.ELEMENT_PHONE:
+ resId = R.string.spoken_description_mode_phone;
+ break;
+ case KeyboardId.ELEMENT_PHONE_SYMBOLS:
+ resId = R.string.spoken_description_mode_phone_shift;
+ break;
+ default:
+ return;
+ }
+ sendWindowStateChanged(resId);
+ }
+
+ /**
+ * Announces that the keyboard has been hidden.
+ */
+ private void announceKeyboardHidden() {
+ sendWindowStateChanged(R.string.announce_keyboard_hidden);
+ }
+
+ @Override
+ public void performClickOn(final Key key) {
+ final int x = key.getHitBox().centerX();
+ final int y = key.getHitBox().centerY();
+ if (DEBUG_HOVER) {
+ Log.d(TAG, "performClickOn: key=" + key
+ + " inIgnoreBounds=" + mBoundsToIgnoreHoverEvent.contains(x, y));
+ }
+ if (mBoundsToIgnoreHoverEvent.contains(x, y)) {
+ // This hover exit event points to the key that should be ignored.
+ // Clear the ignoring region to handle further hover events.
+ mBoundsToIgnoreHoverEvent.setEmpty();
+ return;
+ }
+ super.performClickOn(key);
+ }
+
+ @Override
+ protected void onHoverEnterTo(final Key key) {
+ final int x = key.getHitBox().centerX();
+ final int y = key.getHitBox().centerY();
+ if (DEBUG_HOVER) {
+ Log.d(TAG, "onHoverEnterTo: key=" + key
+ + " inIgnoreBounds=" + mBoundsToIgnoreHoverEvent.contains(x, y));
+ }
+ if (mBoundsToIgnoreHoverEvent.contains(x, y)) {
+ return;
+ }
+ // This hover enter event points to the key that isn't in the ignoring region.
+ // Further hover events should be handled.
+ mBoundsToIgnoreHoverEvent.setEmpty();
+ super.onHoverEnterTo(key);
+ }
+
+ @Override
+ protected void onHoverExitFrom(final Key key) {
+ final int x = key.getHitBox().centerX();
+ final int y = key.getHitBox().centerY();
+ if (DEBUG_HOVER) {
+ Log.d(TAG, "onHoverExitFrom: key=" + key
+ + " inIgnoreBounds=" + mBoundsToIgnoreHoverEvent.contains(x, y));
+ }
+ super.onHoverExitFrom(key);
+ }
+
+ @Override
+ public void performLongClickOn(final Key key) {
+ if (DEBUG_HOVER) {
+ 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.hasNoPanelAutoMoreKey()) {
+ // 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()[0].mCode;
+ final String text = KeyCodeDescriptionMapper.getInstance().getDescriptionForCodePoint(
+ mKeyboardView.getContext(), codePointOfNoPanelAutoMoreKey);
+ if (text != null) {
+ sendWindowStateChanged(text);
+ }
+ }
+ }
+}