Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 86e1e1f9 authored by Tadashi G. Takaoka's avatar Tadashi G. Takaoka Committed by Android (Google) Code Review
Browse files

Merge "Support more keys accessibility mode"

parents 70a1352c 62316d7e
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -48,6 +48,7 @@
    <integer name="config_max_longpress_timeout">700</integer>
    <integer name="config_min_longpress_timeout">100</integer>
    <integer name="config_longpress_timeout_step">10</integer>
    <integer name="config_accessibility_long_press_key_timeout">1500</integer>
    <integer name="config_max_more_keys_column">5</integer>
    <integer name="config_more_keys_keyboard_fadein_anim_time">0</integer>
    <integer name="config_more_keys_keyboard_fadeout_anim_time">100</integer>
+5 −0
Original line number Diff line number Diff line
@@ -139,4 +139,9 @@
    <string name="spoken_symbol_unknown">Unknown symbol</string>
    <!-- Spoken description for unknown emoji code point. -->
    <string name="spoken_emoji_unknown">Unknown emoji</string>

    <!-- Spoken descriptions when opening a more keys keyboard that has alternative characters. -->
    <string name="spoken_open_more_keys_keyboard">Alternative characters are available</string>
    <!-- Spoken descriptions when closing a more keys keyboard that has alternative characters. -->
    <string name="spoken_close_more_keys_keyboard">Alternative characters are dismissed</string>
</resources>
+67 −0
Original line number Diff line number Diff line
/*
 * 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 com.android.inputmethod.accessibility;

import android.content.Context;
import android.os.Handler;
import android.os.Message;

import com.android.inputmethod.keyboard.Key;
import com.android.inputmethod.latin.R;

// Handling long press timer to show a more keys keyboard.
final class AccessibilityLongPressTimer extends Handler {
    public interface LongPressTimerCallback {
        public void onLongPressed(Key key);
    }

    private static final int MSG_LONG_PRESS = 1;

    private final LongPressTimerCallback mCallback;
    private final long mConfigAccessibilityLongPressTimeout;

    public AccessibilityLongPressTimer(final LongPressTimerCallback callback,
            final Context context) {
        super();
        mCallback = callback;
        mConfigAccessibilityLongPressTimeout = context.getResources().getInteger(
                R.integer.config_accessibility_long_press_key_timeout);
    }

    @Override
    public void handleMessage(final Message msg) {
        switch (msg.what) {
        case MSG_LONG_PRESS:
            cancelLongPress();
            mCallback.onLongPressed((Key)msg.obj);
            return;
        default:
            super.handleMessage(msg);
            return;
        }
    }

    public void startLongPress(final Key key) {
        cancelLongPress();
        final Message longPressMessage = obtainMessage(MSG_LONG_PRESS, key);
        sendMessageDelayed(longPressMessage, mConfigAccessibilityLongPressTimeout);
    }

    public void cancelLongPress() {
        removeMessages(MSG_LONG_PRESS);
    }
}
+39 −9
Original line number Diff line number Diff line
@@ -33,14 +33,29 @@ import com.android.inputmethod.keyboard.Keyboard;
import com.android.inputmethod.keyboard.KeyboardView;
import com.android.inputmethod.keyboard.PointerTracker;

/**
 * This class represents a delegate that can be registered in a class that extends
 * {@link KeyboardView} to enhance accessibility support via composition rather via inheritance.
 *
 * To implement accessibility mode, the target keyboard view has to:<p>
 * - Call {@link #setKeyboard(Keyboard)} when a new keyboard is set to the keyboard view.
 * - Dispatch a hover event by calling {@link #onHoverEnter(MotionEvent)}.
 *
 * @param <KV> The keyboard view class type.
 */
public class KeyboardAccessibilityDelegate<KV extends KeyboardView>
        extends AccessibilityDelegateCompat {
    private static final String TAG = KeyboardAccessibilityDelegate.class.getSimpleName();
    protected static final boolean DEBUG_HOVER = false;

    protected final KV mKeyboardView;
    protected final KeyDetector mKeyDetector;
    private Keyboard mKeyboard;
    private KeyboardAccessibilityNodeProvider mAccessibilityNodeProvider;
    private Key mLastHoverKey;

    public static final int HOVER_EVENT_POINTER_ID = 0;

    public KeyboardAccessibilityDelegate(final KV keyboardView, final KeyDetector keyDetector) {
        super();
        mKeyboardView = keyboardView;
@@ -180,8 +195,11 @@ public class KeyboardAccessibilityDelegate<KV extends KeyboardView>
     */
    protected void onHoverEnter(final MotionEvent event) {
        final Key key = getHoverKeyOf(event);
        if (DEBUG_HOVER) {
            Log.d(TAG, "onHoverEnter: key=" + key);
        }
        if (key != null) {
            onHoverEnterKey(key);
            onHoverEnterTo(key);
        }
        setLastHoverKey(key);
    }
@@ -196,14 +214,14 @@ public class KeyboardAccessibilityDelegate<KV extends KeyboardView>
        final Key key = getHoverKeyOf(event);
        if (key != lastKey) {
            if (lastKey != null) {
                onHoverExitKey(lastKey);
                onHoverExitFrom(lastKey);
            }
            if (key != null) {
                onHoverEnterKey(key);
                onHoverEnterTo(key);
            }
        }
        if (key != null) {
            onHoverMoveKey(key);
            onHoverMoveWithin(key);
        }
        setLastHoverKey(key);
    }
@@ -215,15 +233,18 @@ public class KeyboardAccessibilityDelegate<KV extends KeyboardView>
     */
    protected void onHoverExit(final MotionEvent event) {
        final Key lastKey = getLastHoverKey();
        if (DEBUG_HOVER) {
            Log.d(TAG, "onHoverExit: key=" + getHoverKeyOf(event) + " last=" + lastKey);
        }
        if (lastKey != null) {
            onHoverExitKey(lastKey);
            onHoverExitFrom(lastKey);
        }
        final Key key = getHoverKeyOf(event);
        // Make sure we're not getting an EXIT event because the user slid
        // off the keyboard area, then force a key press.
        if (key != null) {
            onRegisterHoverKey(key, event);
            onHoverExitKey(key);
            onHoverExitFrom(key);
        }
        setLastHoverKey(null);
    }
@@ -235,6 +256,9 @@ public class KeyboardAccessibilityDelegate<KV extends KeyboardView>
     * @param event A hover exit event that triggers key registering.
     */
    protected void onRegisterHoverKey(final Key key, final MotionEvent event) {
        if (DEBUG_HOVER) {
            Log.d(TAG, "onRegisterHoverKey: key=" + key);
        }
        simulateTouchEvent(MotionEvent.ACTION_DOWN, event);
        simulateTouchEvent(MotionEvent.ACTION_UP, event);
    }
@@ -274,7 +298,10 @@ public class KeyboardAccessibilityDelegate<KV extends KeyboardView>
     *
     * @param key The currently hovered key.
     */
    protected void onHoverEnterKey(final Key key) {
    protected void onHoverEnterTo(final Key key) {
        if (DEBUG_HOVER) {
            Log.d(TAG, "onHoverEnterTo: key=" + key);
        }
        key.onPressed();
        mKeyboardView.invalidateKey(key);
        final KeyboardAccessibilityNodeProvider provider = getAccessibilityNodeProvider();
@@ -287,14 +314,17 @@ public class KeyboardAccessibilityDelegate<KV extends KeyboardView>
     *
     * @param key The currently hovered key.
     */
    protected void onHoverMoveKey(final Key key) { }
    protected void onHoverMoveWithin(final Key key) { }

    /**
     * Handles a hover exit event on a key.
     *
     * @param key The currently hovered key.
     */
    protected void onHoverExitKey(final Key key) {
    protected void onHoverExitFrom(final Key key) {
        if (DEBUG_HOVER) {
            Log.d(TAG, "onHoverExitFrom: key=" + key);
        }
        key.onReleased();
        mKeyboardView.invalidateKey(key);
        final KeyboardAccessibilityNodeProvider provider = getAccessibilityNodeProvider();
+77 −1
Original line number Diff line number Diff line
@@ -17,17 +17,29 @@
package com.android.inputmethod.accessibility;

import android.content.Context;
import android.os.SystemClock;
import android.util.Log;
import android.util.SparseIntArray;
import android.view.MotionEvent;

import com.android.inputmethod.keyboard.Key;
import com.android.inputmethod.keyboard.KeyDetector;
import com.android.inputmethod.keyboard.Keyboard;
import com.android.inputmethod.keyboard.KeyboardId;
import com.android.inputmethod.keyboard.MainKeyboardView;
import com.android.inputmethod.keyboard.PointerTracker;
import com.android.inputmethod.latin.R;
import com.android.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> {
        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();

@@ -46,10 +58,15 @@ public final class MainKeyboardAccessibilityDelegate
    /** The most recently set keyboard mode. */
    private int mLastKeyboardMode = KEYBOARD_IS_HIDDEN;
    private static final int KEYBOARD_IS_HIDDEN = -1;
    private boolean mShouldIgnoreOnRegisterHoverKey;

    private final AccessibilityLongPressTimer mAccessibilityLongPressTimer;

    public MainKeyboardAccessibilityDelegate(final MainKeyboardView mainKeyboardView,
            final KeyDetector keyDetector) {
        super(mainKeyboardView, keyDetector);
        mAccessibilityLongPressTimer = new AccessibilityLongPressTimer(
                this /* callback */, mainKeyboardView.getContext());
    }

    /**
@@ -172,4 +189,63 @@ public final class MainKeyboardAccessibilityDelegate
    private void announceKeyboardHidden() {
        sendWindowStateChanged(R.string.announce_keyboard_hidden);
    }

    @Override
    protected void onRegisterHoverKey(final Key key, final MotionEvent event) {
        if (DEBUG_HOVER) {
            Log.d(TAG, "onRegisterHoverKey: key=" + key + " ignore="
                    + mShouldIgnoreOnRegisterHoverKey);
        }
        if (!mShouldIgnoreOnRegisterHoverKey) {
            super.onRegisterHoverKey(key, event);
        }
        mShouldIgnoreOnRegisterHoverKey = false;
    }

    @Override
    protected void onHoverEnterTo(final Key key) {
        if (DEBUG_HOVER) {
            Log.d(TAG, "onHoverEnterTo: key=" + key);
        }
        mAccessibilityLongPressTimer.cancelLongPress();
        super.onHoverEnterTo(key);
        if (key.isLongPressEnabled()) {
            mAccessibilityLongPressTimer.startLongPress(key);
        }
    }

    protected void onHoverExitFrom(final Key key) {
        if (DEBUG_HOVER) {
            Log.d(TAG, "onHoverExitFrom: key=" + key);
        }
        mAccessibilityLongPressTimer.cancelLongPress();
        super.onHoverExitFrom(key);
    }

    @Override
    public void onLongPressed(final Key key) {
        if (DEBUG_HOVER) {
            Log.d(TAG, "onLongPressed: 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);
        // The above fake down event triggers an unnecessary long press timer that should be
        // canceled.
        tracker.cancelLongPressTimer();
        downEvent.recycle();
        // Invoke {@link MainKeyboardView#onLongPress(PointerTracker)} as if a long press timeout
        // has passed.
        mKeyboardView.onLongPress(tracker);
        // 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.
        mShouldIgnoreOnRegisterHoverKey = !tracker.isInOperation();
    }
}
Loading