Home | History | Annotate | Download | only in accessibility
      1 /*
      2  * Copyright (C) 2011 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
      5  * use this file except in compliance with the License. You may obtain a copy of
      6  * the License at
      7  *
      8  * http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
     12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
     13  * License for the specific language governing permissions and limitations under
     14  * the License.
     15  */
     16 
     17 package com.android.inputmethod.accessibility;
     18 
     19 import android.content.Context;
     20 import android.content.SharedPreferences;
     21 import android.graphics.Color;
     22 import android.graphics.Paint;
     23 import android.inputmethodservice.InputMethodService;
     24 import android.util.Log;
     25 import android.view.MotionEvent;
     26 import android.view.accessibility.AccessibilityEvent;
     27 import android.view.inputmethod.EditorInfo;
     28 
     29 import com.android.inputmethod.compat.AccessibilityEventCompatUtils;
     30 import com.android.inputmethod.compat.MotionEventCompatUtils;
     31 import com.android.inputmethod.keyboard.Key;
     32 import com.android.inputmethod.keyboard.KeyDetector;
     33 import com.android.inputmethod.keyboard.LatinKeyboardView;
     34 import com.android.inputmethod.keyboard.PointerTracker;
     35 
     36 public class AccessibleKeyboardViewProxy {
     37     private static final String TAG = AccessibleKeyboardViewProxy.class.getSimpleName();
     38     private static final AccessibleKeyboardViewProxy sInstance = new AccessibleKeyboardViewProxy();
     39 
     40     private InputMethodService mInputMethod;
     41     private FlickGestureDetector mGestureDetector;
     42     private LatinKeyboardView mView;
     43     private AccessibleKeyboardActionListener mListener;
     44 
     45     private int mLastHoverKeyIndex = KeyDetector.NOT_A_KEY;
     46 
     47     public static void init(InputMethodService inputMethod, SharedPreferences prefs) {
     48         sInstance.initInternal(inputMethod, prefs);
     49         sInstance.mListener = AccessibleInputMethodServiceProxy.getInstance();
     50     }
     51 
     52     public static AccessibleKeyboardViewProxy getInstance() {
     53         return sInstance;
     54     }
     55 
     56     public static void setView(LatinKeyboardView view) {
     57         sInstance.mView = view;
     58     }
     59 
     60     private AccessibleKeyboardViewProxy() {
     61         // Not publicly instantiable.
     62     }
     63 
     64     private void initInternal(InputMethodService inputMethod, SharedPreferences prefs) {
     65         final Paint paint = new Paint();
     66         paint.setTextAlign(Paint.Align.LEFT);
     67         paint.setTextSize(14.0f);
     68         paint.setAntiAlias(true);
     69         paint.setColor(Color.YELLOW);
     70 
     71         mInputMethod = inputMethod;
     72         mGestureDetector = new KeyboardFlickGestureDetector(inputMethod);
     73     }
     74 
     75     public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event,
     76             PointerTracker tracker) {
     77         if (mView == null) {
     78             Log.e(TAG, "No keyboard view set!");
     79             return false;
     80         }
     81 
     82         switch (event.getEventType()) {
     83         case AccessibilityEventCompatUtils.TYPE_VIEW_HOVER_ENTER:
     84             final Key key = tracker.getKey(mLastHoverKeyIndex);
     85 
     86             if (key == null)
     87                 break;
     88 
     89             final EditorInfo info = mInputMethod.getCurrentInputEditorInfo();
     90             final boolean shouldObscure = AccessibilityUtils.getInstance().shouldObscureInput(info);
     91             final CharSequence description = KeyCodeDescriptionMapper.getInstance()
     92                     .getDescriptionForKey(mView.getContext(), mView.getKeyboard(), key,
     93                             shouldObscure);
     94 
     95             if (description == null)
     96                 return false;
     97 
     98             event.getText().add(description);
     99 
    100             break;
    101         }
    102 
    103         return true;
    104     }
    105 
    106     /**
    107      * Receives hover events when accessibility is turned on in SDK versions ICS
    108      * and higher.
    109      *
    110      * @param event The hover event.
    111      * @return {@code true} if the event is handled
    112      */
    113     public boolean dispatchHoverEvent(MotionEvent event, PointerTracker tracker) {
    114         if (mGestureDetector.onHoverEvent(event, this, tracker))
    115             return true;
    116 
    117         return onHoverEventInternal(event, tracker);
    118     }
    119 
    120     /**
    121      * Handles touch exploration events when Accessibility is turned on.
    122      *
    123      * @param event The touch exploration hover event.
    124      * @return {@code true} if the event was handled
    125      */
    126     /*package*/ boolean onHoverEventInternal(MotionEvent event, PointerTracker tracker) {
    127         final int x = (int) event.getX();
    128         final int y = (int) event.getY();
    129 
    130         switch (event.getAction()) {
    131         case MotionEventCompatUtils.ACTION_HOVER_ENTER:
    132         case MotionEventCompatUtils.ACTION_HOVER_MOVE:
    133             final int keyIndex = tracker.getKeyIndexOn(x, y);
    134 
    135             if (keyIndex != mLastHoverKeyIndex) {
    136                 fireKeyHoverEvent(tracker, mLastHoverKeyIndex, false);
    137                 mLastHoverKeyIndex = keyIndex;
    138                 fireKeyHoverEvent(tracker, mLastHoverKeyIndex, true);
    139             }
    140 
    141             return true;
    142         }
    143 
    144         return false;
    145     }
    146 
    147     private void fireKeyHoverEvent(PointerTracker tracker, int keyIndex, boolean entering) {
    148         if (mListener == null) {
    149             Log.e(TAG, "No accessible keyboard action listener set!");
    150             return;
    151         }
    152 
    153         if (mView == null) {
    154             Log.e(TAG, "No keyboard view set!");
    155             return;
    156         }
    157 
    158         if (keyIndex == KeyDetector.NOT_A_KEY)
    159             return;
    160 
    161         final Key key = tracker.getKey(keyIndex);
    162 
    163         if (key == null)
    164             return;
    165 
    166         if (entering) {
    167             mListener.onHoverEnter(key.mCode);
    168             mView.sendAccessibilityEvent(AccessibilityEventCompatUtils.TYPE_VIEW_HOVER_ENTER);
    169         } else {
    170             mListener.onHoverExit(key.mCode);
    171             mView.sendAccessibilityEvent(AccessibilityEventCompatUtils.TYPE_VIEW_HOVER_EXIT);
    172         }
    173     }
    174 
    175     private class KeyboardFlickGestureDetector extends FlickGestureDetector {
    176         public KeyboardFlickGestureDetector(Context context) {
    177             super(context);
    178         }
    179 
    180         @Override
    181         public boolean onFlick(MotionEvent e1, MotionEvent e2, int direction) {
    182             if (mListener != null) {
    183                 mListener.onFlickGesture(direction);
    184             }
    185             return true;
    186         }
    187     }
    188 }
    189