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