PageRenderTime 14ms CodeModel.GetById 2ms app.highlight 9ms RepoModel.GetById 1ms app.codeStats 0ms

/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
 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}