/ime/aimelib/src/com/google/android/marvin/aime/usercommands/UserCommandHandler.java

http://eyes-free.googlecode.com/ · Java · 201 lines · 99 code · 29 blank · 73 comment · 14 complexity · 73b48a4987ab90d8c8c470a504f3a938 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.google.android.marvin.aime.usercommands;
  17. import com.google.android.marvin.commands.Command;
  18. import com.google.android.marvin.commands.CommandConstants;
  19. import com.google.android.marvin.commands.CommandsManager;
  20. import android.content.BroadcastReceiver;
  21. import android.content.Context;
  22. import android.content.Intent;
  23. import android.content.IntentFilter;
  24. import android.util.Log;
  25. import android.view.KeyEvent;
  26. import java.util.LinkedList;
  27. import java.util.List;
  28. /**
  29. * @author clsimon@google.com (Cheryl Simon)
  30. * @author alanv@google.com (Alan Viverette)
  31. */
  32. public class UserCommandHandler {
  33. private static final String TAG = "UserCommandHandler";
  34. private final Context mContext;
  35. private final CommandsManager mManager;
  36. /** Tracks the {@link KeyEvent}s for which we received a keyDown event. */
  37. private final LinkedList<KeyEvent> mCurrentKeyDownEvents;
  38. /** Contains the list of available keyboard shortcuts and actions. */
  39. private final LinkedList<Command> mAvailableCommands;
  40. /** Receives broadcast updates. */
  41. private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
  42. @Override
  43. public void onReceive(Context receiveContext, Intent intent) {
  44. String action = intent.getAction();
  45. if (CommandsManager.COMMAND_UPDATE_ACTION.equals(action)) {
  46. updateAvailableCommands();
  47. }
  48. }
  49. };
  50. /**
  51. * If greater than 0, means a command was handled in this key cycle, and we
  52. * need to ignore the next {@code mCommandHandled} key up events.
  53. */
  54. private int mCommandHandled = 0;
  55. /**
  56. * Set to {@code} true when the Menu key is down.
  57. */
  58. private boolean mModifierKeyDown = false;
  59. /**
  60. * Constructs a new user command handler using the specified parent context.
  61. *
  62. * @param context The parent context.
  63. */
  64. public UserCommandHandler(Context context) {
  65. mContext = context;
  66. mManager = new CommandsManager();
  67. mCurrentKeyDownEvents = new LinkedList<KeyEvent>();
  68. mAvailableCommands = new LinkedList<Command>();
  69. IntentFilter intentFilter = new IntentFilter(CommandsManager.COMMAND_UPDATE_ACTION);
  70. mContext.registerReceiver(mReceiver, intentFilter);
  71. updateAvailableCommands();
  72. }
  73. /**
  74. * Releases resources associated with this user command handler. You should
  75. * call this in in your activity's onDestroy() method.
  76. */
  77. public void release() {
  78. mContext.unregisterReceiver(mReceiver);
  79. }
  80. /**
  81. * Returns whether a key code is a valid modifier for user commands.
  82. *
  83. * @param keyCode The key code.
  84. * @return {@code true} if the key code is a valid modifier
  85. */
  86. private boolean isModifierKey(int keyCode) {
  87. switch (keyCode) {
  88. case KeyEvent.KEYCODE_SEARCH:
  89. case KeyEvent.KEYCODE_MENU:
  90. return true;
  91. default:
  92. return false;
  93. }
  94. }
  95. /**
  96. * Attempts to handle a key down event. Returns {@code true} if the event is
  97. * captured.
  98. *
  99. * @param ev The key event to handle.
  100. * @return {@code true} if the key is captured.
  101. */
  102. public boolean onKeyDown(KeyEvent ev) {
  103. if (isModifierKey(ev.getKeyCode())) {
  104. mModifierKeyDown = true;
  105. } else if (!mModifierKeyDown) {
  106. return false;
  107. }
  108. mCurrentKeyDownEvents.add(ev);
  109. // TODO Figure out a way to do this that doesn't involve looping through
  110. // evey single command every single time a key is pressed.
  111. for (Command command : mAvailableCommands) {
  112. if (matchesKeyEvents(command, mCurrentKeyDownEvents)) {
  113. Intent intent = new Intent(command.getAction());
  114. intent.putExtra(CommandConstants.EXTRA_COMMAND_NAME, command.getDisplayName());
  115. mContext.sendBroadcast(intent);
  116. mCommandHandled = mCurrentKeyDownEvents.size();
  117. return true;
  118. }
  119. }
  120. return false;
  121. }
  122. /**
  123. * Attempts to handle a key up event. Returns <code>true</code> if the event
  124. * is captured.
  125. *
  126. * @param ev The key event to handle.
  127. * @return {@code true} if the key is captured.
  128. */
  129. public boolean onKeyUp(KeyEvent ev) {
  130. if (isModifierKey(ev.getKeyCode())) {
  131. mModifierKeyDown = false;
  132. }
  133. // If we want to do transformations on text being entered with a hard
  134. // keyboard, we need to process the up events to update the meta key
  135. // state we are tracking.
  136. mCurrentKeyDownEvents.clear();
  137. // If we handled a command, we want to ignore the key ups of all of the
  138. // events so we don't show a menu or search for something.
  139. if (mCommandHandled > 0) {
  140. mCommandHandled--;
  141. return true;
  142. }
  143. return false;
  144. }
  145. /**
  146. * Updates the list of available commands.
  147. */
  148. private void updateAvailableCommands() {
  149. Log.i(TAG, "Updating available commands...");
  150. mAvailableCommands.clear();
  151. mAvailableCommands.addAll(mManager.getAvailableCommands(mContext));
  152. }
  153. /**
  154. * @return {@code true} if the command matches the provided {@code event}.
  155. */
  156. private boolean matchesKeyEvents(Command command, List<KeyEvent> events) {
  157. if (events.size() < 2) {
  158. return false;
  159. }
  160. // look for 2 events, assume first event is modifier, last event is
  161. // second key.
  162. KeyEvent modifierEvent = events.get(0);
  163. if (modifierEvent.getKeyCode() != command.getModifier()) {
  164. return false;
  165. }
  166. KeyEvent keyEvent = events.get(events.size() - 1);
  167. if (keyEvent.getKeyCode() != command.getKeyCode()) {
  168. return false;
  169. }
  170. return true;
  171. }
  172. }