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

Commit 34b2b5e6 authored by alanv's avatar alanv
Browse files

Remove accessibility gesture handlers from LatinIME.

Bug: 6457558
Change-Id: If33ca6f026d4846ba79a701ef42c0112f5b0b488
parent 67b2c584
Loading
Loading
Loading
Loading
+0 −1
Original line number Diff line number Diff line
@@ -58,7 +58,6 @@ public class AccessibilityUtils {
        // These only need to be initialized if the kill switch is off.
        sInstance.initInternal(inputMethod);
        KeyCodeDescriptionMapper.init();
        AccessibleInputMethodServiceProxy.init(inputMethod);
        AccessibleKeyboardViewProxy.init(inputMethod);
    }

+0 −84
Original line number Diff line number Diff line
/*
 * 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 com.android.inputmethod.accessibility;

import android.content.Context;
import android.inputmethodservice.InputMethodService;
import android.media.AudioManager;
import android.os.Vibrator;
import android.view.KeyEvent;

public class AccessibleInputMethodServiceProxy implements AccessibleKeyboardActionListener {
    private static final AccessibleInputMethodServiceProxy sInstance =
            new AccessibleInputMethodServiceProxy();

    /**
     * Duration of the key click vibration in milliseconds.
     */
    private static final long VIBRATE_KEY_CLICK = 50;

    private static final float FX_VOLUME = -1.0f;

    private InputMethodService mInputMethod;
    private Vibrator mVibrator;
    private AudioManager mAudioManager;

    public static void init(InputMethodService inputMethod) {
        sInstance.initInternal(inputMethod);
    }

    public static AccessibleInputMethodServiceProxy getInstance() {
        return sInstance;
    }

    private AccessibleInputMethodServiceProxy() {
        // Not publicly instantiable.
    }

    private void initInternal(InputMethodService inputMethod) {
        mInputMethod = inputMethod;
        mVibrator = (Vibrator) inputMethod.getSystemService(Context.VIBRATOR_SERVICE);
        mAudioManager = (AudioManager) inputMethod.getSystemService(Context.AUDIO_SERVICE);
    }

    /**
     * Handle flick gestures by mapping them to directional pad keys.
     */
    @Override
    public void onFlickGesture(int direction) {
        switch (direction) {
        case FlickGestureDetector.FLICK_LEFT:
            sendDownUpKeyEvents(KeyEvent.KEYCODE_DPAD_LEFT);
            break;
        case FlickGestureDetector.FLICK_RIGHT:
            sendDownUpKeyEvents(KeyEvent.KEYCODE_DPAD_RIGHT);
            break;
        }
    }

    /**
     * Provide haptic feedback and send the specified keyCode to the input
     * connection as a pair of down/up events.
     *
     * @param keyCode
     */
    private void sendDownUpKeyEvents(int keyCode) {
        mVibrator.vibrate(VIBRATE_KEY_CLICK);
        mAudioManager.playSoundEffect(AudioManager.FX_KEYPRESS_STANDARD, FX_VOLUME);
        mInputMethod.sendDownUpKeyEvents(keyCode);
    }
}
+0 −30
Original line number Diff line number Diff line
/*
 * 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 com.android.inputmethod.accessibility;

public interface AccessibleKeyboardActionListener {
    /**
     * @param direction the direction of the flick gesture, one of
     *            <ul>
     *              <li>{@link FlickGestureDetector#FLICK_UP}
     *              <li>{@link FlickGestureDetector#FLICK_DOWN}
     *              <li>{@link FlickGestureDetector#FLICK_LEFT}
     *              <li>{@link FlickGestureDetector#FLICK_RIGHT}
     *            </ul>
     */
    public void onFlickGesture(int direction);
}
+0 −39
Original line number Diff line number Diff line
@@ -17,8 +17,6 @@
package com.android.inputmethod.accessibility;

import android.content.Context;
import android.graphics.Color;
import android.graphics.Paint;
import android.inputmethodservice.InputMethodService;
import android.support.v4.view.AccessibilityDelegateCompat;
import android.support.v4.view.ViewCompat;
@@ -38,16 +36,13 @@ public class AccessibleKeyboardViewProxy extends AccessibilityDelegateCompat {
    private static final AccessibleKeyboardViewProxy sInstance = new AccessibleKeyboardViewProxy();

    private InputMethodService mInputMethod;
    private FlickGestureDetector mGestureDetector;
    private LatinKeyboardView mView;
    private AccessibleKeyboardActionListener mListener;
    private AccessibilityEntityProvider mAccessibilityNodeProvider;

    private Key mLastHoverKey = null;

    public static void init(InputMethodService inputMethod) {
        sInstance.initInternal(inputMethod);
        sInstance.mListener = AccessibleInputMethodServiceProxy.getInstance();
    }

    public static AccessibleKeyboardViewProxy getInstance() {
@@ -59,14 +54,7 @@ public class AccessibleKeyboardViewProxy extends AccessibilityDelegateCompat {
    }

    private void initInternal(InputMethodService inputMethod) {
        final Paint paint = new Paint();
        paint.setTextAlign(Paint.Align.LEFT);
        paint.setTextSize(14.0f);
        paint.setAntiAlias(true);
        paint.setColor(Color.YELLOW);

        mInputMethod = inputMethod;
        mGestureDetector = new KeyboardFlickGestureDetector(inputMethod);
    }

    /**
@@ -112,19 +100,6 @@ public class AccessibleKeyboardViewProxy extends AccessibilityDelegateCompat {
     * @return {@code true} if the event is handled
     */
    public boolean dispatchHoverEvent(MotionEvent event, PointerTracker tracker) {
        if (mGestureDetector.onHoverEvent(event, this, tracker))
            return true;

        return onHoverEventInternal(event, tracker);
    }

    /**
     * Handles touch exploration events when Accessibility is turned on.
     *
     * @param event The touch exploration hover event.
     * @return {@code true} if the event was handled
     */
    /* package */boolean onHoverEventInternal(MotionEvent event, PointerTracker tracker) {
        final int x = (int) event.getX();
        final int y = (int) event.getY();
        final Key key = tracker.getKeyOn(x, y);
@@ -214,20 +189,6 @@ public class AccessibleKeyboardViewProxy extends AccessibilityDelegateCompat {
        mView.getParent().requestSendAccessibilityEvent(mView, event);
    }

    private class KeyboardFlickGestureDetector extends FlickGestureDetector {
        public KeyboardFlickGestureDetector(Context context) {
            super(context);
        }

        @Override
        public boolean onFlick(MotionEvent e1, MotionEvent e2, int direction) {
            if (mListener != null) {
                mListener.onFlickGesture(direction);
            }
            return true;
        }
    }

    /**
     * Notifies the user of changes in the keyboard shift state.
     */
+0 −223
Original line number Diff line number Diff line
/*
 * 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 com.android.inputmethod.accessibility;

import android.content.Context;
import android.os.Message;
import android.view.MotionEvent;
import android.view.ViewConfiguration;

import com.android.inputmethod.keyboard.PointerTracker;
import com.android.inputmethod.latin.StaticInnerHandlerWrapper;

/**
 * Detects flick gestures within a stream of hover events.
 * <p>
 * A flick gesture is defined as a stream of hover events with the following
 * properties:
 * <ul>
 *   <li>Begins with a {@link MotionEvent#ACTION_HOVER_ENTER} event
 *   <li>Contains any number of {@link MotionEvent#ACTION_HOVER_MOVE}
 *       events
 *   <li>Ends with a {@link MotionEvent#ACTION_HOVER_EXIT} event
 *   <li>Maximum duration of 250 milliseconds
 *   <li>Minimum distance between enter and exit points must be at least equal to
 *       scaled double tap slop (see
 *       {@link ViewConfiguration#getScaledDoubleTapSlop()})
 * </ul>
 * <p>
 * Initial enter events are intercepted and cached until the stream fails to
 * satisfy the constraints defined above, at which point the cached enter event
 * is sent to its source {@link AccessibleKeyboardViewProxy} and subsequent move
 * and exit events are ignored.
 */
public abstract class FlickGestureDetector {
    public static final int FLICK_UP = 0;
    public static final int FLICK_RIGHT = 1;
    public static final int FLICK_LEFT = 2;
    public static final int FLICK_DOWN = 3;

    private final FlickHandler mFlickHandler;
    private final int mFlickRadiusSquare;

    private AccessibleKeyboardViewProxy mCachedView;
    private PointerTracker mCachedTracker;
    private MotionEvent mCachedHoverEnter;

    private static class FlickHandler extends StaticInnerHandlerWrapper<FlickGestureDetector> {
        private static final int MSG_FLICK_TIMEOUT = 1;

        /** The maximum duration of a flick gesture in milliseconds. */
        private static final int DELAY_FLICK_TIMEOUT = 250;

        public FlickHandler(FlickGestureDetector outerInstance) {
            super(outerInstance);
        }

        @Override
        public void handleMessage(Message msg) {
            final FlickGestureDetector gestureDetector = getOuterInstance();

            switch (msg.what) {
            case MSG_FLICK_TIMEOUT:
                gestureDetector.clearFlick(true);
            }
        }

        public void startFlickTimeout() {
            cancelFlickTimeout();
            sendEmptyMessageDelayed(MSG_FLICK_TIMEOUT, DELAY_FLICK_TIMEOUT);
        }

        public void cancelFlickTimeout() {
            removeMessages(MSG_FLICK_TIMEOUT);
        }
    }

    /**
     * Creates a new flick gesture detector.
     *
     * @param context The parent context.
     */
    public FlickGestureDetector(Context context) {
        final int doubleTapSlop = ViewConfiguration.get(context).getScaledDoubleTapSlop();

        mFlickHandler = new FlickHandler(this);
        mFlickRadiusSquare = doubleTapSlop * doubleTapSlop;
    }

    /**
     * Processes motion events to detect flick gestures.
     *
     * @param event The current event.
     * @param view The source of the event.
     * @param tracker A pointer tracker for the event.
     * @return {@code true} if the event was handled.
     */
    public boolean onHoverEvent(MotionEvent event, AccessibleKeyboardViewProxy view,
            PointerTracker tracker) {
        // Always cache and consume the first hover event.
        if (event.getAction() == MotionEvent.ACTION_HOVER_ENTER) {
            mCachedView = view;
            mCachedTracker = tracker;
            mCachedHoverEnter = MotionEvent.obtain(event);
            mFlickHandler.startFlickTimeout();
            return true;
        }

        // Stop if the event has already been canceled.
        if (mCachedHoverEnter == null) {
            return false;
        }

        final float distanceSquare = calculateDistanceSquare(mCachedHoverEnter, event);

        switch (event.getAction()) {
        case MotionEvent.ACTION_HOVER_MOVE:
            // Consume all valid move events before timeout.
            return true;
        case MotionEvent.ACTION_HOVER_EXIT:
            // Ignore exit events outside the flick radius.
            if (distanceSquare < mFlickRadiusSquare) {
                clearFlick(true);
                return false;
            } else {
                return dispatchFlick(mCachedHoverEnter, event);
            }
        default:
            return false;
        }
    }

    /**
     * Clears the cached flick information and optionally forwards the event to
     * the source view's internal hover event handler.
     *
     * @param sendCachedEvent Set to {@code true} to forward the hover event to
     *            the source view.
     */
    private void clearFlick(boolean sendCachedEvent) {
        mFlickHandler.cancelFlickTimeout();

        if (mCachedHoverEnter != null) {
            if (sendCachedEvent) {
                mCachedView.onHoverEventInternal(mCachedHoverEnter, mCachedTracker);
            }
            mCachedHoverEnter.recycle();
            mCachedHoverEnter = null;
        }

        mCachedTracker = null;
        mCachedView = null;
    }

    /**
     * Computes the direction of a flick gesture and forwards it to
     * {@link #onFlick(MotionEvent, MotionEvent, int)} for handling.
     *
     * @param e1 The {@link MotionEvent#ACTION_HOVER_ENTER} event where the flick started.
     * @param e2 The {@link MotionEvent#ACTION_HOVER_EXIT} event where the flick ended.
     * @return {@code true} if the flick event was handled.
     */
    private boolean dispatchFlick(MotionEvent e1, MotionEvent e2) {
        clearFlick(false);

        final float dX = e2.getX() - e1.getX();
        final float dY = e2.getY() - e1.getY();
        final int direction;

        if (dY > dX) {
            if (dY > -dX) {
                direction = FLICK_DOWN;
            } else {
                direction = FLICK_LEFT;
            }
        } else {
            if (dY > -dX) {
                direction = FLICK_RIGHT;
            } else {
                direction = FLICK_UP;
            }
        }

        return onFlick(e1, e2, direction);
    }

    private float calculateDistanceSquare(MotionEvent e1, MotionEvent e2) {
        final float dX = e2.getX() - e1.getX();
        final float dY = e2.getY() - e1.getY();
        return (dX * dX) + (dY * dY);
    }

    /**
     * Handles a detected flick gesture.
     *
     * @param e1 The {@link MotionEventCompatUtils#ACTION_HOVER_ENTER} event
     *            where the flick started.
     * @param e2 The {@link MotionEventCompatUtils#ACTION_HOVER_EXIT} event
     *            where the flick ended.
     * @param direction The direction of the flick event, one of:
     *            <ul>
     *              <li>{@link #FLICK_UP}
     *              <li>{@link #FLICK_DOWN}
     *              <li>{@link #FLICK_LEFT}
     *              <li>{@link #FLICK_RIGHT}
     *            </ul>
     * @return {@code true} if the flick event was handled.
     */
    public abstract boolean onFlick(MotionEvent e1, MotionEvent e2, int direction);
}