Merge "Added support for touch exploration to Latin IME."
commit
8521781fd7
|
@ -131,6 +131,81 @@
|
|||
<!-- Label for "Wait" key of phone number keyboard. Must be short to fit on key! [CHAR LIMIT=5]-->
|
||||
<string name="label_wait_key">Wait</string>
|
||||
|
||||
<!-- Spoken description for the currently entered text -->
|
||||
<string name="spoken_current_text_is">Current text is "%s"</string>
|
||||
<!-- Spoken description when there is no text entered -->
|
||||
<string name="spoken_no_text_entered">No text entered</string>
|
||||
|
||||
<!-- Spoken description for unknown keyboard keys. -->
|
||||
<string name="spoken_description_unknown">Key code %d</string>
|
||||
<!-- Spoken description for the "Shift" keyboard key. -->
|
||||
<string name="spoken_description_shift">Shift</string>
|
||||
<!-- Spoken description for the "Shift" keyboard key's pressed state. -->
|
||||
<string name="spoken_description_shift_shifted">Shift enabled</string>
|
||||
<!-- Spoken description for the "Shift" keyboard key's pressed state. -->
|
||||
<string name="spoken_description_caps_lock">Caps lock enabled</string>
|
||||
<!-- Spoken description for the "Delete" keyboard key. -->
|
||||
<string name="spoken_description_delete">Delete</string>
|
||||
<!-- Spoken description for the "To Symbol" keyboard key. -->
|
||||
<string name="spoken_description_to_symbol">Symbols</string>
|
||||
<!-- Spoken description for the "To Alpha" keyboard key. -->
|
||||
<string name="spoken_description_to_alpha">Letters</string>
|
||||
<!-- Spoken description for the "To Numbers" keyboard key. -->
|
||||
<string name="spoken_description_to_numeric">Numbers</string>
|
||||
<!-- Spoken description for the "Settings" keyboard key. -->
|
||||
<string name="spoken_description_settings">Settings</string>
|
||||
<!-- Spoken description for the "Tab" keyboard key. -->
|
||||
<string name="spoken_description_tab">Tab</string>
|
||||
<!-- Spoken description for the "Space" keyboard key. -->
|
||||
<string name="spoken_description_space">Space</string>
|
||||
<!-- Spoken description for the "Mic" keyboard key. -->
|
||||
<string name="spoken_description_mic">Voice input</string>
|
||||
<!-- Spoken description for the "Smiley" keyboard key. -->
|
||||
<string name="spoken_description_smiley">Smiley face</string>
|
||||
<!-- Spoken description for the "Return" keyboard key. -->
|
||||
<string name="spoken_description_return">Return</string>
|
||||
|
||||
<!-- Spoken description for the "," keyboard key. -->
|
||||
<string name="spoken_description_comma">Comma</string>
|
||||
<!-- Spoken description for the "." keyboard key. -->
|
||||
<string name="spoken_description_period">Period</string>
|
||||
<!-- Spoken description for the "(" keyboard key. -->
|
||||
<string name="spoken_description_left_parenthesis">Left parenthesis</string>
|
||||
<!-- Spoken description for the ")" keyboard key. -->
|
||||
<string name="spoken_description_right_parenthesis">Right parenthesis</string>
|
||||
<!-- Spoken description for the ":" keyboard key. -->
|
||||
<string name="spoken_description_colon">Colon</string>
|
||||
<!-- Spoken description for the ";" keyboard key. -->
|
||||
<string name="spoken_description_semicolon">Semicolon</string>
|
||||
<!-- Spoken description for the "!" keyboard key. -->
|
||||
<string name="spoken_description_exclamation_mark">Exclamation mark</string>
|
||||
<!-- Spoken description for the "?" keyboard key. -->
|
||||
<string name="spoken_description_question_mark">Question mark</string>
|
||||
<!-- Spoken description for the """ keyboard key. -->
|
||||
<string name="spoken_description_double_quote">Double quote</string>
|
||||
<!-- Spoken description for the "'" keyboard key. -->
|
||||
<string name="spoken_description_single_quote">Single quote</string>
|
||||
<!-- Spoken description for the "•" keyboard key. -->
|
||||
<string name="spoken_description_dot">Dot</string>
|
||||
<!-- Spoken description for the "√" keyboard key. -->
|
||||
<string name="spoken_description_square_root">Square root</string>
|
||||
<!-- Spoken description for the "π" keyboard key. -->
|
||||
<string name="spoken_description_pi">Pi</string>
|
||||
<!-- Spoken description for the "Δ" keyboard key. -->
|
||||
<string name="spoken_description_delta">Delta</string>
|
||||
<!-- Spoken description for the "™" keyboard key. -->
|
||||
<string name="spoken_description_trademark">Trademark</string>
|
||||
<!-- Spoken description for the "℅" keyboard key. -->
|
||||
<string name="spoken_description_care_of">Care of</string>
|
||||
<!-- Spoken description for the "*" keyboard key. -->
|
||||
<string name="spoken_description_star">Star</string>
|
||||
<!-- Spoken description for the "#" keyboard key. -->
|
||||
<string name="spoken_description_pound">Pound</string>
|
||||
<!-- Spoken description for the "…" keyboard key. -->
|
||||
<string name="spoken_description_ellipsis">Ellipsis</string>
|
||||
<!-- Spoken description for the "„" keyboard key. -->
|
||||
<string name="spoken_description_low_double_quote">Low double quote</string>
|
||||
|
||||
<!-- Voice related labels -->
|
||||
|
||||
<!-- Title of the warning dialog that shows when a user initiates voice input for
|
||||
|
|
|
@ -0,0 +1,133 @@
|
|||
/*
|
||||
* 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.accessibilityservice.AccessibilityServiceInfo;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.inputmethodservice.InputMethodService;
|
||||
import android.os.SystemClock;
|
||||
import android.util.Log;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
import android.view.accessibility.AccessibilityManager;
|
||||
|
||||
import com.android.inputmethod.compat.AccessibilityEventCompatUtils;
|
||||
import com.android.inputmethod.compat.AccessibilityManagerCompatWrapper;
|
||||
import com.android.inputmethod.compat.MotionEventCompatUtils;
|
||||
|
||||
public class AccessibilityUtils {
|
||||
private static final String TAG = AccessibilityUtils.class.getSimpleName();
|
||||
private static final String CLASS = AccessibilityUtils.class.getClass().getName();
|
||||
private static final String PACKAGE = AccessibilityUtils.class.getClass().getPackage()
|
||||
.getName();
|
||||
|
||||
private static final AccessibilityUtils sInstance = new AccessibilityUtils();
|
||||
|
||||
private AccessibilityManager mAccessibilityManager;
|
||||
private AccessibilityManagerCompatWrapper mCompatManager;
|
||||
|
||||
/*
|
||||
* Setting this constant to {@code false} will disable all keyboard
|
||||
* accessibility code, regardless of whether Accessibility is turned on in
|
||||
* the system settings. It should ONLY be used in the event of an emergency.
|
||||
*/
|
||||
private static final boolean ENABLE_ACCESSIBILITY = true;
|
||||
|
||||
public static void init(InputMethodService inputMethod, SharedPreferences prefs) {
|
||||
if (!ENABLE_ACCESSIBILITY)
|
||||
return;
|
||||
|
||||
// These only need to be initialized if the kill switch is off.
|
||||
sInstance.initInternal(inputMethod, prefs);
|
||||
KeyCodeDescriptionMapper.init(inputMethod, prefs);
|
||||
AccessibleInputMethodServiceProxy.init(inputMethod, prefs);
|
||||
AccessibleKeyboardViewProxy.init(inputMethod, prefs);
|
||||
}
|
||||
|
||||
public static AccessibilityUtils getInstance() {
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
private AccessibilityUtils() {
|
||||
// This class is not publicly instantiable.
|
||||
}
|
||||
|
||||
private void initInternal(Context context, SharedPreferences prefs) {
|
||||
mAccessibilityManager = (AccessibilityManager) context
|
||||
.getSystemService(Context.ACCESSIBILITY_SERVICE);
|
||||
mCompatManager = new AccessibilityManagerCompatWrapper(mAccessibilityManager);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if touch exploration is enabled. Currently, this
|
||||
* means that the kill switch is off, the device supports touch exploration,
|
||||
* and a spoken feedback service is turned on.
|
||||
*
|
||||
* @return {@code true} if touch exploration is enabled.
|
||||
*/
|
||||
public boolean isTouchExplorationEnabled() {
|
||||
return ENABLE_ACCESSIBILITY
|
||||
&& AccessibilityEventCompatUtils.supportsTouchExploration()
|
||||
&& mAccessibilityManager.isEnabled()
|
||||
&& !mCompatManager.getEnabledAccessibilityServiceList(
|
||||
AccessibilityServiceInfo.FEEDBACK_SPOKEN).isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@true} if the provided event is a touch exploration (e.g. hover)
|
||||
* event. This is used to determine whether the event should be processed by
|
||||
* the touch exploration code within the keyboard.
|
||||
*
|
||||
* @param event The event to check.
|
||||
* @return {@true} is the event is a touch exploration event
|
||||
*/
|
||||
public boolean isTouchExplorationEvent(MotionEvent event) {
|
||||
final int action = event.getAction();
|
||||
|
||||
return action == MotionEventCompatUtils.ACTION_HOVER_ENTER
|
||||
|| action == MotionEventCompatUtils.ACTION_HOVER_EXIT
|
||||
|| action == MotionEventCompatUtils.ACTION_HOVER_MOVE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the specified text to the {@link AccessibilityManager} to be
|
||||
* spoken.
|
||||
*
|
||||
* @param text the text to speak
|
||||
*/
|
||||
public void speak(CharSequence text) {
|
||||
if (!mAccessibilityManager.isEnabled()) {
|
||||
Log.e(TAG, "Attempted to speak when accessibility was disabled!");
|
||||
return;
|
||||
}
|
||||
|
||||
// The following is a hack to avoid using the heavy-weight TextToSpeech
|
||||
// class. Instead, we're just forcing a fake AccessibilityEvent into
|
||||
// the screen reader to make it speak.
|
||||
final AccessibilityEvent event = AccessibilityEvent
|
||||
.obtain(AccessibilityEventCompatUtils.TYPE_VIEW_HOVER_ENTER);
|
||||
|
||||
event.setPackageName(PACKAGE);
|
||||
event.setClassName(CLASS);
|
||||
event.setEventTime(SystemClock.uptimeMillis());
|
||||
event.setEnabled(true);
|
||||
event.getText().add(text);
|
||||
|
||||
mAccessibilityManager.sendAccessibilityEvent(event);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,129 @@
|
|||
/*
|
||||
* 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.SharedPreferences;
|
||||
import android.inputmethodservice.InputMethodService;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.text.TextUtils;
|
||||
import android.view.inputmethod.ExtractedText;
|
||||
import android.view.inputmethod.ExtractedTextRequest;
|
||||
|
||||
import com.android.inputmethod.latin.R;
|
||||
|
||||
public class AccessibleInputMethodServiceProxy implements AccessibleKeyboardActionListener {
|
||||
private static final AccessibleInputMethodServiceProxy sInstance =
|
||||
new AccessibleInputMethodServiceProxy();
|
||||
|
||||
/*
|
||||
* Delay for the handler event that's fired when Accessibility is on and the
|
||||
* user hovers outside of any valid keys. This is used to let the user know
|
||||
* that if they lift their finger, nothing will be typed.
|
||||
*/
|
||||
private static final long DELAY_NO_HOVER_SELECTION = 250;
|
||||
|
||||
private InputMethodService mInputMethod;
|
||||
|
||||
private AccessibilityHandler mAccessibilityHandler;
|
||||
|
||||
private class AccessibilityHandler extends Handler {
|
||||
private static final int MSG_NO_HOVER_SELECTION = 0;
|
||||
|
||||
public AccessibilityHandler(Looper looper) {
|
||||
super(looper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case MSG_NO_HOVER_SELECTION:
|
||||
notifyNoHoverSelection();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void postNoHoverSelection() {
|
||||
removeMessages(MSG_NO_HOVER_SELECTION);
|
||||
sendEmptyMessageDelayed(MSG_NO_HOVER_SELECTION, DELAY_NO_HOVER_SELECTION);
|
||||
}
|
||||
|
||||
public void cancelNoHoverSelection() {
|
||||
removeMessages(MSG_NO_HOVER_SELECTION);
|
||||
}
|
||||
}
|
||||
|
||||
public static void init(InputMethodService inputMethod, SharedPreferences prefs) {
|
||||
sInstance.initInternal(inputMethod, prefs);
|
||||
}
|
||||
|
||||
public static AccessibleInputMethodServiceProxy getInstance() {
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
private AccessibleInputMethodServiceProxy() {
|
||||
// Not publicly instantiable.
|
||||
}
|
||||
|
||||
private void initInternal(InputMethodService inputMethod, SharedPreferences prefs) {
|
||||
mInputMethod = inputMethod;
|
||||
mAccessibilityHandler = new AccessibilityHandler(inputMethod.getMainLooper());
|
||||
}
|
||||
|
||||
/**
|
||||
* If touch exploration is enabled, cancels the event sent by
|
||||
* {@link AccessibleInputMethodServiceProxy#onHoverExit(int)} because the
|
||||
* user is currently hovering above a key.
|
||||
*/
|
||||
@Override
|
||||
public void onHoverEnter(int primaryCode) {
|
||||
mAccessibilityHandler.cancelNoHoverSelection();
|
||||
}
|
||||
|
||||
/**
|
||||
* If touch exploration is enabled, sends a delayed event to notify the user
|
||||
* that they are not currently hovering above a key.
|
||||
*/
|
||||
@Override
|
||||
public void onHoverExit(int primaryCode) {
|
||||
mAccessibilityHandler.postNoHoverSelection();
|
||||
}
|
||||
|
||||
/**
|
||||
* When Accessibility is turned on, notifies the user that they are not
|
||||
* currently hovering above a key. By default this will speak the currently
|
||||
* entered text.
|
||||
*/
|
||||
private void notifyNoHoverSelection() {
|
||||
final ExtractedText extracted = mInputMethod.getCurrentInputConnection().getExtractedText(
|
||||
new ExtractedTextRequest(), 0);
|
||||
|
||||
if (extracted == null)
|
||||
return;
|
||||
|
||||
final CharSequence text;
|
||||
|
||||
if (TextUtils.isEmpty(extracted.text)) {
|
||||
text = mInputMethod.getString(R.string.spoken_no_text_entered);
|
||||
} else {
|
||||
text = mInputMethod.getString(R.string.spoken_current_text_is, extracted.text);
|
||||
}
|
||||
|
||||
AccessibilityUtils.getInstance().speak(text);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* 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 {
|
||||
/**
|
||||
* Called when the user hovers inside a key. This is sent only when
|
||||
* Accessibility is turned on. For keys that repeat, this is only called
|
||||
* once.
|
||||
*
|
||||
* @param primaryCode the code of the key that was hovered over
|
||||
*/
|
||||
public void onHoverEnter(int primaryCode);
|
||||
|
||||
/**
|
||||
* Called when the user hovers outside a key. This is sent only when
|
||||
* Accessibility is turned on. For keys that repeat, this is only called
|
||||
* once.
|
||||
*
|
||||
* @param primaryCode the code of the key that was hovered over
|
||||
*/
|
||||
public void onHoverExit(int primaryCode);
|
||||
}
|
|
@ -0,0 +1,201 @@
|
|||
/*
|
||||
* 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.content.SharedPreferences;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.util.Log;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.ViewConfiguration;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
|
||||
import com.android.inputmethod.compat.AccessibilityEventCompatUtils;
|
||||
import com.android.inputmethod.compat.MotionEventCompatUtils;
|
||||
import com.android.inputmethod.keyboard.Key;
|
||||
import com.android.inputmethod.keyboard.KeyDetector;
|
||||
import com.android.inputmethod.keyboard.KeyboardView;
|
||||
import com.android.inputmethod.keyboard.PointerTracker;
|
||||
|
||||
public class AccessibleKeyboardViewProxy {
|
||||
private static final String TAG = AccessibleKeyboardViewProxy.class.getSimpleName();
|
||||
private static final AccessibleKeyboardViewProxy sInstance = new AccessibleKeyboardViewProxy();
|
||||
|
||||
// Delay in milliseconds between key press DOWN and UP events
|
||||
private static final long DELAY_KEY_PRESS = 10;
|
||||
|
||||
private int mScaledEdgeSlop;
|
||||
private KeyboardView mView;
|
||||
private AccessibleKeyboardActionListener mListener;
|
||||
|
||||
private int mLastHoverKeyIndex = KeyDetector.NOT_A_KEY;
|
||||
private int mLastX = -1;
|
||||
private int mLastY = -1;
|
||||
|
||||
public static void init(Context context, SharedPreferences prefs) {
|
||||
sInstance.initInternal(context, prefs);
|
||||
sInstance.mListener = AccessibleInputMethodServiceProxy.getInstance();
|
||||
}
|
||||
|
||||
public static AccessibleKeyboardViewProxy getInstance() {
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
public static void setView(KeyboardView view) {
|
||||
sInstance.mView = view;
|
||||
}
|
||||
|
||||
private AccessibleKeyboardViewProxy() {
|
||||
// Not publicly instantiable.
|
||||
}
|
||||
|
||||
private void initInternal(Context context, SharedPreferences prefs) {
|
||||
final Paint paint = new Paint();
|
||||
paint.setTextAlign(Paint.Align.LEFT);
|
||||
paint.setTextSize(14.0f);
|
||||
paint.setAntiAlias(true);
|
||||
paint.setColor(Color.YELLOW);
|
||||
|
||||
mScaledEdgeSlop = ViewConfiguration.get(context).getScaledEdgeSlop();
|
||||
}
|
||||
|
||||
public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event,
|
||||
PointerTracker tracker) {
|
||||
if (mView == null) {
|
||||
Log.e(TAG, "No keyboard view set!");
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (event.getEventType()) {
|
||||
case AccessibilityEventCompatUtils.TYPE_VIEW_HOVER_ENTER:
|
||||
final Key key = tracker.getKey(mLastHoverKeyIndex);
|
||||
|
||||
if (key == null)
|
||||
break;
|
||||
|
||||
final CharSequence description = KeyCodeDescriptionMapper.getInstance()
|
||||
.getDescriptionForKey(mView.getContext(), mView.getKeyboard(), key);
|
||||
|
||||
if (description == null)
|
||||
return false;
|
||||
|
||||
event.getText().add(description);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Receives hover events when accessibility is turned on in API > 11. In
|
||||
* earlier API levels, events are manually routed from onTouchEvent.
|
||||
*
|
||||
* @param event The hover event.
|
||||
* @return {@code true} if the event is handled
|
||||
*/
|
||||
public boolean onHoverEvent(MotionEvent event, PointerTracker tracker) {
|
||||
return onTouchExplorationEvent(event, tracker);
|
||||
}
|
||||
|
||||
public boolean dispatchTouchEvent(MotionEvent event) {
|
||||
// Since touch exploration translates hover double-tap to a regular
|
||||
// single-tap, we're going to drop non-touch exploration events.
|
||||
if (!AccessibilityUtils.getInstance().isTouchExplorationEvent(event))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles touch exploration events when Accessibility is turned on.
|
||||
*
|
||||
* @param event The touch exploration hover event.
|
||||
* @return {@code true} if the event was handled
|
||||
*/
|
||||
private boolean onTouchExplorationEvent(MotionEvent event, PointerTracker tracker) {
|
||||
final int x = (int) event.getX();
|
||||
final int y = (int) event.getY();
|
||||
|
||||
switch (event.getAction()) {
|
||||
case MotionEventCompatUtils.ACTION_HOVER_ENTER:
|
||||
case MotionEventCompatUtils.ACTION_HOVER_MOVE:
|
||||
final int keyIndex = tracker.getKeyIndexOn(x, y);
|
||||
|
||||
if (keyIndex != mLastHoverKeyIndex) {
|
||||
fireKeyHoverEvent(tracker, mLastHoverKeyIndex, false);
|
||||
mLastHoverKeyIndex = keyIndex;
|
||||
mLastX = x;
|
||||
mLastY = y;
|
||||
fireKeyHoverEvent(tracker, mLastHoverKeyIndex, true);
|
||||
}
|
||||
|
||||
return true;
|
||||
case MotionEventCompatUtils.ACTION_HOVER_EXIT:
|
||||
final int width = mView.getWidth();
|
||||
final int height = mView.getHeight();
|
||||
|
||||
if (x < mScaledEdgeSlop || y < mScaledEdgeSlop || x >= (width - mScaledEdgeSlop)
|
||||
|| y >= (height - mScaledEdgeSlop)) {
|
||||
fireKeyHoverEvent(tracker, mLastHoverKeyIndex, false);
|
||||
mLastHoverKeyIndex = KeyDetector.NOT_A_KEY;
|
||||
mLastX = -1;
|
||||
mLastY = -1;
|
||||
} else if (mLastHoverKeyIndex != KeyDetector.NOT_A_KEY) {
|
||||
fireKeyPressEvent(tracker, mLastX, mLastY, event.getEventTime());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void fireKeyHoverEvent(PointerTracker tracker, int keyIndex, boolean entering) {
|
||||
if (mListener == null) {
|
||||
Log.e(TAG, "No accessible keyboard action listener set!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (mView == null) {
|
||||
Log.e(TAG, "No keyboard view set!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (keyIndex == KeyDetector.NOT_A_KEY)
|
||||
return;
|
||||
|
||||
final Key key = tracker.getKey(keyIndex);
|
||||
|
||||
if (key == null)
|
||||
return;
|
||||
|
||||
if (entering) {
|
||||
mListener.onHoverEnter(key.mCode);
|
||||
mView.sendAccessibilityEvent(AccessibilityEventCompatUtils.TYPE_VIEW_HOVER_ENTER);
|
||||
} else {
|
||||
mListener.onHoverExit(key.mCode);
|
||||
mView.sendAccessibilityEvent(AccessibilityEventCompatUtils.TYPE_VIEW_HOVER_EXIT);
|
||||
}
|
||||
}
|
||||
|
||||
private void fireKeyPressEvent(PointerTracker tracker, int x, int y, long eventTime) {
|
||||
tracker.onDownEvent(x, y, eventTime, null);
|
||||
tracker.onUpEvent(x, y, eventTime + DELAY_KEY_PRESS, null);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,226 @@
|
|||
/*
|
||||
* 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.content.SharedPreferences;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.android.inputmethod.keyboard.Key;
|
||||
import com.android.inputmethod.keyboard.Keyboard;
|
||||
import com.android.inputmethod.keyboard.KeyboardId;
|
||||
import com.android.inputmethod.latin.R;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
public class KeyCodeDescriptionMapper {
|
||||
private static KeyCodeDescriptionMapper sInstance = new KeyCodeDescriptionMapper();
|
||||
|
||||
// Map of key labels to spoken description resource IDs
|
||||
private final HashMap<CharSequence, Integer> mKeyLabelMap;
|
||||
|
||||
// Map of key codes to spoken description resource IDs
|
||||
private final HashMap<Integer, Integer> mKeyCodeMap;
|
||||
|
||||
// Map of shifted key codes to spoken description resource IDs
|
||||
private final HashMap<Integer, Integer> mShiftedKeyCodeMap;
|
||||
|
||||
// Map of shift-locked key codes to spoken description resource IDs
|
||||
private final HashMap<Integer, Integer> mShiftLockedKeyCodeMap;
|
||||
|
||||
public static void init(Context context, SharedPreferences prefs) {
|
||||
sInstance.initInternal(context, prefs);
|
||||
}
|
||||
|
||||
public static KeyCodeDescriptionMapper getInstance() {
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
private KeyCodeDescriptionMapper() {
|
||||
mKeyLabelMap = new HashMap<CharSequence, Integer>();
|
||||
mKeyCodeMap = new HashMap<Integer, Integer>();
|
||||
mShiftedKeyCodeMap = new HashMap<Integer, Integer>();
|
||||
mShiftLockedKeyCodeMap = new HashMap<Integer, Integer>();
|
||||
}
|
||||
|
||||
private void initInternal(Context context, SharedPreferences prefs) {
|
||||
// Manual label substitutions for key labels with no string resource
|
||||
mKeyLabelMap.put(":-)", R.string.spoken_description_smiley);
|
||||
|
||||
// Symbols that most TTS engines can't speak
|
||||
mKeyCodeMap.put((int) '.', R.string.spoken_description_period);
|
||||
mKeyCodeMap.put((int) ',', R.string.spoken_description_comma);
|
||||
mKeyCodeMap.put((int) '(', R.string.spoken_description_left_parenthesis);
|
||||
mKeyCodeMap.put((int) ')', R.string.spoken_description_right_parenthesis);
|
||||
mKeyCodeMap.put((int) ':', R.string.spoken_description_colon);
|
||||
mKeyCodeMap.put((int) ';', R.string.spoken_description_semicolon);
|
||||
mKeyCodeMap.put((int) '!', R.string.spoken_description_exclamation_mark);
|
||||
mKeyCodeMap.put((int) '?', R.string.spoken_description_question_mark);
|
||||
mKeyCodeMap.put((int) '\"', R.string.spoken_description_double_quote);
|
||||
mKeyCodeMap.put((int) '\'', R.string.spoken_description_single_quote);
|
||||
mKeyCodeMap.put((int) '*', R.string.spoken_description_star);
|
||||
mKeyCodeMap.put((int) '#', R.string.spoken_description_pound);
|
||||
mKeyCodeMap.put((int) ' ', R.string.spoken_description_space);
|
||||
|
||||
// Non-ASCII symbols (must use escape codes!)
|
||||
mKeyCodeMap.put((int) '\u2022', R.string.spoken_description_dot);
|
||||
mKeyCodeMap.put((int) '\u221A', R.string.spoken_description_square_root);
|
||||
mKeyCodeMap.put((int) '\u03C0', R.string.spoken_description_pi);
|
||||
mKeyCodeMap.put((int) '\u0394', R.string.spoken_description_delta);
|
||||
mKeyCodeMap.put((int) '\u2122', R.string.spoken_description_trademark);
|
||||
mKeyCodeMap.put((int) '\u2105', R.string.spoken_description_care_of);
|
||||
mKeyCodeMap.put((int) '\u2026', R.string.spoken_description_ellipsis);
|
||||
mKeyCodeMap.put((int) '\u201E', R.string.spoken_description_low_double_quote);
|
||||
|
||||
// Special non-character codes defined in Keyboard
|
||||
mKeyCodeMap.put(Keyboard.CODE_DELETE, R.string.spoken_description_delete);
|
||||
mKeyCodeMap.put(Keyboard.CODE_ENTER, R.string.spoken_description_return);
|
||||
mKeyCodeMap.put(Keyboard.CODE_SETTINGS, R.string.spoken_description_settings);
|
||||
mKeyCodeMap.put(Keyboard.CODE_SHIFT, R.string.spoken_description_shift);
|
||||
mKeyCodeMap.put(Keyboard.CODE_SHORTCUT, R.string.spoken_description_mic);
|
||||
mKeyCodeMap.put(Keyboard.CODE_SWITCH_ALPHA_SYMBOL, R.string.spoken_description_to_symbol);
|
||||
mKeyCodeMap.put(Keyboard.CODE_TAB, R.string.spoken_description_tab);
|
||||
|
||||
// Shifted versions of non-character codes defined in Keyboard
|
||||
mShiftedKeyCodeMap.put(Keyboard.CODE_SHIFT, R.string.spoken_description_shift_shifted);
|
||||
|
||||
// Shift-locked versions of non-character codes defined in Keyboard
|
||||
mShiftLockedKeyCodeMap.put(Keyboard.CODE_SHIFT, R.string.spoken_description_caps_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the localized description of the action performed by a specified
|
||||
* key based on the current keyboard state.
|
||||
* <p>
|
||||
* The order of precedence for key descriptions is:
|
||||
* <ol>
|
||||
* <li>Manually-defined based on the key label</li>
|
||||
* <li>Automatic or manually-defined based on the key code</li>
|
||||
* <li>Automatically based on the key label</li>
|
||||
* <li>{code null} for keys with no label or key code defined</li>
|
||||
* </p>
|
||||
*
|
||||
* @param context The package's context.
|
||||
* @param keyboard The keyboard on which the key resides.
|
||||
* @param key The key from which to obtain a description.
|
||||
* @return a character sequence describing the action performed by pressing
|
||||
* the key
|
||||
*/
|
||||
public CharSequence getDescriptionForKey(Context context, Keyboard keyboard, Key key) {
|
||||
if (key.mCode == Keyboard.CODE_SWITCH_ALPHA_SYMBOL) {
|
||||
final CharSequence description = getDescriptionForSwitchAlphaSymbol(context, keyboard);
|
||||
if (description != null)
|
||||
return description;
|
||||
}
|
||||
|
||||
if (!TextUtils.isEmpty(key.mLabel)) {
|
||||
final String label = key.mLabel.toString().trim();
|
||||
|
||||
if (mKeyLabelMap.containsKey(label)) {
|
||||
return context.getString(mKeyLabelMap.get(label));
|
||||
} else if (label.length() == 1
|
||||
|| (keyboard.isManualTemporaryUpperCase() && !TextUtils
|
||||
.isEmpty(key.mHintLetter))) {
|
||||
return getDescriptionForKeyCode(context, keyboard, key);
|
||||
} else {
|
||||
return label;
|
||||
}
|
||||
} else if (key.mCode != Keyboard.CODE_DUMMY) {
|
||||
return getDescriptionForKeyCode(context, keyboard, key);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a context-specific description for the CODE_SWITCH_ALPHA_SYMBOL
|
||||
* key or {@code null} if there is not a description provided for the
|
||||
* current keyboard context.
|
||||
*
|
||||
* @param context The package's context.
|
||||
* @param keyboard The keyboard on which the key resides.
|
||||
* @return a character sequence describing the action performed by pressing
|
||||
* the key
|
||||
*/
|
||||
private CharSequence getDescriptionForSwitchAlphaSymbol(Context context, Keyboard keyboard) {
|
||||
final KeyboardId id = keyboard.mId;
|
||||
|
||||
if (id.isAlphabetKeyboard()) {
|
||||
return context.getString(R.string.spoken_description_to_symbol);
|
||||
} else if (id.isSymbolsKeyboard()) {
|
||||
return context.getString(R.string.spoken_description_to_alpha);
|
||||
} else if (id.isPhoneSymbolsKeyboard()) {
|
||||
return context.getString(R.string.spoken_description_to_numeric);
|
||||
} else if (id.isPhoneKeyboard()) {
|
||||
return context.getString(R.string.spoken_description_to_symbol);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the keycode for the specified key given the current keyboard
|
||||
* state.
|
||||
*
|
||||
* @param keyboard The keyboard on which the key resides.
|
||||
* @param key The key from which to obtain a key code.
|
||||
* @return the key code for the specified key
|
||||
*/
|
||||
private int getCorrectKeyCode(Keyboard keyboard, Key key) {
|
||||
if (keyboard.isManualTemporaryUpperCase() && !TextUtils.isEmpty(key.mHintLetter)) {
|
||||
return key.mHintLetter.charAt(0);
|
||||
} else {
|
||||
return key.mCode;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a localized character sequence describing what will happen when
|
||||
* the specified key is pressed based on its key code.
|
||||
* <p>
|
||||
* The order of precedence for key code descriptions is:
|
||||
* <ol>
|
||||
* <li>Manually-defined shift-locked description</li>
|
||||
* <li>Manually-defined shifted description</li>
|
||||
* <li>Manually-defined normal description</li>
|
||||
* <li>Automatic based on the character represented by the key code</li>
|
||||
* <li>Fall-back for undefined or control characters</li>
|
||||
* </ol>
|
||||
* </p>
|
||||
*
|
||||
* @param context The package's context.
|
||||
* @param keyboard The keyboard on which the key resides.
|
||||
* @param key The key from which to obtain a description.
|
||||
* @return a character sequence describing the action performed by pressing
|
||||
* the key
|
||||
*/
|
||||
private CharSequence getDescriptionForKeyCode(Context context, Keyboard keyboard, Key key) {
|
||||
final int code = getCorrectKeyCode(keyboard, key);
|
||||
|
||||
if (keyboard.isShiftLocked() && mShiftLockedKeyCodeMap.containsKey(code)) {
|
||||
return context.getString(mShiftLockedKeyCodeMap.get(code));
|
||||
} else if (keyboard.isShiftedOrShiftLocked() && mShiftedKeyCodeMap.containsKey(code)) {
|
||||
return context.getString(mShiftedKeyCodeMap.get(code));
|
||||
} else if (mKeyCodeMap.containsKey(code)) {
|
||||
return context.getString(mKeyCodeMap.get(code));
|
||||
} else if (Character.isDefined(code) && !Character.isISOControl(code)) {
|
||||
return Character.toString((char) code);
|
||||
} else {
|
||||
return context.getString(R.string.spoken_description_unknown, code);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* 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.compat;
|
||||
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
public class AccessibilityEventCompatUtils {
|
||||
public static final int TYPE_VIEW_HOVER_ENTER = 0x80;
|
||||
public static final int TYPE_VIEW_HOVER_EXIT = 0x100;
|
||||
|
||||
private static final Field FIELD_TYPE_VIEW_HOVER_ENTER = CompatUtils.getField(
|
||||
AccessibilityEvent.class, "TYPE_VIEW_HOVER_ENTER");
|
||||
private static final Field FIELD_TYPE_VIEW_HOVER_EXIT = CompatUtils.getField(
|
||||
AccessibilityEvent.class, "TYPE_VIEW_HOVER_EXIT");
|
||||
private static final Integer OBJ_TYPE_VIEW_HOVER_ENTER = (Integer) CompatUtils
|
||||
.getFieldValue(null, null, FIELD_TYPE_VIEW_HOVER_ENTER);
|
||||
private static final Integer OBJ_TYPE_VIEW_HOVER_EXIT = (Integer) CompatUtils
|
||||
.getFieldValue(null, null, FIELD_TYPE_VIEW_HOVER_EXIT);
|
||||
|
||||
public static boolean supportsTouchExploration() {
|
||||
return OBJ_TYPE_VIEW_HOVER_ENTER != null && OBJ_TYPE_VIEW_HOVER_EXIT != null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* 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.compat;
|
||||
|
||||
import android.accessibilityservice.AccessibilityServiceInfo;
|
||||
import android.view.accessibility.AccessibilityManager;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class AccessibilityManagerCompatWrapper {
|
||||
private static final Method METHOD_getEnabledAccessibilityServiceList = CompatUtils.getMethod(
|
||||
AccessibilityManager.class, "getEnabledAccessibilityServiceList", int.class);
|
||||
|
||||
private final AccessibilityManager mManager;
|
||||
|
||||
public AccessibilityManagerCompatWrapper(AccessibilityManager manager) {
|
||||
mManager = manager;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(int feedbackType) {
|
||||
return (List<AccessibilityServiceInfo>) CompatUtils.invoke(mManager,
|
||||
Collections.<AccessibilityServiceInfo>emptyList(),
|
||||
METHOD_getEnabledAccessibilityServiceList, feedbackType);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* 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.compat;
|
||||
|
||||
public class MotionEventCompatUtils {
|
||||
public static final int ACTION_HOVER_MOVE = 0x7;
|
||||
public static final int ACTION_HOVER_ENTER = 0x9;
|
||||
public static final int ACTION_HOVER_EXIT = 0xA;
|
||||
}
|
|
@ -120,13 +120,17 @@ public class KeyboardId {
|
|||
}
|
||||
|
||||
public boolean isSymbolsKeyboard() {
|
||||
return mXmlId == R.xml.kbd_symbols;
|
||||
return mXmlId == R.xml.kbd_symbols || mXmlId == R.xml.kbd_symbols_shift;
|
||||
}
|
||||
|
||||
public boolean isPhoneKeyboard() {
|
||||
return mMode == MODE_PHONE;
|
||||
}
|
||||
|
||||
public boolean isPhoneSymbolsKeyboard() {
|
||||
return mXmlId == R.xml.kbd_phone_symbols;
|
||||
}
|
||||
|
||||
public boolean isNumberKeyboard() {
|
||||
return mMode == MODE_NUMBER;
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ import android.view.LayoutInflater;
|
|||
import android.view.View;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
|
||||
import com.android.inputmethod.accessibility.AccessibleKeyboardViewProxy;
|
||||
import com.android.inputmethod.compat.InputMethodManagerCompatWrapper;
|
||||
import com.android.inputmethod.keyboard.internal.Key;
|
||||
import com.android.inputmethod.keyboard.internal.ModifierKeyState;
|
||||
|
@ -759,6 +760,11 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
|
|||
|
||||
mKeyboardView = (LatinKeyboardView) mCurrentInputView.findViewById(R.id.keyboard_view);
|
||||
mKeyboardView.setOnKeyboardActionListener(mInputMethodService);
|
||||
|
||||
// This always needs to be set since the accessibility state can
|
||||
// potentially change without the input view being re-created.
|
||||
AccessibleKeyboardViewProxy.setView(mKeyboardView);
|
||||
|
||||
return mCurrentInputView;
|
||||
}
|
||||
|
||||
|
|
|
@ -42,9 +42,12 @@ import android.view.View;
|
|||
import android.view.ViewConfiguration;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewGroup.MarginLayoutParams;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
import android.widget.PopupWindow;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.android.inputmethod.accessibility.AccessibilityUtils;
|
||||
import com.android.inputmethod.accessibility.AccessibleKeyboardViewProxy;
|
||||
import com.android.inputmethod.compat.FrameLayoutCompatUtils;
|
||||
import com.android.inputmethod.keyboard.internal.Key;
|
||||
import com.android.inputmethod.keyboard.internal.MiniKeyboardBuilder;
|
||||
|
@ -1325,4 +1328,37 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
|
|||
public boolean handleBack() {
|
||||
return dismissMiniKeyboard();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dispatchTouchEvent(MotionEvent event) {
|
||||
if (AccessibilityUtils.getInstance().isTouchExplorationEnabled()) {
|
||||
return AccessibleKeyboardViewProxy.getInstance().dispatchTouchEvent(event)
|
||||
|| super.dispatchTouchEvent(event);
|
||||
}
|
||||
|
||||
return super.dispatchTouchEvent(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
|
||||
if (AccessibilityUtils.getInstance().isTouchExplorationEnabled()) {
|
||||
final PointerTracker tracker = getPointerTracker(0);
|
||||
return AccessibleKeyboardViewProxy.getInstance().dispatchPopulateAccessibilityEvent(
|
||||
event, tracker) || super.dispatchPopulateAccessibilityEvent(event);
|
||||
}
|
||||
|
||||
return super.dispatchPopulateAccessibilityEvent(event);
|
||||
}
|
||||
|
||||
public boolean onHoverEvent(MotionEvent event) {
|
||||
// Since reflection doesn't support calling superclass methods, this
|
||||
// method checks for the existence of onHoverEvent() in the View class
|
||||
// before returning a value.
|
||||
if (AccessibilityUtils.getInstance().isTouchExplorationEnabled()) {
|
||||
final PointerTracker tracker = getPointerTracker(0);
|
||||
return AccessibleKeyboardViewProxy.getInstance().onHoverEvent(event, tracker);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,6 +53,7 @@ import android.view.inputmethod.EditorInfo;
|
|||
import android.view.inputmethod.ExtractedText;
|
||||
import android.view.inputmethod.InputConnection;
|
||||
|
||||
import com.android.inputmethod.accessibility.AccessibilityUtils;
|
||||
import com.android.inputmethod.compat.CompatUtils;
|
||||
import com.android.inputmethod.compat.EditorInfoCompatUtils;
|
||||
import com.android.inputmethod.compat.InputConnectionCompatUtils;
|
||||
|
@ -353,6 +354,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
|
|||
SubtypeSwitcher.init(this, prefs);
|
||||
KeyboardSwitcher.init(this, prefs);
|
||||
Recorrection.init(this, prefs);
|
||||
AccessibilityUtils.init(this, prefs);
|
||||
|
||||
super.onCreate();
|
||||
|
||||
|
|
Loading…
Reference in New Issue