/ime/latinime/src/com/googlecode/eyesfree/inputmethod/PersistentInputMethodService.java

http://eyes-free.googlecode.com/ · Java · 230 lines · 162 code · 32 blank · 36 comment · 29 complexity · fe8e9c81bcbe0df8d5ad94fc2bff2831 MD5 · raw file

  1. /*
  2. * Copyright (C) 2011 Google Inc.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of 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,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package com.googlecode.eyesfree.inputmethod;
  17. import android.content.res.Configuration;
  18. import android.os.Handler;
  19. import android.os.Message;
  20. import android.util.Log;
  21. import android.view.KeyEvent;
  22. import android.view.ViewConfiguration;
  23. import android.view.WindowManager;
  24. import android.view.inputmethod.EditorInfo;
  25. import android.view.inputmethod.InputConnection;
  26. import com.google.android.marvin.aime.AccessibleInputMethodService;
  27. /**
  28. * This class extends {@link AccessibleInputMethodService} by overriding methods
  29. * related to hiding the input window. It allows the input method to be forced
  30. * open by long-pressing the MENU key.
  31. *
  32. * @author alanv@google.com (Alan Viverette)
  33. */
  34. public class PersistentInputMethodService extends LinearNavigationInputMethodService {
  35. private static final String TAG = "PersistentInputMethodService";
  36. private static final int LONGPRESS_TIMEOUT = ViewConfiguration.getLongPressTimeout();
  37. private static final int TAP_TIMEOUT = ViewConfiguration.getTapTimeout();
  38. /** Constant used for LongPressHandler. */
  39. private static final int LONG_PRESS = 1;
  40. private LongPressHandler mLongPressHandler;
  41. /**
  42. * Avoids handling repeated key presses. This is set after the first down
  43. * event in a series of down events, then cleared on an up event.
  44. */
  45. private KeyEvent mDownEvent;
  46. private boolean mInLongPress;
  47. private class LongPressHandler extends Handler {
  48. LongPressHandler() {
  49. super();
  50. }
  51. @Override
  52. public void handleMessage(Message msg) {
  53. switch (msg.what) {
  54. case LONG_PRESS:
  55. mInLongPress = true;
  56. onKeyLongPress(msg.arg1, (KeyEvent) msg.obj);
  57. break;
  58. }
  59. }
  60. }
  61. @Override
  62. public void onCreate() {
  63. super.onCreate();
  64. mLongPressHandler = new LongPressHandler();
  65. }
  66. @Override
  67. public void onConfigurationChanged(Configuration newConfig) {
  68. super.onConfigurationChanged(newConfig);
  69. if (!isAccessibilityEnabled()) {
  70. return;
  71. }
  72. // Ensure that we force the view open when accessibility is enabled, but
  73. // only if we've already been attached to a window.
  74. if (getWindow().getWindow().getAttributes().token != null) {
  75. try {
  76. showWindow(true);
  77. } catch (RuntimeException e) {
  78. e.printStackTrace();
  79. }
  80. }
  81. }
  82. @Override
  83. protected void onAccessibilityChanged(boolean accessibilityEnabled) {
  84. if (!accessibilityEnabled) {
  85. // This is the only correct way to hide the window. Don't use
  86. // hideWindow() or requestHideSelf() because they'll mess up the
  87. // internal state.
  88. onCreateInputMethodInterface().hideSoftInput(0, null);
  89. }
  90. }
  91. @Override
  92. public boolean onKeyDown(int keyCode, KeyEvent event) {
  93. if (!isAccessibilityEnabled()) {
  94. return super.onKeyDown(keyCode, event);
  95. }
  96. switch (keyCode) {
  97. case KeyEvent.KEYCODE_BACK:
  98. return false;
  99. case KeyEvent.KEYCODE_VOLUME_UP:
  100. case KeyEvent.KEYCODE_VOLUME_DOWN: {
  101. event.startTracking();
  102. if (mDownEvent == null) {
  103. mDownEvent = event;
  104. mInLongPress = false;
  105. mLongPressHandler.removeMessages(LONG_PRESS);
  106. mLongPressHandler.sendMessageDelayed(
  107. mLongPressHandler.obtainMessage(LONG_PRESS, keyCode, 0, event),
  108. TAP_TIMEOUT + LONGPRESS_TIMEOUT);
  109. }
  110. return true;
  111. }
  112. }
  113. return super.onKeyDown(keyCode, event);
  114. }
  115. @Override
  116. public boolean onKeyUp(int keyCode, KeyEvent event) {
  117. if (!isAccessibilityEnabled()) {
  118. return super.onKeyUp(keyCode, event);
  119. }
  120. final KeyEvent downEvent = mDownEvent;
  121. mDownEvent = null;
  122. // Give the parent class a chance to consume the cached down
  123. // event, then send it through the input connection.
  124. if (downEvent != null && !mInLongPress) {
  125. final InputConnection input = getCurrentInputConnection();
  126. if (!super.onKeyDown(downEvent.getKeyCode(), downEvent) && input != null) {
  127. input.sendKeyEvent(downEvent);
  128. }
  129. }
  130. switch (keyCode) {
  131. case KeyEvent.KEYCODE_BACK:
  132. return false;
  133. case KeyEvent.KEYCODE_VOLUME_UP:
  134. case KeyEvent.KEYCODE_VOLUME_DOWN: {
  135. mLongPressHandler.removeMessages(LONG_PRESS);
  136. if (mInLongPress) {
  137. mInLongPress = false;
  138. return true;
  139. } else {
  140. return super.onKeyUp(keyCode, event);
  141. }
  142. }
  143. }
  144. return super.onKeyUp(keyCode, event);
  145. }
  146. @Override
  147. public void onStartInput(EditorInfo attribute, boolean restarting) {
  148. super.onStartInput(attribute, restarting);
  149. if (!isAccessibilityEnabled()) {
  150. return;
  151. }
  152. // Restart input view when changing input.
  153. onStartInputView(attribute, restarting);
  154. }
  155. @Override
  156. public void hideWindow() {
  157. if (!isAccessibilityEnabled()) {
  158. super.hideWindow();
  159. }
  160. }
  161. @Override
  162. public void showWindow(boolean showInput) {
  163. if (!isAccessibilityEnabled()) {
  164. super.showWindow(showInput);
  165. } else {
  166. try {
  167. super.showWindow(true);
  168. } catch (WindowManager.BadTokenException e) {
  169. Log.e(TAG, "Failed to show window", e);
  170. }
  171. }
  172. }
  173. @Override
  174. public boolean onShowInputRequested(int flags, boolean configChange) {
  175. if (!isAccessibilityEnabled()) {
  176. return super.onShowInputRequested(flags, configChange);
  177. } else {
  178. return true;
  179. }
  180. }
  181. @Override
  182. public boolean onEvaluateInputViewShown() {
  183. if (!isAccessibilityEnabled()) {
  184. return super.onEvaluateInputViewShown();
  185. } else {
  186. return true;
  187. }
  188. }
  189. @Override
  190. public boolean onEvaluateFullscreenMode() {
  191. if (!isAccessibilityEnabled()) {
  192. return super.onEvaluateFullscreenMode();
  193. } else {
  194. // This input method should never show an extract view.
  195. return false;
  196. }
  197. }
  198. }