Merge change 24850 into eclair
* changes: Last big work on #1991910: Make swipes work with capacitive keys
This commit is contained in:
318
api/current.xml
318
api/current.xml
@@ -16089,6 +16089,17 @@
|
||||
visibility="public"
|
||||
>
|
||||
</method>
|
||||
<method name="onBackPressed"
|
||||
return="void"
|
||||
abstract="false"
|
||||
native="false"
|
||||
synchronized="false"
|
||||
static="false"
|
||||
final="false"
|
||||
deprecated="not deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
</method>
|
||||
<method name="onChildTitleChanged"
|
||||
return="void"
|
||||
abstract="false"
|
||||
@@ -16318,6 +16329,21 @@
|
||||
<parameter name="event" type="android.view.KeyEvent">
|
||||
</parameter>
|
||||
</method>
|
||||
<method name="onKeyLongPress"
|
||||
return="boolean"
|
||||
abstract="false"
|
||||
native="false"
|
||||
synchronized="false"
|
||||
static="false"
|
||||
final="false"
|
||||
deprecated="not deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
<parameter name="keyCode" type="int">
|
||||
</parameter>
|
||||
<parameter name="event" type="android.view.KeyEvent">
|
||||
</parameter>
|
||||
</method>
|
||||
<method name="onKeyMultiple"
|
||||
return="boolean"
|
||||
abstract="false"
|
||||
@@ -20085,6 +20111,17 @@
|
||||
visibility="public"
|
||||
>
|
||||
</method>
|
||||
<method name="onBackPressed"
|
||||
return="void"
|
||||
abstract="false"
|
||||
native="false"
|
||||
synchronized="false"
|
||||
static="false"
|
||||
final="false"
|
||||
deprecated="not deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
</method>
|
||||
<method name="onContentChanged"
|
||||
return="void"
|
||||
abstract="false"
|
||||
@@ -20219,6 +20256,21 @@
|
||||
<parameter name="event" type="android.view.KeyEvent">
|
||||
</parameter>
|
||||
</method>
|
||||
<method name="onKeyLongPress"
|
||||
return="boolean"
|
||||
abstract="false"
|
||||
native="false"
|
||||
synchronized="false"
|
||||
static="false"
|
||||
final="false"
|
||||
deprecated="not deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
<parameter name="keyCode" type="int">
|
||||
</parameter>
|
||||
<parameter name="event" type="android.view.KeyEvent">
|
||||
</parameter>
|
||||
</method>
|
||||
<method name="onKeyMultiple"
|
||||
return="boolean"
|
||||
abstract="false"
|
||||
@@ -69570,6 +69622,17 @@
|
||||
visibility="public"
|
||||
>
|
||||
</constructor>
|
||||
<method name="getKeyDispatcherState"
|
||||
return="android.view.KeyEvent.DispatcherState"
|
||||
abstract="false"
|
||||
native="false"
|
||||
synchronized="false"
|
||||
static="false"
|
||||
final="false"
|
||||
deprecated="not deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
</method>
|
||||
<method name="onBind"
|
||||
return="android.os.IBinder"
|
||||
abstract="false"
|
||||
@@ -70315,6 +70378,21 @@
|
||||
<parameter name="event" type="android.view.KeyEvent">
|
||||
</parameter>
|
||||
</method>
|
||||
<method name="onKeyLongPress"
|
||||
return="boolean"
|
||||
abstract="false"
|
||||
native="false"
|
||||
synchronized="false"
|
||||
static="false"
|
||||
final="false"
|
||||
deprecated="not deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
<parameter name="keyCode" type="int">
|
||||
</parameter>
|
||||
<parameter name="event" type="android.view.KeyEvent">
|
||||
</parameter>
|
||||
</method>
|
||||
<method name="onKeyMultiple"
|
||||
return="boolean"
|
||||
abstract="false"
|
||||
@@ -144823,7 +144901,7 @@
|
||||
type="android.view.KeyEvent"
|
||||
static="false"
|
||||
final="false"
|
||||
deprecated="not deprecated"
|
||||
deprecated="deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
<parameter name="origEvent" type="android.view.KeyEvent">
|
||||
@@ -144880,6 +144958,25 @@
|
||||
<parameter name="newRepeat" type="int">
|
||||
</parameter>
|
||||
</method>
|
||||
<method name="changeTimeRepeat"
|
||||
return="android.view.KeyEvent"
|
||||
abstract="false"
|
||||
native="false"
|
||||
synchronized="false"
|
||||
static="true"
|
||||
final="false"
|
||||
deprecated="not deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
<parameter name="event" type="android.view.KeyEvent">
|
||||
</parameter>
|
||||
<parameter name="eventTime" type="long">
|
||||
</parameter>
|
||||
<parameter name="newRepeat" type="int">
|
||||
</parameter>
|
||||
<parameter name="newFlags" type="int">
|
||||
</parameter>
|
||||
</method>
|
||||
<method name="describeContents"
|
||||
return="int"
|
||||
abstract="false"
|
||||
@@ -144891,6 +144988,19 @@
|
||||
visibility="public"
|
||||
>
|
||||
</method>
|
||||
<method name="dispatch"
|
||||
return="boolean"
|
||||
abstract="false"
|
||||
native="false"
|
||||
synchronized="false"
|
||||
static="false"
|
||||
final="true"
|
||||
deprecated="deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
<parameter name="receiver" type="android.view.KeyEvent.Callback">
|
||||
</parameter>
|
||||
</method>
|
||||
<method name="dispatch"
|
||||
return="boolean"
|
||||
abstract="false"
|
||||
@@ -144903,6 +145013,10 @@
|
||||
>
|
||||
<parameter name="receiver" type="android.view.KeyEvent.Callback">
|
||||
</parameter>
|
||||
<parameter name="state" type="android.view.KeyEvent.DispatcherState">
|
||||
</parameter>
|
||||
<parameter name="target" type="java.lang.Object">
|
||||
</parameter>
|
||||
</method>
|
||||
<method name="getAction"
|
||||
return="int"
|
||||
@@ -145149,6 +145263,17 @@
|
||||
visibility="public"
|
||||
>
|
||||
</method>
|
||||
<method name="isLongPress"
|
||||
return="boolean"
|
||||
abstract="false"
|
||||
native="false"
|
||||
synchronized="false"
|
||||
static="false"
|
||||
final="true"
|
||||
deprecated="not deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
</method>
|
||||
<method name="isModifierKey"
|
||||
return="boolean"
|
||||
abstract="false"
|
||||
@@ -145206,6 +145331,28 @@
|
||||
visibility="public"
|
||||
>
|
||||
</method>
|
||||
<method name="isTracking"
|
||||
return="boolean"
|
||||
abstract="false"
|
||||
native="false"
|
||||
synchronized="false"
|
||||
static="false"
|
||||
final="true"
|
||||
deprecated="not deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
</method>
|
||||
<method name="startTracking"
|
||||
return="void"
|
||||
abstract="false"
|
||||
native="false"
|
||||
synchronized="false"
|
||||
static="false"
|
||||
final="true"
|
||||
deprecated="not deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
</method>
|
||||
<method name="writeToParcel"
|
||||
return="void"
|
||||
abstract="false"
|
||||
@@ -145275,6 +145422,17 @@
|
||||
visibility="public"
|
||||
>
|
||||
</field>
|
||||
<field name="FLAG_CANCELED_LONG_PRESS"
|
||||
type="int"
|
||||
transient="false"
|
||||
volatile="false"
|
||||
value="256"
|
||||
static="true"
|
||||
final="true"
|
||||
deprecated="not deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
</field>
|
||||
<field name="FLAG_EDITOR_ACTION"
|
||||
type="int"
|
||||
transient="false"
|
||||
@@ -145308,6 +145466,17 @@
|
||||
visibility="public"
|
||||
>
|
||||
</field>
|
||||
<field name="FLAG_LONG_PRESS"
|
||||
type="int"
|
||||
transient="false"
|
||||
volatile="false"
|
||||
value="128"
|
||||
static="true"
|
||||
final="true"
|
||||
deprecated="not deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
</field>
|
||||
<field name="FLAG_SOFT_KEYBOARD"
|
||||
type="int"
|
||||
transient="false"
|
||||
@@ -145319,6 +145488,17 @@
|
||||
visibility="public"
|
||||
>
|
||||
</field>
|
||||
<field name="FLAG_TRACKING"
|
||||
type="int"
|
||||
transient="false"
|
||||
volatile="false"
|
||||
value="512"
|
||||
static="true"
|
||||
final="true"
|
||||
deprecated="not deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
</field>
|
||||
<field name="FLAG_VIRTUAL_HARD_KEY"
|
||||
type="int"
|
||||
transient="false"
|
||||
@@ -146464,6 +146644,21 @@
|
||||
<parameter name="event" type="android.view.KeyEvent">
|
||||
</parameter>
|
||||
</method>
|
||||
<method name="onKeyLongPress"
|
||||
return="boolean"
|
||||
abstract="true"
|
||||
native="false"
|
||||
synchronized="false"
|
||||
static="false"
|
||||
final="false"
|
||||
deprecated="not deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
<parameter name="keyCode" type="int">
|
||||
</parameter>
|
||||
<parameter name="event" type="android.view.KeyEvent">
|
||||
</parameter>
|
||||
</method>
|
||||
<method name="onKeyMultiple"
|
||||
return="boolean"
|
||||
abstract="true"
|
||||
@@ -146497,6 +146692,101 @@
|
||||
</parameter>
|
||||
</method>
|
||||
</interface>
|
||||
<class name="KeyEvent.DispatcherState"
|
||||
extends="java.lang.Object"
|
||||
abstract="false"
|
||||
static="true"
|
||||
final="false"
|
||||
deprecated="not deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
<constructor name="KeyEvent.DispatcherState"
|
||||
type="android.view.KeyEvent.DispatcherState"
|
||||
static="false"
|
||||
final="false"
|
||||
deprecated="not deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
</constructor>
|
||||
<method name="handleUpEvent"
|
||||
return="void"
|
||||
abstract="false"
|
||||
native="false"
|
||||
synchronized="false"
|
||||
static="false"
|
||||
final="false"
|
||||
deprecated="not deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
<parameter name="event" type="android.view.KeyEvent">
|
||||
</parameter>
|
||||
</method>
|
||||
<method name="isTracking"
|
||||
return="boolean"
|
||||
abstract="false"
|
||||
native="false"
|
||||
synchronized="false"
|
||||
static="false"
|
||||
final="false"
|
||||
deprecated="not deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
<parameter name="event" type="android.view.KeyEvent">
|
||||
</parameter>
|
||||
</method>
|
||||
<method name="performedLongPress"
|
||||
return="void"
|
||||
abstract="false"
|
||||
native="false"
|
||||
synchronized="false"
|
||||
static="false"
|
||||
final="false"
|
||||
deprecated="not deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
<parameter name="event" type="android.view.KeyEvent">
|
||||
</parameter>
|
||||
</method>
|
||||
<method name="reset"
|
||||
return="void"
|
||||
abstract="false"
|
||||
native="false"
|
||||
synchronized="false"
|
||||
static="false"
|
||||
final="false"
|
||||
deprecated="not deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
</method>
|
||||
<method name="reset"
|
||||
return="void"
|
||||
abstract="false"
|
||||
native="false"
|
||||
synchronized="false"
|
||||
static="false"
|
||||
final="false"
|
||||
deprecated="not deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
<parameter name="target" type="java.lang.Object">
|
||||
</parameter>
|
||||
</method>
|
||||
<method name="startTracking"
|
||||
return="void"
|
||||
abstract="false"
|
||||
native="false"
|
||||
synchronized="false"
|
||||
static="false"
|
||||
final="false"
|
||||
deprecated="not deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
<parameter name="event" type="android.view.KeyEvent">
|
||||
</parameter>
|
||||
<parameter name="target" type="java.lang.Object">
|
||||
</parameter>
|
||||
</method>
|
||||
</class>
|
||||
<class name="LayoutInflater"
|
||||
extends="java.lang.Object"
|
||||
abstract="true"
|
||||
@@ -150871,6 +151161,17 @@
|
||||
visibility="public"
|
||||
>
|
||||
</method>
|
||||
<method name="getKeyDispatcherState"
|
||||
return="android.view.KeyEvent.DispatcherState"
|
||||
abstract="false"
|
||||
native="false"
|
||||
synchronized="false"
|
||||
static="false"
|
||||
final="false"
|
||||
deprecated="not deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
</method>
|
||||
<method name="getLayoutParams"
|
||||
return="android.view.ViewGroup.LayoutParams"
|
||||
abstract="false"
|
||||
@@ -151997,6 +152298,21 @@
|
||||
<parameter name="event" type="android.view.KeyEvent">
|
||||
</parameter>
|
||||
</method>
|
||||
<method name="onKeyLongPress"
|
||||
return="boolean"
|
||||
abstract="false"
|
||||
native="false"
|
||||
synchronized="false"
|
||||
static="false"
|
||||
final="false"
|
||||
deprecated="not deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
<parameter name="keyCode" type="int">
|
||||
</parameter>
|
||||
<parameter name="event" type="android.view.KeyEvent">
|
||||
</parameter>
|
||||
</method>
|
||||
<method name="onKeyMultiple"
|
||||
return="boolean"
|
||||
abstract="false"
|
||||
|
||||
@@ -1752,8 +1752,9 @@ public class Activity extends ContextThemeWrapper
|
||||
*
|
||||
* <p>If the focused view didn't want this event, this method is called.
|
||||
*
|
||||
* <p>The default implementation handles KEYCODE_BACK to stop the activity
|
||||
* and go back, and other default key handling if configured with {@link #setDefaultKeyMode}.
|
||||
* <p>The default implementation sets up state to call
|
||||
* {@link #onKeyLongPress}, and does other default key handling
|
||||
* if configured with {@link #setDefaultKeyMode}.
|
||||
*
|
||||
* @return Return <code>true</code> to prevent this event from being propagated
|
||||
* further, or <code>false</code> to indicate that you have not handled
|
||||
@@ -1762,16 +1763,19 @@ public class Activity extends ContextThemeWrapper
|
||||
* @see android.view.KeyEvent
|
||||
*/
|
||||
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
||||
if (keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0) {
|
||||
finish();
|
||||
if (keyCode == KeyEvent.KEYCODE_BACK) {
|
||||
event.startTracking();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (mDefaultKeyMode == DEFAULT_KEYS_DISABLE) {
|
||||
return false;
|
||||
} else if (mDefaultKeyMode == DEFAULT_KEYS_SHORTCUT) {
|
||||
return getWindow().performPanelShortcut(Window.FEATURE_OPTIONS_PANEL,
|
||||
keyCode, event, Menu.FLAG_ALWAYS_PERFORM_CLOSE);
|
||||
if (getWindow().performPanelShortcut(Window.FEATURE_OPTIONS_PANEL,
|
||||
keyCode, event, Menu.FLAG_ALWAYS_PERFORM_CLOSE)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
// Common code for DEFAULT_KEYS_DIALER & DEFAULT_KEYS_SEARCH_*
|
||||
boolean clearSpannable = false;
|
||||
@@ -1780,8 +1784,8 @@ public class Activity extends ContextThemeWrapper
|
||||
clearSpannable = true;
|
||||
handled = false;
|
||||
} else {
|
||||
handled = TextKeyListener.getInstance().onKeyDown(null, mDefaultKeySsb,
|
||||
keyCode, event);
|
||||
handled = TextKeyListener.getInstance().onKeyDown(
|
||||
null, mDefaultKeySsb, keyCode, event);
|
||||
if (handled && mDefaultKeySsb.length() > 0) {
|
||||
// something useable has been typed - dispatch it now.
|
||||
|
||||
@@ -1812,12 +1816,24 @@ public class Activity extends ContextThemeWrapper
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Default implementation of {@link KeyEvent.Callback#onKeyLongPress(int, KeyEvent)
|
||||
* KeyEvent.Callback.onKeyLongPress()}: always returns false (doesn't handle
|
||||
* the event).
|
||||
*/
|
||||
public boolean onKeyLongPress(int keyCode, KeyEvent event) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a key was released and not handled by any of the views
|
||||
* inside of the activity. So, for example, key presses while the cursor
|
||||
* is inside a TextView will not trigger the event (unless it is a navigation
|
||||
* to another object) because TextView handles its own key presses.
|
||||
*
|
||||
* <p>The default implementation handles KEYCODE_BACK to stop the activity
|
||||
* and go back.
|
||||
*
|
||||
* @return Return <code>true</code> to prevent this event from being propagated
|
||||
* further, or <code>false</code> to indicate that you have not handled
|
||||
* this event and it should continue to be propagated.
|
||||
@@ -1825,6 +1841,11 @@ public class Activity extends ContextThemeWrapper
|
||||
* @see KeyEvent
|
||||
*/
|
||||
public boolean onKeyUp(int keyCode, KeyEvent event) {
|
||||
if (keyCode == KeyEvent.KEYCODE_BACK && event.isTracking()
|
||||
&& !event.isCanceled()) {
|
||||
onBackPressed();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1837,6 +1858,15 @@ public class Activity extends ContextThemeWrapper
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the activity has detected the user's press of the back
|
||||
* key. The default implementation simply finishes the current activity,
|
||||
* but you can override this to do whatever you want.
|
||||
*/
|
||||
public void onBackPressed() {
|
||||
finish();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a touch screen event was not handled by any of the views
|
||||
* under it. This is most useful to process touch events that happen
|
||||
@@ -1909,9 +1939,10 @@ public class Activity extends ContextThemeWrapper
|
||||
/**
|
||||
* Called when the current {@link Window} of the activity gains or loses
|
||||
* focus. This is the best indicator of whether this activity is visible
|
||||
* to the user.
|
||||
* to the user. The default implementation clears the key tracking
|
||||
* state, so should always be called.
|
||||
*
|
||||
* <p>Note that this provides information what global focus state, which
|
||||
* <p>Note that this provides information about global focus state, which
|
||||
* is managed independently of activity lifecycles. As such, while focus
|
||||
* changes will generally have some relation to lifecycle changes (an
|
||||
* activity that is stopped will not generally get window focus), you
|
||||
@@ -1988,7 +2019,8 @@ public class Activity extends ContextThemeWrapper
|
||||
if (getWindow().superDispatchKeyEvent(event)) {
|
||||
return true;
|
||||
}
|
||||
return event.dispatch(this);
|
||||
return event.dispatch(this, mDecor != null
|
||||
? mDecor.getKeyDispatcherState() : null, this);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -481,30 +481,45 @@ public class Dialog implements DialogInterface, Window.Callback,
|
||||
*
|
||||
* <p>If the focused view didn't want this event, this method is called.
|
||||
*
|
||||
* <p>The default implementation handles KEYCODE_BACK to close the
|
||||
* dialog.
|
||||
* <p>The default implementation consumed the KEYCODE_BACK to later
|
||||
* handle it in {@link #onKeyUp}.
|
||||
*
|
||||
* @see #onKeyUp
|
||||
* @see android.view.KeyEvent
|
||||
*/
|
||||
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
||||
if (keyCode == KeyEvent.KEYCODE_BACK) {
|
||||
if (mCancelable) {
|
||||
cancel();
|
||||
}
|
||||
event.startTracking();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default implementation of {@link KeyEvent.Callback#onKeyLongPress(int, KeyEvent)
|
||||
* KeyEvent.Callback.onKeyLongPress()}: always returns false (doesn't handle
|
||||
* the event).
|
||||
*/
|
||||
public boolean onKeyLongPress(int keyCode, KeyEvent event) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* A key was released.
|
||||
*
|
||||
* <p>The default implementation handles KEYCODE_BACK to close the
|
||||
* dialog.
|
||||
*
|
||||
* @see #onKeyDown
|
||||
* @see KeyEvent
|
||||
*/
|
||||
public boolean onKeyUp(int keyCode, KeyEvent event) {
|
||||
if (keyCode == KeyEvent.KEYCODE_BACK && event.isTracking()
|
||||
&& !event.isCanceled()) {
|
||||
onBackPressed();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -517,6 +532,17 @@ public class Dialog implements DialogInterface, Window.Callback,
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the dialog has detected the user's press of the back
|
||||
* key. The default implementation simply cancels the dialog (only if
|
||||
* it is cancelable), but you can override this to do whatever you want.
|
||||
*/
|
||||
public void onBackPressed() {
|
||||
if (mCancelable) {
|
||||
cancel();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a touch screen event was not handled by any of the views
|
||||
* under it. This is most useful to process touch events that happen outside
|
||||
@@ -599,7 +625,8 @@ public class Dialog implements DialogInterface, Window.Callback,
|
||||
if (mWindow.superDispatchKeyEvent(event)) {
|
||||
return true;
|
||||
}
|
||||
return event.dispatch(this);
|
||||
return event.dispatch(this, mDecor != null
|
||||
? mDecor.getKeyDispatcherState() : null, this);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -745,11 +745,9 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
|
||||
return true;
|
||||
}
|
||||
|
||||
if (keyCode == KeyEvent.KEYCODE_SEARCH && event.getRepeatCount() < 1) {
|
||||
// If the search key is pressed, toggle between global and in-app search. If we are
|
||||
// currently doing global search and there is no in-app search context to toggle to,
|
||||
// just don't do anything.
|
||||
return toggleGlobalSearch();
|
||||
if (keyCode == KeyEvent.KEYCODE_SEARCH) {
|
||||
// Consume search key for later use.
|
||||
return true;
|
||||
}
|
||||
|
||||
// if it's an action specified by the searchable activity, launch the
|
||||
@@ -763,6 +761,29 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyUp(int keyCode, KeyEvent event) {
|
||||
if (DBG) Log.d(LOG_TAG, "onKeyUp(" + keyCode + "," + event + ")");
|
||||
if (mSearchable == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// handle back key to go back to previous searchable, etc.
|
||||
if (handleBackKey(keyCode, event)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (keyCode == KeyEvent.KEYCODE_SEARCH && event.isTracking()
|
||||
&& !event.isCanceled()) {
|
||||
// If the search key is pressed, toggle between global and in-app search. If we are
|
||||
// currently doing global search and there is no in-app search context to toggle to,
|
||||
// just don't do anything.
|
||||
return toggleGlobalSearch();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback to watch the textedit field for empty/non-empty
|
||||
*/
|
||||
@@ -1500,21 +1521,24 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
|
||||
*
|
||||
* @return <code>true</code> if there was a previous component that we could go back to.
|
||||
*/
|
||||
private boolean backToPreviousComponent() {
|
||||
private boolean backToPreviousComponent(boolean doIt) {
|
||||
ComponentName previous = popPreviousComponent();
|
||||
if (previous == null) {
|
||||
return false;
|
||||
}
|
||||
if (!show(previous, mAppSearchData, false)) {
|
||||
Log.w(LOG_TAG, "Failed to switch to source " + previous);
|
||||
return false;
|
||||
}
|
||||
|
||||
// must touch text to trigger suggestions
|
||||
// TODO: should this be the text as it was when the user left
|
||||
// the source that we are now going back to?
|
||||
String query = mSearchAutoComplete.getText().toString();
|
||||
setUserQuery(query);
|
||||
if (doIt) {
|
||||
if (!show(previous, mAppSearchData, false)) {
|
||||
Log.w(LOG_TAG, "Failed to switch to source " + previous);
|
||||
return false;
|
||||
}
|
||||
|
||||
// must touch text to trigger suggestions
|
||||
// TODO: should this be the text as it was when the user left
|
||||
// the source that we are now going back to?
|
||||
String query = mSearchAutoComplete.getText().toString();
|
||||
setUserQuery(query);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1660,6 +1684,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
|
||||
public static class SearchAutoComplete extends AutoCompleteTextView {
|
||||
|
||||
private int mThreshold;
|
||||
private int mLastKeyDown;
|
||||
private SearchDialog mSearchDialog;
|
||||
|
||||
public SearchAutoComplete(Context context) {
|
||||
@@ -1740,27 +1765,44 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
|
||||
*/
|
||||
@Override
|
||||
public boolean onKeyPreIme(int keyCode, KeyEvent event) {
|
||||
mLastKeyDown = keyCode;
|
||||
if (mSearchDialog.mSearchable == null) {
|
||||
return false;
|
||||
}
|
||||
if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_DOWN) {
|
||||
if (mSearchDialog.backToPreviousComponent()) {
|
||||
return true;
|
||||
if (keyCode == KeyEvent.KEYCODE_BACK) {
|
||||
if (event.getAction() == KeyEvent.ACTION_DOWN
|
||||
&& event.getRepeatCount() == 0) {
|
||||
// We releae the back key, might we want to do
|
||||
// something before the IME?
|
||||
if (mSearchDialog.backToPreviousComponent(false)) {
|
||||
return true;
|
||||
}
|
||||
if (isInputMethodNotNeeded() ||
|
||||
(isEmpty() && getDropDownChildCount() >= getAdapterCount())) {
|
||||
return true;
|
||||
}
|
||||
mLastKeyDown = 0;
|
||||
return false; // will dismiss soft keyboard if necessary
|
||||
} else if (event.getAction() == KeyEvent.ACTION_UP
|
||||
&& mLastKeyDown == keyCode && !event.isCanceled()) {
|
||||
if (mSearchDialog.backToPreviousComponent(true)) {
|
||||
return true;
|
||||
}
|
||||
// If the drop-down obscures the keyboard, the user wouldn't see anything
|
||||
// happening when pressing back, so we dismiss the entire dialog instead.
|
||||
//
|
||||
// also: if there is no text entered, we also want to dismiss the whole dialog,
|
||||
// not just the soft keyboard. the exception to this is if there are shortcuts
|
||||
// that aren't displayed (e.g are being obscured by the soft keyboard); in that
|
||||
// case we want to dismiss the soft keyboard so the user can see the rest of the
|
||||
// shortcuts.
|
||||
if (isInputMethodNotNeeded() ||
|
||||
(isEmpty() && getDropDownChildCount() >= getAdapterCount())) {
|
||||
mSearchDialog.cancel();
|
||||
return true;
|
||||
}
|
||||
return false; // will dismiss soft keyboard if necessary
|
||||
}
|
||||
// If the drop-down obscures the keyboard, the user wouldn't see anything
|
||||
// happening when pressing back, so we dismiss the entire dialog instead.
|
||||
//
|
||||
// also: if there is no text entered, we also want to dismiss the whole dialog,
|
||||
// not just the soft keyboard. the exception to this is if there are shortcuts
|
||||
// that aren't displayed (e.g are being obscured by the soft keyboard); in that
|
||||
// case we want to dismiss the soft keyboard so the user can see the rest of the
|
||||
// shortcuts.
|
||||
if (isInputMethodNotNeeded() ||
|
||||
(isEmpty() && getDropDownChildCount() >= getAdapterCount())) {
|
||||
mSearchDialog.cancel();
|
||||
return true;
|
||||
}
|
||||
return false; // will dismiss soft keyboard if necessary
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -1772,11 +1814,18 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
|
||||
}
|
||||
|
||||
protected boolean handleBackKey(int keyCode, KeyEvent event) {
|
||||
if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_DOWN) {
|
||||
if (backToPreviousComponent()) {
|
||||
if (keyCode == KeyEvent.KEYCODE_BACK) {
|
||||
if (event.getAction() == KeyEvent.ACTION_DOWN) {
|
||||
// Consume the event, to get an up at which point we execute.
|
||||
return true;
|
||||
}
|
||||
cancel();
|
||||
if (event.getAction() == KeyEvent.ACTION_UP && event.isTracking()
|
||||
&& !event.isCanceled()) {
|
||||
if (backToPreviousComponent(true)) {
|
||||
return true;
|
||||
}
|
||||
cancel();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -45,6 +45,9 @@ public abstract class AbstractInputMethodService extends Service
|
||||
implements KeyEvent.Callback {
|
||||
private InputMethod mInputMethod;
|
||||
|
||||
final KeyEvent.DispatcherState mDispatcherState
|
||||
= new KeyEvent.DispatcherState();
|
||||
|
||||
/**
|
||||
* Base class for derived classes to implement their {@link InputMethod}
|
||||
* interface. This takes care of basic maintenance of the input method,
|
||||
@@ -129,7 +132,8 @@ public abstract class AbstractInputMethodService extends Service
|
||||
* callbacks on the service, and tell the client when this is done.
|
||||
*/
|
||||
public void dispatchKeyEvent(int seq, KeyEvent event, EventCallback callback) {
|
||||
boolean handled = event.dispatch(AbstractInputMethodService.this);
|
||||
boolean handled = event.dispatch(AbstractInputMethodService.this,
|
||||
mDispatcherState, this);
|
||||
if (callback != null) {
|
||||
callback.finishedEvent(seq, handled);
|
||||
}
|
||||
@@ -147,6 +151,16 @@ public abstract class AbstractInputMethodService extends Service
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the global {@link KeyEvent.DispatcherState KeyEvent.DispatcherState}
|
||||
* for used for processing events from the target application.
|
||||
* Normally you will not need to use this directly, but
|
||||
* just use the standard high-level event callbacks like {@link #onKeyDown}.
|
||||
*/
|
||||
public KeyEvent.DispatcherState getKeyDispatcherState() {
|
||||
return mDispatcherState;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the framework during initialization, when the InputMethod
|
||||
* interface for this service needs to be created.
|
||||
|
||||
@@ -554,7 +554,7 @@ public class InputMethodService extends AbstractInputMethodService {
|
||||
mImm = (InputMethodManager)getSystemService(INPUT_METHOD_SERVICE);
|
||||
mInflater = (LayoutInflater)getSystemService(
|
||||
Context.LAYOUT_INFLATER_SERVICE);
|
||||
mWindow = new SoftInputWindow(this, mTheme);
|
||||
mWindow = new SoftInputWindow(this, mTheme, mDispatcherState);
|
||||
initViews();
|
||||
mWindow.getWindow().setLayout(FILL_PARENT, WRAP_CONTENT);
|
||||
}
|
||||
@@ -1557,6 +1557,28 @@ public class InputMethodService extends AbstractInputMethodService {
|
||||
mImm.showSoftInputFromInputMethod(mToken, flags);
|
||||
}
|
||||
|
||||
private boolean handleBack(boolean doIt) {
|
||||
if (mShowInputRequested) {
|
||||
// If the soft input area is shown, back closes it and we
|
||||
// consume the back key.
|
||||
if (doIt) requestHideSelf(0);
|
||||
return true;
|
||||
} else if (mWindowVisible) {
|
||||
if (mCandidatesVisibility == View.VISIBLE) {
|
||||
// If we are showing candidates even if no input area, then
|
||||
// hide them.
|
||||
if (doIt) setCandidatesViewShown(false);
|
||||
} else {
|
||||
// If we have the window visible for some other reason --
|
||||
// most likely to show candidates -- then just get rid
|
||||
// of it. This really shouldn't happen, but just in case...
|
||||
if (doIt) hideWindow();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override this to intercept key down events before they are processed by the
|
||||
* application. If you return true, the application will not itself
|
||||
@@ -1564,37 +1586,32 @@ public class InputMethodService extends AbstractInputMethodService {
|
||||
* will occur as if the IME had not seen the event at all.
|
||||
*
|
||||
* <p>The default implementation intercepts {@link KeyEvent#KEYCODE_BACK
|
||||
* KeyEvent.KEYCODE_BACK} to hide the current IME UI if it is shown. In
|
||||
* additional, in fullscreen mode only, it will consume DPAD movement
|
||||
* KeyEvent.KEYCODE_BACK} if the IME is currently shown, to
|
||||
* possibly hide it when the key goes up (if not canceled or long pressed). In
|
||||
* addition, in fullscreen mode only, it will consume DPAD movement
|
||||
* events to move the cursor in the extracted text view, not allowing
|
||||
* them to perform navigation in the underlying application.
|
||||
*/
|
||||
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
||||
if (event.getKeyCode() == KeyEvent.KEYCODE_BACK
|
||||
&& event.getRepeatCount() == 0) {
|
||||
if (mShowInputRequested) {
|
||||
// If the soft input area is shown, back closes it and we
|
||||
// consume the back key.
|
||||
requestHideSelf(0);
|
||||
if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
|
||||
if (handleBack(false)) {
|
||||
event.startTracking();
|
||||
return true;
|
||||
} else if (mWindowVisible) {
|
||||
if (mCandidatesVisibility == View.VISIBLE) {
|
||||
// If we are showing candidates even if no input area, then
|
||||
// hide them.
|
||||
setCandidatesViewShown(false);
|
||||
return true;
|
||||
} else {
|
||||
// If we have the window visible for some other reason --
|
||||
// most likely to show candidates -- then just get rid
|
||||
// of it. This really shouldn't happen, but just in case...
|
||||
hideWindow();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return doMovementKey(keyCode, event, MOVEMENT_DOWN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Default implementation of {@link KeyEvent.Callback#onKeyLongPress(int, KeyEvent)
|
||||
* KeyEvent.Callback.onKeyLongPress()}: always returns false (doesn't handle
|
||||
* the event).
|
||||
*/
|
||||
public boolean onKeyLongPress(int keyCode, KeyEvent event) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override this to intercept special key multiple events before they are
|
||||
* processed by the
|
||||
@@ -1617,12 +1634,18 @@ public class InputMethodService extends AbstractInputMethodService {
|
||||
* process the event. If you return true, the normal application processing
|
||||
* will occur as if the IME had not seen the event at all.
|
||||
*
|
||||
* <p>The default implementation always returns false, except when
|
||||
* in fullscreen mode, where it will consume DPAD movement
|
||||
* <p>The default implementation intercepts {@link KeyEvent#KEYCODE_BACK
|
||||
* KeyEvent.KEYCODE_BACK} to hide the current IME UI if it is shown. In
|
||||
* addition, in fullscreen mode only, it will consume DPAD movement
|
||||
* events to move the cursor in the extracted text view, not allowing
|
||||
* them to perform navigation in the underlying application.
|
||||
*/
|
||||
public boolean onKeyUp(int keyCode, KeyEvent event) {
|
||||
if (event.getKeyCode() == KeyEvent.KEYCODE_BACK && event.isTracking()
|
||||
&& !event.isCanceled()) {
|
||||
return handleBack(true);
|
||||
}
|
||||
|
||||
return doMovementKey(keyCode, event, MOVEMENT_UP);
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ import android.content.Context;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.os.IBinder;
|
||||
import android.view.Gravity;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.WindowManager;
|
||||
|
||||
/**
|
||||
@@ -30,7 +31,8 @@ import android.view.WindowManager;
|
||||
* always visible.
|
||||
*/
|
||||
class SoftInputWindow extends Dialog {
|
||||
|
||||
final KeyEvent.DispatcherState mDispatcherState;
|
||||
|
||||
public void setToken(IBinder token) {
|
||||
WindowManager.LayoutParams lp = getWindow().getAttributes();
|
||||
lp.token = token;
|
||||
@@ -49,11 +51,19 @@ class SoftInputWindow extends Dialog {
|
||||
* using styles. This theme is applied on top of the current theme in
|
||||
* <var>context</var>. If 0, the default dialog theme will be used.
|
||||
*/
|
||||
public SoftInputWindow(Context context, int theme) {
|
||||
public SoftInputWindow(Context context, int theme,
|
||||
KeyEvent.DispatcherState dispatcherState) {
|
||||
super(context, theme);
|
||||
mDispatcherState = dispatcherState;
|
||||
initDockWindow();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWindowFocusChanged(boolean hasFocus) {
|
||||
super.onWindowFocusChanged(hasFocus);
|
||||
mDispatcherState.reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the size of the DockWindow.
|
||||
*
|
||||
|
||||
@@ -18,6 +18,7 @@ package android.view;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.util.SparseIntArray;
|
||||
import android.view.KeyCharacterMap;
|
||||
import android.view.KeyCharacterMap.KeyData;
|
||||
|
||||
@@ -276,6 +277,32 @@ public class KeyEvent implements Parcelable {
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
@@ -305,7 +332,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.
|
||||
@@ -315,6 +346,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.
|
||||
*
|
||||
@@ -500,11 +547,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;
|
||||
@@ -532,6 +583,26 @@ public class KeyEvent implements Parcelable {
|
||||
return new KeyEvent(event, eventTime, newRepeat);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
@@ -720,6 +791,34 @@ public class KeyEvent implements Parcelable {
|
||||
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.
|
||||
@@ -905,20 +1004,50 @@ public class KeyEvent implements Parcelable {
|
||||
return KeyCharacterMap.load(mDeviceId).isPrintingKey(mKeyCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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;
|
||||
boolean res = receiver.onKeyDown(mKeyCode, this);
|
||||
if (state != null) {
|
||||
if (res && mRepeatCount == 0 && (mFlags&FLAG_START_TRACKING) != 0) {
|
||||
state.startTracking(this, target);
|
||||
} else if (isLongPress() && state.isTracking(this)) {
|
||||
try {
|
||||
if (receiver.onKeyLongPress(mKeyCode, this)) {
|
||||
state.performedLongPress(this);
|
||||
res = true;
|
||||
}
|
||||
} catch (AbstractMethodError e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
case ACTION_UP:
|
||||
if (state != null) {
|
||||
state.handleUpEvent(this);
|
||||
}
|
||||
return receiver.onKeyUp(mKeyCode, this);
|
||||
case ACTION_MULTIPLE:
|
||||
final int count = mRepeatCount;
|
||||
@@ -938,10 +1067,97 @@ 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() {
|
||||
mDownKeyCode = 0;
|
||||
mDownTarget = null;
|
||||
mActiveLongPresses.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop any tracking associated with this target.
|
||||
*/
|
||||
public void reset(Object target) {
|
||||
if (mDownTarget == target) {
|
||||
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");
|
||||
}
|
||||
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();
|
||||
int index = mActiveLongPresses.indexOfKey(keyCode);
|
||||
if (index >= 0) {
|
||||
event.mFlags |= FLAG_CANCELED | FLAG_CANCELED_LONG_PRESS;
|
||||
mActiveLongPresses.removeAt(index);
|
||||
}
|
||||
if (mDownKeyCode == keyCode) {
|
||||
event.mFlags |= FLAG_TRACKING;
|
||||
mDownKeyCode = 0;
|
||||
mDownTarget = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "KeyEvent{action=" + mAction + " code=" + mKeyCode
|
||||
+ " repeat=" + mRepeatCount
|
||||
|
||||
@@ -2600,6 +2600,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
|
||||
if (mOnFocusChangeListener != null) {
|
||||
mOnFocusChangeListener.onFocusChange(this, gainFocus);
|
||||
}
|
||||
|
||||
if (mAttachInfo != null) {
|
||||
mAttachInfo.mKeyDispatchState.reset(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3608,6 +3612,16 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
|
||||
ViewDebug.dumpCapturedView(subTag, v);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the global {@link KeyEvent.DispatcherState KeyEvent.DispatcherState}
|
||||
* for this view's window. Returns null if the view is not currently attached
|
||||
* to the window. Normally you will not need to use this directly, but
|
||||
* just use the standard high-level event callbacks like {@link #onKeyDown}.
|
||||
*/
|
||||
public KeyEvent.DispatcherState getKeyDispatcherState() {
|
||||
return mAttachInfo != null ? mAttachInfo.mKeyDispatchState : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch a key event before it is processed by any input method
|
||||
* associated with the view hierarchy. This can be used to intercept
|
||||
@@ -3645,7 +3659,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
|
||||
return true;
|
||||
}
|
||||
|
||||
return event.dispatch(this);
|
||||
return event.dispatch(this, mAttachInfo != null
|
||||
? mAttachInfo.mKeyDispatchState : null, this);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3909,6 +3924,15 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default implementation of {@link KeyEvent.Callback#onKeyLongPress(int, KeyEvent)
|
||||
* KeyEvent.Callback.onKeyLongPress()}: always returns false (doesn't handle
|
||||
* the event).
|
||||
*/
|
||||
public boolean onKeyLongPress(int keyCode, KeyEvent event) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default implementation of {@link KeyEvent.Callback#onKeyMultiple(int, int, KeyEvent)
|
||||
* KeyEvent.Callback.onKeyMultiple()}: perform clicking of the view
|
||||
@@ -8572,6 +8596,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
|
||||
*/
|
||||
final ArrayList<View> mScrollContainers = new ArrayList<View>();
|
||||
|
||||
final KeyEvent.DispatcherState mKeyDispatchState
|
||||
= new KeyEvent.DispatcherState();
|
||||
|
||||
/**
|
||||
* Indicates whether the view's window currently has the focus.
|
||||
*/
|
||||
|
||||
@@ -1812,6 +1812,7 @@ public final class ViewRoot extends Handler implements ViewParent,
|
||||
if (hasWindowFocus && imm != null && mLastWasImTarget) {
|
||||
imm.startGettingWindowFocus(mView);
|
||||
}
|
||||
mAttachInfo.mKeyDispatchState.reset();
|
||||
mView.dispatchWindowFocusChanged(hasWindowFocus);
|
||||
}
|
||||
|
||||
|
||||
@@ -2945,10 +2945,14 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
|
||||
okToSend = false;
|
||||
break;
|
||||
case KeyEvent.KEYCODE_BACK:
|
||||
if (mFiltered && mPopup != null && mPopup.isShowing() &&
|
||||
event.getAction() == KeyEvent.ACTION_DOWN) {
|
||||
handled = true;
|
||||
mTextFilter.setText("");
|
||||
if (mFiltered && mPopup != null && mPopup.isShowing()) {
|
||||
if (event.getAction() == KeyEvent.ACTION_DOWN) {
|
||||
handled = true;
|
||||
} else if (event.getAction() == KeyEvent.ACTION_UP
|
||||
&& event.isTracking() && !event.isCanceled()) {
|
||||
handled = true;
|
||||
mTextFilter.setText("");
|
||||
}
|
||||
}
|
||||
okToSend = false;
|
||||
break;
|
||||
|
||||
@@ -132,6 +132,8 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe
|
||||
|
||||
private AutoCompleteTextView.PassThroughClickListener mPassThroughClickListener;
|
||||
|
||||
private int mDownKeyCode;
|
||||
|
||||
public AutoCompleteTextView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
@@ -603,12 +605,19 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe
|
||||
|
||||
@Override
|
||||
public boolean onKeyPreIme(int keyCode, KeyEvent event) {
|
||||
if (event.getAction() == KeyEvent.ACTION_DOWN
|
||||
&& event.getRepeatCount() == 0) {
|
||||
mDownKeyCode = keyCode;
|
||||
}
|
||||
if (isPopupShowing()) {
|
||||
// special case for the back key, we do not even try to send it
|
||||
// to the drop down list but instead, consume it immediately
|
||||
if (keyCode == KeyEvent.KEYCODE_BACK && !mDropDownAlwaysVisible) {
|
||||
dismissDropDown();
|
||||
return true;
|
||||
if (event.getAction() == KeyEvent.ACTION_UP
|
||||
&& mDownKeyCode == keyCode && !event.isCanceled()) {
|
||||
dismissDropDown();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.onKeyPreIme(keyCode, event);
|
||||
@@ -1017,6 +1026,7 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe
|
||||
@Override
|
||||
public void onWindowFocusChanged(boolean hasWindowFocus) {
|
||||
super.onWindowFocusChanged(hasWindowFocus);
|
||||
mDownKeyCode = 0;
|
||||
performValidation();
|
||||
if (!hasWindowFocus && !mDropDownAlwaysVisible) {
|
||||
dismissDropDown();
|
||||
@@ -1026,6 +1036,7 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe
|
||||
@Override
|
||||
protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
|
||||
super.onFocusChanged(focused, direction, previouslyFocusedRect);
|
||||
mDownKeyCode = 0;
|
||||
performValidation();
|
||||
if (!focused && !mDropDownAlwaysVisible) {
|
||||
dismissDropDown();
|
||||
|
||||
@@ -981,6 +981,29 @@ public abstract class KeyInputQueue {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the queue has an up event pending that corresponds
|
||||
* to the same key as the given key event.
|
||||
*/
|
||||
boolean hasKeyUpEvent(KeyEvent origEvent) {
|
||||
synchronized (mFirst) {
|
||||
final int keyCode = origEvent.getKeyCode();
|
||||
QueuedEvent cur = mLast.prev;
|
||||
while (cur.prev != null) {
|
||||
if (cur.classType == RawInputEvent.CLASS_KEYBOARD) {
|
||||
KeyEvent ke = (KeyEvent)cur.event;
|
||||
if (ke.getAction() == KeyEvent.ACTION_UP
|
||||
&& ke.getKeyCode() == keyCode) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
cur = cur.prev;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void recycleEvent(QueuedEvent ev) {
|
||||
synchronized (mFirst) {
|
||||
//Log.i(TAG, "Recycle event: " + ev);
|
||||
|
||||
@@ -99,6 +99,7 @@ import android.view.RawInputEvent;
|
||||
import android.view.Surface;
|
||||
import android.view.SurfaceSession;
|
||||
import android.view.View;
|
||||
import android.view.ViewConfiguration;
|
||||
import android.view.ViewTreeObserver;
|
||||
import android.view.WindowManager;
|
||||
import android.view.WindowManagerImpl;
|
||||
@@ -151,9 +152,6 @@ public class WindowManagerService extends IWindowManager.Stub
|
||||
|
||||
static final int LOG_WM_NO_SURFACE_MEMORY = 31000;
|
||||
|
||||
/** How long to wait for first key repeat, in milliseconds */
|
||||
static final int KEY_REPEAT_FIRST_DELAY = 750;
|
||||
|
||||
/** How long to wait for subsequent key repeats, in milliseconds */
|
||||
static final int KEY_REPEAT_DELAY = 50;
|
||||
|
||||
@@ -4900,6 +4898,16 @@ public class WindowManagerService extends IWindowManager.Stub
|
||||
return INJECT_SUCCEEDED;
|
||||
}
|
||||
|
||||
// Okay we have finished waiting for the last event to be processed.
|
||||
// First off, if this is a repeat event, check to see if there is
|
||||
// a corresponding up event in the queue. If there is, we will
|
||||
// just drop the repeat, because it makes no sense to repeat after
|
||||
// the user has released a key. (This is especially important for
|
||||
// long presses.)
|
||||
if (event.getRepeatCount() > 0 && mQueue.hasKeyUpEvent(event)) {
|
||||
return INJECT_SUCCEEDED;
|
||||
}
|
||||
|
||||
WindowState focus = (WindowState)focusObj;
|
||||
|
||||
if (DEBUG_INPUT) Log.v(
|
||||
@@ -6018,6 +6026,7 @@ public class WindowManagerService extends IWindowManager.Stub
|
||||
// Last keydown time for auto-repeating keys
|
||||
long lastKeyTime = SystemClock.uptimeMillis();
|
||||
long nextKeyTime = lastKeyTime+LONG_WAIT;
|
||||
long downTime = 0;
|
||||
|
||||
// How many successive repeats we generated
|
||||
int keyRepeatCount = 0;
|
||||
@@ -6088,15 +6097,17 @@ public class WindowManagerService extends IWindowManager.Stub
|
||||
KeyEvent ke = (KeyEvent)ev.event;
|
||||
if (ke.isDown()) {
|
||||
lastKey = ke;
|
||||
downTime = curTime;
|
||||
keyRepeatCount = 0;
|
||||
lastKeyTime = curTime;
|
||||
nextKeyTime = lastKeyTime
|
||||
+ KEY_REPEAT_FIRST_DELAY;
|
||||
+ ViewConfiguration.getLongPressTimeout();
|
||||
if (DEBUG_INPUT) Log.v(
|
||||
TAG, "Received key down: first repeat @ "
|
||||
+ nextKeyTime);
|
||||
} else {
|
||||
lastKey = null;
|
||||
downTime = 0;
|
||||
// Arbitrary long timeout.
|
||||
lastKeyTime = curTime;
|
||||
nextKeyTime = curTime + LONG_WAIT;
|
||||
@@ -6144,7 +6155,19 @@ public class WindowManagerService extends IWindowManager.Stub
|
||||
if (DEBUG_INPUT) Log.v(
|
||||
TAG, "Key repeat: count=" + keyRepeatCount
|
||||
+ ", next @ " + nextKeyTime);
|
||||
dispatchKey(KeyEvent.changeTimeRepeat(lastKey, curTime, keyRepeatCount), 0, 0);
|
||||
KeyEvent newEvent;
|
||||
if (downTime != 0 && (downTime
|
||||
+ ViewConfiguration.getLongPressTimeout())
|
||||
<= curTime) {
|
||||
newEvent = KeyEvent.changeTimeRepeat(lastKey,
|
||||
curTime, keyRepeatCount,
|
||||
lastKey.getFlags() | KeyEvent.FLAG_LONG_PRESS);
|
||||
downTime = 0;
|
||||
} else {
|
||||
newEvent = KeyEvent.changeTimeRepeat(lastKey,
|
||||
curTime, keyRepeatCount);
|
||||
}
|
||||
dispatchKey(newEvent, 0, 0);
|
||||
|
||||
} else {
|
||||
curTime = SystemClock.uptimeMillis();
|
||||
|
||||
Reference in New Issue
Block a user