AccessibilityUtils.java revision 282adf733093b41a31514746825ea05fc90fb3ee
15ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette/* 25ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette * Copyright (C) 2011 The Android Open Source Project 35ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette * 45ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette * Licensed under the Apache License, Version 2.0 (the "License"); 55ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette * you may not use this file except in compliance with the License. 65ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette * You may obtain a copy of the License at 75ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette * 85ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette * http://www.apache.org/licenses/LICENSE-2.0 95ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette * 105ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette * Unless required by applicable law or agreed to in writing, software 115ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette * distributed under the License is distributed on an "AS IS" BASIS, 125ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 135ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette * See the License for the specific language governing permissions and 145ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette * limitations under the License. 155ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette */ 165ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 175ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverettepackage com.android.inputmethod.accessibility; 185ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 195ac4638f999db4fea8a9e24171dbceb640a10858Alan Viveretteimport android.content.Context; 205ac4638f999db4fea8a9e24171dbceb640a10858Alan Viveretteimport android.inputmethodservice.InputMethodService; 21b0c8db018d53b103dcb4b699be27a4e1a2c2f92cAlan Viveretteimport android.media.AudioManager; 225ac4638f999db4fea8a9e24171dbceb640a10858Alan Viveretteimport android.os.SystemClock; 23c960695f38ae0564dff3a6897fd1843c8e74c604Alan Viveretteimport android.provider.Settings; 245ac4638f999db4fea8a9e24171dbceb640a10858Alan Viveretteimport android.util.Log; 255ac4638f999db4fea8a9e24171dbceb640a10858Alan Viveretteimport android.view.MotionEvent; 265ac4638f999db4fea8a9e24171dbceb640a10858Alan Viveretteimport android.view.accessibility.AccessibilityEvent; 275ac4638f999db4fea8a9e24171dbceb640a10858Alan Viveretteimport android.view.accessibility.AccessibilityManager; 28b0c8db018d53b103dcb4b699be27a4e1a2c2f92cAlan Viveretteimport android.view.inputmethod.EditorInfo; 295ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 30b0c8db018d53b103dcb4b699be27a4e1a2c2f92cAlan Viveretteimport com.android.inputmethod.compat.AudioManagerCompatWrapper; 31c960695f38ae0564dff3a6897fd1843c8e74c604Alan Viveretteimport com.android.inputmethod.compat.SettingsSecureCompatUtils; 32be55086fd9218bc03ee0ccac1052d96b40d8a979Tadashi G. Takaokaimport com.android.inputmethod.latin.InputTypeUtils; 33b0c8db018d53b103dcb4b699be27a4e1a2c2f92cAlan Viveretteimport com.android.inputmethod.latin.R; 345ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 355ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverettepublic class AccessibilityUtils { 365ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette private static final String TAG = AccessibilityUtils.class.getSimpleName(); 375ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette private static final String CLASS = AccessibilityUtils.class.getClass().getName(); 385ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette private static final String PACKAGE = AccessibilityUtils.class.getClass().getPackage() 395ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette .getName(); 405ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 415ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette private static final AccessibilityUtils sInstance = new AccessibilityUtils(); 425ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 43b0c8db018d53b103dcb4b699be27a4e1a2c2f92cAlan Viverette private Context mContext; 445ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette private AccessibilityManager mAccessibilityManager; 45b0c8db018d53b103dcb4b699be27a4e1a2c2f92cAlan Viverette private AudioManagerCompatWrapper mAudioManager; 465ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 475ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette /* 485ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette * Setting this constant to {@code false} will disable all keyboard 495ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette * accessibility code, regardless of whether Accessibility is turned on in 505ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette * the system settings. It should ONLY be used in the event of an emergency. 515ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette */ 525ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette private static final boolean ENABLE_ACCESSIBILITY = true; 535ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 542ac5988f84b5c38d313951a3d7faddebf5f25e04Tadashi G. Takaoka public static void init(InputMethodService inputMethod) { 555ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette if (!ENABLE_ACCESSIBILITY) 565ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette return; 575ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 585ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette // These only need to be initialized if the kill switch is off. 592ac5988f84b5c38d313951a3d7faddebf5f25e04Tadashi G. Takaoka sInstance.initInternal(inputMethod); 602ac5988f84b5c38d313951a3d7faddebf5f25e04Tadashi G. Takaoka KeyCodeDescriptionMapper.init(); 612ac5988f84b5c38d313951a3d7faddebf5f25e04Tadashi G. Takaoka AccessibleKeyboardViewProxy.init(inputMethod); 625ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette } 635ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 645ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette public static AccessibilityUtils getInstance() { 655ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette return sInstance; 665ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette } 675ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 685ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette private AccessibilityUtils() { 695ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette // This class is not publicly instantiable. 705ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette } 715ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 722ac5988f84b5c38d313951a3d7faddebf5f25e04Tadashi G. Takaoka private void initInternal(Context context) { 73b0c8db018d53b103dcb4b699be27a4e1a2c2f92cAlan Viverette mContext = context; 745ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette mAccessibilityManager = (AccessibilityManager) context 755ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette .getSystemService(Context.ACCESSIBILITY_SERVICE); 76b0c8db018d53b103dcb4b699be27a4e1a2c2f92cAlan Viverette 77b0c8db018d53b103dcb4b699be27a4e1a2c2f92cAlan Viverette final AudioManager audioManager = (AudioManager) context 78b0c8db018d53b103dcb4b699be27a4e1a2c2f92cAlan Viverette .getSystemService(Context.AUDIO_SERVICE); 79b0c8db018d53b103dcb4b699be27a4e1a2c2f92cAlan Viverette mAudioManager = new AudioManagerCompatWrapper(audioManager); 805ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette } 815ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 825ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette /** 835ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette * Returns {@code true} if touch exploration is enabled. Currently, this 845ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette * means that the kill switch is off, the device supports touch exploration, 855ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette * and a spoken feedback service is turned on. 865ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette * 875ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette * @return {@code true} if touch exploration is enabled. 885ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette */ 895ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette public boolean isTouchExplorationEnabled() { 905ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette return ENABLE_ACCESSIBILITY 915ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette && mAccessibilityManager.isEnabled() 92c6435f92a80c6664870f9d1a4bb2a1c5153ef2c3Tadashi G. Takaoka && mAccessibilityManager.isTouchExplorationEnabled(); 935ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette } 945ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 955ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette /** 965ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette * Returns {@true} if the provided event is a touch exploration (e.g. hover) 975ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette * event. This is used to determine whether the event should be processed by 985ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette * the touch exploration code within the keyboard. 995ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette * 1005ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette * @param event The event to check. 1015ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette * @return {@true} is the event is a touch exploration event 1025ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette */ 1035ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette public boolean isTouchExplorationEvent(MotionEvent event) { 1045ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette final int action = event.getAction(); 1055ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 106c6435f92a80c6664870f9d1a4bb2a1c5153ef2c3Tadashi G. Takaoka return action == MotionEvent.ACTION_HOVER_ENTER 107c6435f92a80c6664870f9d1a4bb2a1c5153ef2c3Tadashi G. Takaoka || action == MotionEvent.ACTION_HOVER_EXIT 108c6435f92a80c6664870f9d1a4bb2a1c5153ef2c3Tadashi G. Takaoka || action == MotionEvent.ACTION_HOVER_MOVE; 1095ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette } 1105ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 1115ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette /** 112c960695f38ae0564dff3a6897fd1843c8e74c604Alan Viverette * Returns whether the device should obscure typed password characters. 113c960695f38ae0564dff3a6897fd1843c8e74c604Alan Viverette * Typically this means speaking "dot" in place of non-control characters. 1149a81ce92c381007affe6bb2310bf94c9856eaae1alanv * 115c960695f38ae0564dff3a6897fd1843c8e74c604Alan Viverette * @return {@code true} if the device should obscure password characters. 116b0c8db018d53b103dcb4b699be27a4e1a2c2f92cAlan Viverette */ 117e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka public boolean shouldObscureInput(EditorInfo editorInfo) { 118e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka if (editorInfo == null) 119b0c8db018d53b103dcb4b699be27a4e1a2c2f92cAlan Viverette return false; 120b0c8db018d53b103dcb4b699be27a4e1a2c2f92cAlan Viverette 121c960695f38ae0564dff3a6897fd1843c8e74c604Alan Viverette // The user can optionally force speaking passwords. 122c960695f38ae0564dff3a6897fd1843c8e74c604Alan Viverette if (SettingsSecureCompatUtils.ACCESSIBILITY_SPEAK_PASSWORD != null) { 123c960695f38ae0564dff3a6897fd1843c8e74c604Alan Viverette final boolean speakPassword = Settings.Secure.getInt(mContext.getContentResolver(), 124c960695f38ae0564dff3a6897fd1843c8e74c604Alan Viverette SettingsSecureCompatUtils.ACCESSIBILITY_SPEAK_PASSWORD, 0) != 0; 125c960695f38ae0564dff3a6897fd1843c8e74c604Alan Viverette if (speakPassword) 126c960695f38ae0564dff3a6897fd1843c8e74c604Alan Viverette return false; 127c960695f38ae0564dff3a6897fd1843c8e74c604Alan Viverette } 128c960695f38ae0564dff3a6897fd1843c8e74c604Alan Viverette 129b0c8db018d53b103dcb4b699be27a4e1a2c2f92cAlan Viverette // Always speak if the user is listening through headphones. 130b0c8db018d53b103dcb4b699be27a4e1a2c2f92cAlan Viverette if (mAudioManager.isWiredHeadsetOn() || mAudioManager.isBluetoothA2dpOn()) 131b0c8db018d53b103dcb4b699be27a4e1a2c2f92cAlan Viverette return false; 132b0c8db018d53b103dcb4b699be27a4e1a2c2f92cAlan Viverette 133b0c8db018d53b103dcb4b699be27a4e1a2c2f92cAlan Viverette // Don't speak if the IME is connected to a password field. 134be55086fd9218bc03ee0ccac1052d96b40d8a979Tadashi G. Takaoka return InputTypeUtils.isPasswordInputType(editorInfo.inputType); 135b0c8db018d53b103dcb4b699be27a4e1a2c2f92cAlan Viverette } 136b0c8db018d53b103dcb4b699be27a4e1a2c2f92cAlan Viverette 137b0c8db018d53b103dcb4b699be27a4e1a2c2f92cAlan Viverette /** 1385ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette * Sends the specified text to the {@link AccessibilityManager} to be 1395ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette * spoken. 1405ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette * 1415ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette * @param text the text to speak 1425ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette */ 1435ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette public void speak(CharSequence text) { 1445ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette if (!mAccessibilityManager.isEnabled()) { 1455ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette Log.e(TAG, "Attempted to speak when accessibility was disabled!"); 1465ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette return; 1475ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette } 1485ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 1495ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette // The following is a hack to avoid using the heavy-weight TextToSpeech 1505ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette // class. Instead, we're just forcing a fake AccessibilityEvent into 1515ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette // the screen reader to make it speak. 1525ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette final AccessibilityEvent event = AccessibilityEvent 153b0c8db018d53b103dcb4b699be27a4e1a2c2f92cAlan Viverette .obtain(AccessibilityEvent.TYPE_VIEW_FOCUSED); 1545ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 1555ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette event.setPackageName(PACKAGE); 1565ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette event.setClassName(CLASS); 1575ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette event.setEventTime(SystemClock.uptimeMillis()); 1585ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette event.setEnabled(true); 1595ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette event.getText().add(text); 1605ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette 1615ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette mAccessibilityManager.sendAccessibilityEvent(event); 1625ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette } 163b0c8db018d53b103dcb4b699be27a4e1a2c2f92cAlan Viverette 164b0c8db018d53b103dcb4b699be27a4e1a2c2f92cAlan Viverette /** 165b0c8db018d53b103dcb4b699be27a4e1a2c2f92cAlan Viverette * Handles speaking the "connect a headset to hear passwords" notification 166b0c8db018d53b103dcb4b699be27a4e1a2c2f92cAlan Viverette * when connecting to a password field. 167b0c8db018d53b103dcb4b699be27a4e1a2c2f92cAlan Viverette * 168e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka * @param editorInfo The input connection's editor info attribute. 169b0c8db018d53b103dcb4b699be27a4e1a2c2f92cAlan Viverette * @param restarting Whether the connection is being restarted. 170b0c8db018d53b103dcb4b699be27a4e1a2c2f92cAlan Viverette */ 171e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka public void onStartInputViewInternal(EditorInfo editorInfo, boolean restarting) { 172e7eac906c0a14b644d457beeb73a407fa1b63673Tadashi G. Takaoka if (shouldObscureInput(editorInfo)) { 173b0c8db018d53b103dcb4b699be27a4e1a2c2f92cAlan Viverette final CharSequence text = mContext.getText(R.string.spoken_use_headphones); 174b0c8db018d53b103dcb4b699be27a4e1a2c2f92cAlan Viverette speak(text); 175b0c8db018d53b103dcb4b699be27a4e1a2c2f92cAlan Viverette } 176b0c8db018d53b103dcb4b699be27a4e1a2c2f92cAlan Viverette } 177282adf733093b41a31514746825ea05fc90fb3eealanv 178282adf733093b41a31514746825ea05fc90fb3eealanv /** 179282adf733093b41a31514746825ea05fc90fb3eealanv * Sends the specified {@link AccessibilityEvent} if accessibility is 180282adf733093b41a31514746825ea05fc90fb3eealanv * enabled. No operation if accessibility is disabled. 181282adf733093b41a31514746825ea05fc90fb3eealanv * 182282adf733093b41a31514746825ea05fc90fb3eealanv * @param event The event to send. 183282adf733093b41a31514746825ea05fc90fb3eealanv */ 184282adf733093b41a31514746825ea05fc90fb3eealanv public void requestSendAccessibilityEvent(AccessibilityEvent event) { 185282adf733093b41a31514746825ea05fc90fb3eealanv if (mAccessibilityManager.isEnabled()) { 186282adf733093b41a31514746825ea05fc90fb3eealanv mAccessibilityManager.sendAccessibilityEvent(event); 187282adf733093b41a31514746825ea05fc90fb3eealanv } 188282adf733093b41a31514746825ea05fc90fb3eealanv } 1895ac4638f999db4fea8a9e24171dbceb640a10858Alan Viverette} 190