Add support for power keys, improve behavior of virtual keys.
The platform now knows how to deal with a platform key, which at this
point is "just like end call, but don't end a call."
Also improve the handling of virtual keys, to allow for canceling when
sliding off into the display and providing haptic feedback.
Finally fixes a bug where the raw x and y in motion event were not
always set which caused the status bar to not work.
diff --git a/api/current.xml b/api/current.xml
index 9ae44e2..e76686f 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -141896,6 +141896,17 @@
visibility="public"
>
</field>
+<field name="VIRTUAL_KEY"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
</class>
<class name="InflateException"
extends="java.lang.RuntimeException"
@@ -142736,6 +142747,17 @@
visibility="public"
>
</method>
+<method name="isCanceled"
+ 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"
@@ -142851,6 +142873,17 @@
visibility="public"
>
</field>
+<field name="FLAG_CANCELED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="32"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="FLAG_EDITOR_ACTION"
type="int"
transient="false"
@@ -142895,6 +142928,17 @@
visibility="public"
>
</field>
+<field name="FLAG_VIRTUAL_HARD_KEY"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="64"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="FLAG_WOKE_HERE"
type="int"
transient="false"
diff --git a/core/java/android/view/HapticFeedbackConstants.java b/core/java/android/view/HapticFeedbackConstants.java
index 841066c..f936f65 100644
--- a/core/java/android/view/HapticFeedbackConstants.java
+++ b/core/java/android/view/HapticFeedbackConstants.java
@@ -24,9 +24,18 @@
private HapticFeedbackConstants() {}
+ /**
+ * The user has performed a long press on an object that is resulting
+ * in an action being performed.
+ */
public static final int LONG_PRESS = 0;
/**
+ * The user has pressed on a virtual on-screen key.
+ */
+ public static final int VIRTUAL_KEY = 1;
+
+ /**
* Flag for {@link View#performHapticFeedback(int, int)
* View.performHapticFeedback(int, int)}: Ignore the setting in the
* view for whether to perform haptic feedback, do it always.
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index 6349288..f9b16fc 100644
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -258,6 +258,25 @@
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;
+
+ /**
* Returns the maximum keycode.
*/
public static int getMaxKeyCode() {
@@ -694,6 +713,14 @@
}
/**
+ * 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;
+ }
+
+ /**
* Retrieve the key code of the key event. This is the physical key that
* was pressed, <em>not</em> the Unicode character.
*
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 52f1a97..89b721d 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -288,9 +288,10 @@
ev.mEventTimeNano = eventTimeNano;
ev.mAction = action;
ev.mMetaState = metaState;
+ ev.mRawX = inData[SAMPLE_X];
+ ev.mRawY = inData[SAMPLE_Y];
ev.mXPrecision = xPrecision;
ev.mYPrecision = yPrecision;
-
ev.mNumPointers = pointers;
ev.mNumSamples = 1;
diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java
index cacf7a2..884950f 100644
--- a/core/java/android/view/ViewRoot.java
+++ b/core/java/android/view/ViewRoot.java
@@ -1665,7 +1665,9 @@
if(Config.LOGV) {
captureMotionLog("captureDispatchPointer", event);
}
- event.offsetLocation(0, mCurScrollY);
+ if (mCurScrollY != 0) {
+ event.offsetLocation(0, mCurScrollY);
+ }
if (MEASURE_LATENCY) {
lt.sample("A Dispatching TouchEvents", System.nanoTime() - event.getEventTimeNano());
}
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index 1371932..f4e9900 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -533,14 +533,15 @@
* @param win The window that currently has focus. This is where the key
* event will normally go.
* @param code Key code.
- * @param metaKeys TODO
+ * @param metaKeys bit mask of meta keys that are held.
* @param down Is this a key press (true) or release (false)?
* @param repeatCount Number of times a key down has repeated.
+ * @param flags event's flags.
* @return Returns true if the policy consumed the event and it should
* not be further dispatched.
*/
public boolean interceptKeyTi(WindowState win, int code,
- int metaKeys, boolean down, int repeatCount);
+ int metaKeys, boolean down, int repeatCount, int flags);
/**
* Called when layout of the windows is about to start.
@@ -792,6 +793,13 @@
public boolean performHapticFeedbackLw(WindowState win, int effectId, boolean always);
/**
+ * A special function that is called from the very low-level input queue
+ * to provide feedback to the user. Currently only called for virtual
+ * keys.
+ */
+ public void keyFeedbackFromInput(KeyEvent event);
+
+ /**
* Called when we have stopped keeping the screen on because a window
* requesting this is no longer visible.
*/
diff --git a/services/java/com/android/server/InputDevice.java b/services/java/com/android/server/InputDevice.java
index 3e98132..0ac5740 100644
--- a/services/java/com/android/server/InputDevice.java
+++ b/services/java/com/android/server/InputDevice.java
@@ -213,7 +213,7 @@
// We only consider the first pointer when computing the edge
// flags, since they are global to the event.
- if (action != MotionEvent.ACTION_DOWN) {
+ if (action == MotionEvent.ACTION_DOWN) {
if (scaled[MotionEvent.SAMPLE_X] <= 0) {
edgeFlags |= MotionEvent.EDGE_LEFT;
} else if (scaled[MotionEvent.SAMPLE_X] >= dispW) {
diff --git a/services/java/com/android/server/KeyInputQueue.java b/services/java/com/android/server/KeyInputQueue.java
index 17e9625..e0ee7ed 100644
--- a/services/java/com/android/server/KeyInputQueue.java
+++ b/services/java/com/android/server/KeyInputQueue.java
@@ -35,7 +35,6 @@
import com.android.internal.util.XmlUtils;
import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
import java.io.BufferedReader;
import java.io.File;
@@ -55,6 +54,7 @@
final SparseArray<InputDevice> mDevices = new SparseArray<InputDevice>();
final ArrayList<VirtualKey> mVirtualKeys = new ArrayList<VirtualKey>();
+ final HapticFeedbackCallback mHapticFeedbackCallback;
int mGlobalMetaState = 0;
boolean mHaveGlobalMetaState = false;
@@ -107,6 +107,10 @@
int filterEvent(QueuedEvent ev);
}
+ public interface HapticFeedbackCallback {
+ void virtualKeyFeedback(KeyEvent event);
+ }
+
static class QueuedEvent {
InputDevice inputDevice;
long whenNano;
@@ -264,11 +268,13 @@
}
}
- KeyInputQueue(Context context) {
+ KeyInputQueue(Context context, HapticFeedbackCallback hapticFeedbackCallback) {
if (MEASURE_LATENCY) {
lt = new LatencyTimer(100, 1000);
}
+ mHapticFeedbackCallback = hapticFeedbackCallback;
+
readVirtualKeys();
readExcludedDevices();
@@ -539,14 +545,40 @@
ms.mLastDown[0] = ms.mDown[0];
if (DEBUG_VIRTUAL_KEYS) Log.v(TAG,
"Generate key up for: " + vk.scancode);
+ KeyEvent event = newKeyEvent(di,
+ di.mKeyDownTime, curTime, false,
+ vk.lastKeycode,
+ 0, vk.scancode,
+ KeyEvent.FLAG_VIRTUAL_HARD_KEY);
+ mHapticFeedbackCallback.virtualKeyFeedback(event);
addLocked(di, curTimeNano, ev.flags,
RawInputEvent.CLASS_KEYBOARD,
- newKeyEvent(di, di.mKeyDownTime,
- curTime, false,
- vk.lastKeycode,
- 0, vk.scancode, 0));
+ event);
+ } else if (isInsideDisplay(di)) {
+ // Whoops the pointer has moved into
+ // the display area! Cancel the
+ // virtual key and start a pointer
+ // motion.
+ mPressedVirtualKey = null;
+ if (DEBUG_VIRTUAL_KEYS) Log.v(TAG,
+ "Cancel key up for: " + vk.scancode);
+ KeyEvent event = newKeyEvent(di,
+ di.mKeyDownTime, curTime, false,
+ vk.lastKeycode,
+ 0, vk.scancode,
+ KeyEvent.FLAG_CANCELED |
+ KeyEvent.FLAG_VIRTUAL_HARD_KEY);
+ mHapticFeedbackCallback.virtualKeyFeedback(event);
+ addLocked(di, curTimeNano, ev.flags,
+ RawInputEvent.CLASS_KEYBOARD,
+ event);
+ doMotion = true;
+ for (int i=InputDevice.MAX_POINTERS-1; i>=0; i--) {
+ ms.mLastDown[i] = false;
+ }
}
- } else if (ms.mDown[0] && !ms.mLastDown[0]) {
+ }
+ if (doMotion && ms.mDown[0] && !ms.mLastDown[0]) {
vk = findSoftButton(di);
if (vk != null) {
doMotion = false;
@@ -558,12 +590,15 @@
if (DEBUG_VIRTUAL_KEYS) Log.v(TAG,
"Generate key down for: " + vk.scancode
+ " (keycode=" + vk.lastKeycode + ")");
+ KeyEvent event = newKeyEvent(di,
+ di.mKeyDownTime, curTime, true,
+ vk.lastKeycode, 0,
+ vk.scancode,
+ KeyEvent.FLAG_VIRTUAL_HARD_KEY);
+ mHapticFeedbackCallback.virtualKeyFeedback(event);
addLocked(di, curTimeNano, ev.flags,
RawInputEvent.CLASS_KEYBOARD,
- newKeyEvent(di, di.mKeyDownTime,
- curTime, true,
- vk.lastKeycode, 0,
- vk.scancode, 0));
+ event);
}
}
@@ -618,17 +653,12 @@
}
};
- private VirtualKey findSoftButton(InputDevice dev) {
- final int N = mVirtualKeys.size();
- if (N <= 0) {
- return null;
- }
-
+ private boolean isInsideDisplay(InputDevice dev) {
final InputDevice.AbsoluteInfo absx = dev.absX;
final InputDevice.AbsoluteInfo absy = dev.absY;
final InputDevice.MotionState absm = dev.mAbs;
if (absx == null || absy == null || absm == null) {
- return null;
+ return true;
}
if (absm.mCurData[MotionEvent.SAMPLE_X] >= absx.minValue
@@ -639,9 +669,23 @@
+ absm.mCurData[MotionEvent.SAMPLE_X]
+ "," + absm.mCurData[MotionEvent.SAMPLE_Y]
+ ") inside of display");
+ return true;
+ }
+
+ return false;
+ }
+
+ private VirtualKey findSoftButton(InputDevice dev) {
+ final int N = mVirtualKeys.size();
+ if (N <= 0) {
return null;
}
+ if (isInsideDisplay(dev)) {
+ return null;
+ }
+
+ final InputDevice.MotionState absm = dev.mAbs;
for (int i=0; i<N; i++) {
VirtualKey sb = mVirtualKeys.get(i);
sb.computeHitRect(dev, mDisplayWidth, mDisplayHeight);
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index e1804a7..78ca831 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -125,7 +125,8 @@
import java.util.List;
/** {@hide} */
-public class WindowManagerService extends IWindowManager.Stub implements Watchdog.Monitor {
+public class WindowManagerService extends IWindowManager.Stub
+ implements Watchdog.Monitor, KeyInputQueue.HapticFeedbackCallback {
static final String TAG = "WindowManager";
static final boolean DEBUG = false;
static final boolean DEBUG_FOCUS = false;
@@ -4686,7 +4687,8 @@
callingPid, callingUid)
== PackageManager.PERMISSION_GRANTED) {
mPolicy.interceptKeyTi(null, keycode,
- nextKey.getMetaState(), down, repeatCount);
+ nextKey.getMetaState(), down, repeatCount,
+ nextKey.getFlags());
}
Log.w(TAG, "Event timeout during app switch: dropping "
+ nextKey);
@@ -4709,7 +4711,8 @@
callingPid, callingUid)
== PackageManager.PERMISSION_GRANTED) {
if (mPolicy.interceptKeyTi(focus,
- keycode, nextKey.getMetaState(), down, repeatCount)) {
+ keycode, nextKey.getMetaState(), down, repeatCount,
+ nextKey.getFlags())) {
return CONSUMED_EVENT_TOKEN;
}
}
@@ -5129,7 +5132,7 @@
PowerManager.WakeLock mHoldingScreen;
KeyQ() {
- super(mContext);
+ super(mContext, WindowManagerService.this);
PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
mHoldingScreen = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK,
"KEEP_SCREEN_ON_FLAG");
@@ -9202,6 +9205,10 @@
synchronized (mKeyWaiter) { }
}
+ public void virtualKeyFeedback(KeyEvent event) {
+ mPolicy.keyFeedbackFromInput(event);
+ }
+
/**
* DimAnimator class that controls the dim animation. This holds the surface and
* all state used for dim animation.