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