diff options
Diffstat (limited to 'core/java/android/view/KeyEvent.java')
-rw-r--r-- | core/java/android/view/KeyEvent.java | 267 |
1 files changed, 263 insertions, 4 deletions
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java index 6349288..d4f9787 100644 --- a/core/java/android/view/KeyEvent.java +++ b/core/java/android/view/KeyEvent.java @@ -18,6 +18,8 @@ package android.view; import android.os.Parcel; import android.os.Parcelable; +import android.util.Log; +import android.util.SparseIntArray; import android.view.KeyCharacterMap; import android.view.KeyCharacterMap.KeyData; @@ -258,6 +260,51 @@ public class KeyEvent implements Parcelable { public static final int FLAG_EDITOR_ACTION = 0x10; /** + * When associated with up key events, this indicates that the key press + * has been canceled. Typically this is used with virtual touch screen + * keys, where the user can slide from the virtual key area on to the + * display: in that case, the application will receive a canceled up + * event and should not perform the action normally associated with the + * key. Note that for this to work, the application can not perform an + * action for a key until it receives an up or the long press timeout has + * expired. + */ + public static final int FLAG_CANCELED = 0x20; + + /** + * This key event was generated by a virtual (on-screen) hard key area. + * Typically this is an area of the touchscreen, outside of the regular + * display, dedicated to "hardware" buttons. + */ + public static final int FLAG_VIRTUAL_HARD_KEY = 0x40; + + /** + * This flag is set for the first key repeat that occurs after the + * long press timeout. + */ + public static final int FLAG_LONG_PRESS = 0x80; + + /** + * Set when a key event has {@link #FLAG_CANCELED} set because a long + * press action was executed while it was down. + */ + public static final int FLAG_CANCELED_LONG_PRESS = 0x100; + + /** + * Set for {@link #ACTION_UP} when this event's key code is still being + * tracked from its initial down. That is, somebody requested that tracking + * started on the key down and a long press has not caused + * the tracking to be canceled. + */ + public static final int FLAG_TRACKING = 0x200; + + /** + * Private control to determine when an app is tracking a key sequence. + * @hide + */ + public static final int FLAG_START_TRACKING = 0x40000000; + + /** * Returns the maximum keycode. */ public static int getMaxKeyCode() { @@ -273,6 +320,9 @@ public class KeyEvent implements Parcelable { return KeyCharacterMap.getDeadChar(accent, c); } + static final boolean DEBUG = false; + static final String TAG = "KeyEvent"; + private int mMetaState; private int mAction; private int mKeyCode; @@ -286,7 +336,11 @@ public class KeyEvent implements Parcelable { public interface Callback { /** - * Called when a key down event has occurred. + * Called when a key down event has occurred. If you return true, + * you can first call {@link KeyEvent#startTracking() + * KeyEvent.startTracking()} to have the framework track the event + * through its {@link #onKeyUp(int, KeyEvent)} and also call your + * {@link #onKeyLongPress(int, KeyEvent)} if it occurs. * * @param keyCode The value in event.getKeyCode(). * @param event Description of the key event. @@ -297,6 +351,22 @@ public class KeyEvent implements Parcelable { boolean onKeyDown(int keyCode, KeyEvent event); /** + * Called when a long press has occurred. If you return true, + * the final key up will have {@link KeyEvent#FLAG_CANCELED} and + * {@link KeyEvent#FLAG_CANCELED_LONG_PRESS} set. Note that in + * order to receive this callback, someone in the event change + * <em>must</em> return true from {@link #onKeyDown} <em>and</em> + * call {@link KeyEvent#startTracking()} on the event. + * + * @param keyCode The value in event.getKeyCode(). + * @param event Description of the key event. + * + * @return If you handled the event, return true. If you want to allow + * the event to be handled by the next receiver, return false. + */ + boolean onKeyLongPress(int keyCode, KeyEvent event); + + /** * Called when a key up event has occurred. * * @param keyCode The value in event.getKeyCode(). @@ -481,11 +551,15 @@ public class KeyEvent implements Parcelable { /** * Copy an existing key event, modifying its time and repeat count. * + * @deprecated Use {@link #changeTimeRepeat(KeyEvent, long, int)} + * instead. + * * @param origEvent The existing event to be copied. * @param eventTime The new event time * (in {@link android.os.SystemClock#uptimeMillis}) of the event. * @param newRepeat The new repeat count of the event. */ + @Deprecated public KeyEvent(KeyEvent origEvent, long eventTime, int newRepeat) { mDownTime = origEvent.mDownTime; mEventTime = eventTime; @@ -514,6 +588,26 @@ public class KeyEvent implements Parcelable { } /** + * Create a new key event that is the same as the given one, but whose + * event time and repeat count are replaced with the given value. + * + * @param event The existing event to be copied. This is not modified. + * @param eventTime The new event time + * (in {@link android.os.SystemClock#uptimeMillis}) of the event. + * @param newRepeat The new repeat count of the event. + * @param newFlags New flags for the event, replacing the entire value + * in the original event. + */ + public static KeyEvent changeTimeRepeat(KeyEvent event, long eventTime, + int newRepeat, int newFlags) { + KeyEvent ret = new KeyEvent(event); + ret.mEventTime = eventTime; + ret.mRepeatCount = newRepeat; + ret.mFlags = newFlags; + return ret; + } + + /** * Copy an existing key event, modifying its action. * * @param origEvent The existing event to be copied. @@ -694,6 +788,42 @@ public class KeyEvent implements Parcelable { } /** + * For {@link #ACTION_UP} events, indicates that the event has been + * canceled as per {@link #FLAG_CANCELED}. + */ + public final boolean isCanceled() { + return (mFlags&FLAG_CANCELED) != 0; + } + + /** + * Call this during {@link Callback#onKeyDown} to have the system track + * the key through its final up (possibly including a long press). Note + * that only one key can be tracked at a time -- if another key down + * event is received while a previous one is being tracked, tracking is + * stopped on the previous event. + */ + public final void startTracking() { + mFlags |= FLAG_START_TRACKING; + } + + /** + * For {@link #ACTION_UP} events, indicates that the event is still being + * tracked from its initial down event as per + * {@link #FLAG_TRACKING}. + */ + public final boolean isTracking() { + return (mFlags&FLAG_TRACKING) != 0; + } + + /** + * For {@link #ACTION_DOWN} events, indicates that the event has been + * canceled as per {@link #FLAG_LONG_PRESS}. + */ + public final boolean isLongPress() { + return (mFlags&FLAG_LONG_PRESS) != 0; + } + + /** * Retrieve the key code of the key event. This is the physical key that * was pressed, <em>not</em> the Unicode character. * @@ -879,19 +1009,55 @@ public class KeyEvent implements Parcelable { } /** + * @deprecated Use {@link #dispatch(Callback, DispatcherState, Object)} instead. + */ + @Deprecated + public final boolean dispatch(Callback receiver) { + return dispatch(receiver, null, null); + } + + /** * Deliver this key event to a {@link Callback} interface. If this is * an ACTION_MULTIPLE event and it is not handled, then an attempt will * be made to deliver a single normal event. * * @param receiver The Callback that will be given the event. + * @param state State information retained across events. + * @param target The target of the dispatch, for use in tracking. * * @return The return value from the Callback method that was called. */ - public final boolean dispatch(Callback receiver) { + public final boolean dispatch(Callback receiver, DispatcherState state, + Object target) { switch (mAction) { - case ACTION_DOWN: - return receiver.onKeyDown(mKeyCode, this); + case ACTION_DOWN: { + mFlags &= ~FLAG_START_TRACKING; + if (DEBUG) Log.v(TAG, "Key down to " + target + " in " + state + + ": " + this); + boolean res = receiver.onKeyDown(mKeyCode, this); + if (state != null) { + if (res && mRepeatCount == 0 && (mFlags&FLAG_START_TRACKING) != 0) { + if (DEBUG) Log.v(TAG, " Start tracking!"); + state.startTracking(this, target); + } else if (isLongPress() && state.isTracking(this)) { + try { + if (receiver.onKeyLongPress(mKeyCode, this)) { + if (DEBUG) Log.v(TAG, " Clear from long press!"); + state.performedLongPress(this); + res = true; + } + } catch (AbstractMethodError e) { + } + } + } + return res; + } case ACTION_UP: + if (DEBUG) Log.v(TAG, "Key up to " + target + " in " + state + + ": " + this); + if (state != null) { + state.handleUpEvent(this); + } return receiver.onKeyUp(mKeyCode, this); case ACTION_MULTIPLE: final int count = mRepeatCount; @@ -911,10 +1077,103 @@ public class KeyEvent implements Parcelable { mRepeatCount = count; return handled; } + return false; } return false; } + /** + * Use with {@link KeyEvent#dispatch(Callback, DispatcherState, Object)} + * for more advanced key dispatching, such as long presses. + */ + public static class DispatcherState { + int mDownKeyCode; + Object mDownTarget; + SparseIntArray mActiveLongPresses = new SparseIntArray(); + + /** + * Reset back to initial state. + */ + public void reset() { + if (DEBUG) Log.v(TAG, "Reset: " + this); + mDownKeyCode = 0; + mDownTarget = null; + mActiveLongPresses.clear(); + } + + /** + * Stop any tracking associated with this target. + */ + public void reset(Object target) { + if (mDownTarget == target) { + if (DEBUG) Log.v(TAG, "Reset in " + target + ": " + this); + mDownKeyCode = 0; + mDownTarget = null; + } + } + + /** + * Start tracking the key code associated with the given event. This + * can only be called on a key down. It will allow you to see any + * long press associated with the key, and will result in + * {@link KeyEvent#isTracking} return true on the long press and up + * events. + * + * <p>This is only needed if you are directly dispatching events, rather + * than handling them in {@link Callback#onKeyDown}. + */ + public void startTracking(KeyEvent event, Object target) { + if (event.getAction() != ACTION_DOWN) { + throw new IllegalArgumentException( + "Can only start tracking on a down event"); + } + if (DEBUG) Log.v(TAG, "Start trackingt in " + target + ": " + this); + mDownKeyCode = event.getKeyCode(); + mDownTarget = target; + } + + /** + * Return true if the key event is for a key code that is currently + * being tracked by the dispatcher. + */ + public boolean isTracking(KeyEvent event) { + return mDownKeyCode == event.getKeyCode(); + } + + /** + * Keep track of the given event's key code as having performed an + * action with a long press, so no action should occur on the up. + * <p>This is only needed if you are directly dispatching events, rather + * than handling them in {@link Callback#onKeyLongPress}. + */ + public void performedLongPress(KeyEvent event) { + mActiveLongPresses.put(event.getKeyCode(), 1); + } + + /** + * Handle key up event to stop tracking. This resets the dispatcher state, + * and updates the key event state based on it. + * <p>This is only needed if you are directly dispatching events, rather + * than handling them in {@link Callback#onKeyUp}. + */ + public void handleUpEvent(KeyEvent event) { + final int keyCode = event.getKeyCode(); + if (DEBUG) Log.v(TAG, "Handle key up " + event + ": " + this); + int index = mActiveLongPresses.indexOfKey(keyCode); + if (index >= 0) { + if (DEBUG) Log.v(TAG, " Index: " + index); + event.mFlags |= FLAG_CANCELED | FLAG_CANCELED_LONG_PRESS; + mActiveLongPresses.removeAt(index); + } + if (mDownKeyCode == keyCode) { + if (DEBUG) Log.v(TAG, " Tracking!"); + event.mFlags |= FLAG_TRACKING; + mDownKeyCode = 0; + mDownTarget = null; + } + } + } + public String toString() { return "KeyEvent{action=" + mAction + " code=" + mKeyCode + " repeat=" + mRepeatCount |