Merge "Fix [-] buttons in the notification builder test." into jb-mr2-dev
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 9bf8830..19d495b 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -371,9 +371,9 @@
return new DisplayManager(ctx.getOuterContext());
}});
- registerService(INPUT_METHOD_SERVICE, new ServiceFetcher() {
- public Object createService(ContextImpl ctx) {
- return InputMethodManager.getInstance(ctx);
+ registerService(INPUT_METHOD_SERVICE, new StaticServiceFetcher() {
+ public Object createStaticService() {
+ return InputMethodManager.getInstance();
}});
registerService(TEXT_SERVICES_MANAGER_SERVICE, new ServiceFetcher() {
diff --git a/core/java/android/app/NativeActivity.java b/core/java/android/app/NativeActivity.java
index 51867bc..7d8a36e 100644
--- a/core/java/android/app/NativeActivity.java
+++ b/core/java/android/app/NativeActivity.java
@@ -1,7 +1,20 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
package android.app;
-import com.android.internal.view.IInputMethodSession;
-
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
@@ -25,7 +38,6 @@
import android.view.inputmethod.InputMethodManager;
import java.io.File;
-import java.lang.ref.WeakReference;
/**
* Convenience for implementing an activity that will be implemented
@@ -65,7 +77,6 @@
private NativeContentView mNativeContentView;
private InputMethodManager mIMM;
- private InputMethodCallback mInputMethodCallback;
private int mNativeHandle;
@@ -117,22 +128,6 @@
super(context, attrs);
}
}
-
- static final class InputMethodCallback implements InputMethodManager.FinishedEventCallback {
- WeakReference<NativeActivity> mNa;
-
- InputMethodCallback(NativeActivity na) {
- mNa = new WeakReference<NativeActivity>(na);
- }
-
- @Override
- public void finishedEvent(int seq, boolean handled) {
- NativeActivity na = mNa.get();
- if (na != null) {
- na.finishPreDispatchKeyEventNative(na.mNativeHandle, seq, handled);
- }
- }
- }
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -141,7 +136,6 @@
ActivityInfo ai;
mIMM = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
- mInputMethodCallback = new InputMethodCallback(this);
getWindow().takeSurface(this);
getWindow().takeInputQueue(this);
@@ -353,7 +347,8 @@
}
void preDispatchKeyEvent(KeyEvent event, int seq) {
- mIMM.dispatchInputEvent(this, seq, event, mInputMethodCallback);
+ // FIXME: Input dispatch should be redirected back through ViewRootImpl again.
+ finishPreDispatchKeyEventNative(mNativeHandle, seq, false);
}
void setWindowFlags(int flags, int mask) {
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index 9c0064e..3342068 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -709,7 +709,7 @@
public void setWallpaperOffsets(IBinder windowToken, float xOffset, float yOffset) {
try {
//Log.v(TAG, "Sending new wallpaper offsets from app...");
- WindowManagerGlobal.getWindowSession(mContext.getMainLooper()).setWallpaperPosition(
+ WindowManagerGlobal.getWindowSession().setWallpaperPosition(
windowToken, xOffset, yOffset, mWallpaperXStep, mWallpaperYStep);
//Log.v(TAG, "...app returning after sending offsets!");
} catch (RemoteException e) {
@@ -747,7 +747,7 @@
int x, int y, int z, Bundle extras) {
try {
//Log.v(TAG, "Sending new wallpaper offsets from app...");
- WindowManagerGlobal.getWindowSession(mContext.getMainLooper()).sendWallpaperCommand(
+ WindowManagerGlobal.getWindowSession().sendWallpaperCommand(
windowToken, action, x, y, z, extras, false);
//Log.v(TAG, "...app returning after sending offsets!");
} catch (RemoteException e) {
@@ -767,7 +767,7 @@
*/
public void clearWallpaperOffsets(IBinder windowToken) {
try {
- WindowManagerGlobal.getWindowSession(mContext.getMainLooper()).setWallpaperPosition(
+ WindowManagerGlobal.getWindowSession().setWallpaperPosition(
windowToken, -1, -1, -1, -1);
} catch (RemoteException e) {
// Ignore.
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index ef9b0bf..03e241a 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -256,6 +256,12 @@
* Return the Looper for the main thread of the current process. This is
* the thread used to dispatch calls to application components (activities,
* services, etc).
+ * <p>
+ * By definition, this method returns the same result as would be obtained
+ * by calling {@link Looper#getMainLooper() Looper.getMainLooper()}.
+ * </p>
+ *
+ * @return The main looper.
*/
public abstract Looper getMainLooper();
diff --git a/core/java/android/os/LatencyTimer.java b/core/java/android/os/LatencyTimer.java
deleted file mode 100644
index ed2f0f9..0000000
--- a/core/java/android/os/LatencyTimer.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.os;
-
-import android.util.Log;
-
-import java.util.HashMap;
-
-/**
- * A class to help with measuring latency in your code.
- *
- * Suggested usage:
- * 1) Instanciate a LatencyTimer as a class field.
- * private [static] LatencyTimer mLt = new LatencyTimer(100, 1000);
- * 2) At various points in the code call sample with a string and the time delta to some fixed time.
- * The string should be unique at each point of the code you are measuring.
- * mLt.sample("before processing event", System.nanoTime() - event.getEventTimeNano());
- * processEvent(event);
- * mLt.sample("after processing event ", System.nanoTime() - event.getEventTimeNano());
- *
- * @hide
- */
-public final class LatencyTimer
-{
- final String TAG = "LatencyTimer";
- final int mSampleSize;
- final int mScaleFactor;
- volatile HashMap<String, long[]> store = new HashMap<String, long[]>();
-
- /**
- * Creates a LatencyTimer object
- * @param sampleSize number of samples to collect before printing out the average
- * @param scaleFactor divisor used to make each sample smaller to prevent overflow when
- * (sampleSize * average sample value)/scaleFactor > Long.MAX_VALUE
- */
- public LatencyTimer(int sampleSize, int scaleFactor) {
- if (scaleFactor == 0) {
- scaleFactor = 1;
- }
- mScaleFactor = scaleFactor;
- mSampleSize = sampleSize;
- }
-
- /**
- * Add a sample delay for averaging.
- * @param tag string used for printing out the result. This should be unique at each point of
- * this called.
- * @param delta time difference from an unique point of reference for a particular iteration
- */
- public void sample(String tag, long delta) {
- long[] array = getArray(tag);
-
- // array[mSampleSize] holds the number of used entries
- final int index = (int) array[mSampleSize]++;
- array[index] = delta;
- if (array[mSampleSize] == mSampleSize) {
- long totalDelta = 0;
- for (long d : array) {
- totalDelta += d/mScaleFactor;
- }
- array[mSampleSize] = 0;
- Log.i(TAG, tag + " average = " + totalDelta / mSampleSize);
- }
- }
-
- private long[] getArray(String tag) {
- long[] data = store.get(tag);
- if (data == null) {
- synchronized(store) {
- data = store.get(tag);
- if (data == null) {
- data = new long[mSampleSize + 1];
- store.put(tag, data);
- data[mSampleSize] = 0;
- }
- }
- }
- return data;
- }
-}
diff --git a/core/java/android/os/Looper.java b/core/java/android/os/Looper.java
index 38f4d5e..363a1bf 100644
--- a/core/java/android/os/Looper.java
+++ b/core/java/android/os/Looper.java
@@ -192,6 +192,14 @@
}
/**
+ * Returns true if the current thread is this looper's thread.
+ * @hide
+ */
+ public boolean isCurrentThread() {
+ return Thread.currentThread() == mThread;
+ }
+
+ /**
* Quits the looper.
*
* Causes the {@link #loop} method to terminate as soon as possible.
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 71d8fb6..5db8168 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -762,7 +762,7 @@
mWindowToken = wrapper.mWindowToken;
mSurfaceHolder.setSizeFromLayout();
mInitializing = true;
- mSession = WindowManagerGlobal.getWindowSession(getMainLooper());
+ mSession = WindowManagerGlobal.getWindowSession();
mWindow.setSession(mSession);
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index a520e17..7c82f7e 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -14060,6 +14060,10 @@
* managed: remove them when they should not be displayed anymore. The
* overlay will always have the same size as its host view.
*
+ * <p>Note: Overlays do not currently work correctly with {@link
+ * SurfaceView} or {@link TextureView}; contents in overlays for these
+ * types of views may not display correctly.</p>
+ *
* @return The ViewOverlay object for this view.
* @see ViewOverlay
*/
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index c07191a..311d1d0 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -2975,6 +2975,10 @@
* representation of a view in a parent container, such as might be used
* by an animation effect.
*
+ * <p>Note: Overlays do not currently work correctly with {@link
+ * SurfaceView} or {@link TextureView}; contents in overlays for these
+ * types of views may not display correctly.</p>
+ *
* @return The ViewGroupOverlay object for this view.
* @see ViewGroupOverlay
*/
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index efa8a9e..22586f6 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -42,7 +42,6 @@
import android.os.Bundle;
import android.os.Debug;
import android.os.Handler;
-import android.os.LatencyTimer;
import android.os.Looper;
import android.os.Message;
import android.os.ParcelFileDescriptor;
@@ -116,9 +115,6 @@
* at 60 Hz. This can be used to measure the potential framerate.
*/
private static final String PROPERTY_PROFILE_RENDERING = "viewancestor.profile_rendering";
-
- private static final boolean MEASURE_LATENCY = false;
- private static LatencyTimer lt;
/**
* Maximum time we allow the user to roll the trackball enough to generate
@@ -138,26 +134,15 @@
private static boolean sRenderThreadQueried = false;
private static final Object[] sRenderThreadQueryLock = new Object[0];
+ final Context mContext;
final IWindowSession mWindowSession;
final Display mDisplay;
final String mBasePackageName;
- long mLastTrackballTime = 0;
- final TrackballAxis mTrackballAxisX = new TrackballAxis();
- final TrackballAxis mTrackballAxisY = new TrackballAxis();
-
- final SimulatedDpad mSimulatedDpad;
-
- int mLastJoystickXDirection;
- int mLastJoystickYDirection;
- int mLastJoystickXKeyCode;
- int mLastJoystickYKeyCode;
-
final int[] mTmpLocation = new int[2];
final TypedValue mTmpValue = new TypedValue();
-
- final InputMethodCallback mInputMethodCallback;
+
final Thread mThread;
final WindowLeaked mLocation;
@@ -231,38 +216,23 @@
int mClientWindowLayoutFlags;
boolean mLastOverscanRequested;
- /** Event was not handled and is finished.
- * @hide */
- public static final int EVENT_NOT_HANDLED = 0;
- /** Event was handled and is finished.
- * @hide */
- public static final int EVENT_HANDLED = 1;
- /** Event is waiting on the IME.
- * @hide */
- public static final int EVENT_PENDING_IME = 2;
- /** Event requires post-IME dispatch.
- * @hide */
- public static final int EVENT_POST_IME = 3;
-
// Pool of queued input events.
private static final int MAX_QUEUED_INPUT_EVENT_POOL_SIZE = 10;
private QueuedInputEvent mQueuedInputEventPool;
private int mQueuedInputEventPoolSize;
/* Input event queue.
- * Pending input events are input events waiting to be handled by the application. Current
- * input events are input events which are being handled but are waiting on some action by the
- * IME, even if they themselves may not need to be handled by the IME.
+ * Pending input events are input events waiting to be delivered to the input stages
+ * and handled by the application.
*/
QueuedInputEvent mPendingInputEventHead;
QueuedInputEvent mPendingInputEventTail;
int mPendingInputEventCount;
- QueuedInputEvent mActiveInputEventHead;
- QueuedInputEvent mActiveInputEventTail;
- int mActiveInputEventCount;
boolean mProcessInputEventsScheduled;
String mPendingInputEventQueueLengthCounterName = "pq";
- String mActiveInputEventQueueLengthCounterName = "aq";
+
+ InputStage mFirstInputStage;
+ InputStage mFirstPostImeInputStage;
boolean mWindowAttributesChanged = false;
int mWindowAttributesChangesFlag = 0;
@@ -361,18 +331,8 @@
}
public ViewRootImpl(Context context, Display display) {
- super();
-
- if (MEASURE_LATENCY) {
- if (lt == null) {
- lt = new LatencyTimer(100, 1000);
- }
- }
-
- // Initialize the statics when this class is first instantiated. This is
- // done here instead of in the static block because Zygote does not
- // allow the spawning of threads.
- mWindowSession = WindowManagerGlobal.getWindowSession(context.getMainLooper());
+ mContext = context;
+ mWindowSession = WindowManagerGlobal.getWindowSession();
mDisplay = display;
mBasePackageName = context.getBasePackageName();
@@ -390,7 +350,6 @@
mWinFrame = new Rect();
mWindow = new W(this);
mTargetSdkVersion = context.getApplicationInfo().targetSdkVersion;
- mInputMethodCallback = new InputMethodCallback(this);
mViewVisibility = View.GONE;
mTransparentRegion = new Region();
mPreviousTransparentRegion = new Region();
@@ -411,7 +370,6 @@
PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
mAttachInfo.mScreenOn = powerManager.isScreenOn();
loadSystemProperties();
- mSimulatedDpad = new SimulatedDpad(context);
}
/**
@@ -659,8 +617,22 @@
view.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
}
- mPendingInputEventQueueLengthCounterName = "pq:" + attrs.getTitle();
- mActiveInputEventQueueLengthCounterName = "aq:" + attrs.getTitle();
+ // Set up the input pipeline.
+ CharSequence counterSuffix = attrs.getTitle();
+ InputStage syntheticStage = new SyntheticInputStage();
+ InputStage viewPostImeStage = new ViewPostImeInputStage(syntheticStage);
+ InputStage nativePostImeStage = new NativePostImeInputStage(viewPostImeStage,
+ "aq:native-post-ime:" + counterSuffix);
+ InputStage earlyPostImeStage = new EarlyPostImeInputStage(nativePostImeStage);
+ InputStage imeStage = new ImeInputStage(earlyPostImeStage,
+ "aq:ime:" + counterSuffix);
+ InputStage viewPreImeStage = new ViewPreImeInputStage(imeStage);
+ InputStage nativePreImeStage = new NativePreImeInputStage(viewPreImeStage,
+ "aq:native-pre-ime:" + counterSuffix);
+
+ mFirstInputStage = nativePreImeStage;
+ mFirstPostImeInputStage = earlyPostImeStage;
+ mPendingInputEventQueueLengthCounterName = "aq:pending:" + counterSuffix;
}
}
}
@@ -2859,7 +2831,7 @@
mWindowSession.remove(mWindow);
} catch (RemoteException e) {
}
-
+
// Dispose the input channel after removing the window so the Window Manager
// doesn't interpret the input channel being closed as an abnormal termination.
if (mInputChannel != null) {
@@ -3362,365 +3334,822 @@
return false;
}
- private int deliverInputEvent(QueuedInputEvent q) {
- Trace.traceBegin(Trace.TRACE_TAG_VIEW, "deliverInputEvent");
- try {
+ /**
+ * Base class for implementing a stage in the chain of responsibility
+ * for processing input events.
+ * <p>
+ * Events are delivered to the stage by the {@link #deliver} method. The stage
+ * then has the choice of finishing the event or forwarding it to the next stage.
+ * </p>
+ */
+ abstract class InputStage {
+ private final InputStage mNext;
+
+ protected static final int FORWARD = 0;
+ protected static final int FINISH_HANDLED = 1;
+ protected static final int FINISH_NOT_HANDLED = 2;
+
+ /**
+ * Creates an input stage.
+ * @param next The next stage to which events should be forwarded.
+ */
+ public InputStage(InputStage next) {
+ mNext = next;
+ }
+
+ /**
+ * Delivers an event to be processed.
+ */
+ public final void deliver(QueuedInputEvent q) {
+ if ((q.mFlags & QueuedInputEvent.FLAG_FINISHED) != 0) {
+ forward(q);
+ } else if (mView == null || !mAdded) {
+ finish(q, false);
+ } else {
+ apply(q, onProcess(q));
+ }
+ }
+
+ /**
+ * Marks the the input event as finished then forwards it to the next stage.
+ */
+ protected void finish(QueuedInputEvent q, boolean handled) {
+ q.mFlags |= QueuedInputEvent.FLAG_FINISHED;
+ if (handled) {
+ q.mFlags |= QueuedInputEvent.FLAG_FINISHED_HANDLED;
+ }
+ forward(q);
+ }
+
+ /**
+ * Forwards the event to the next stage.
+ */
+ protected void forward(QueuedInputEvent q) {
+ onDeliverToNext(q);
+ }
+
+ /**
+ * Applies a result code from {@link #onProcess} to the specified event.
+ */
+ protected void apply(QueuedInputEvent q, int result) {
+ if (result == FORWARD) {
+ forward(q);
+ } else if (result == FINISH_HANDLED) {
+ finish(q, true);
+ } else if (result == FINISH_NOT_HANDLED) {
+ finish(q, false);
+ } else {
+ throw new IllegalArgumentException("Invalid result: " + result);
+ }
+ }
+
+ /**
+ * Called when an event is ready to be processed.
+ * @return A result code indicating how the event was handled.
+ */
+ protected int onProcess(QueuedInputEvent q) {
+ return FORWARD;
+ }
+
+ /**
+ * Called when an event is being delivered to the next stage.
+ */
+ protected void onDeliverToNext(QueuedInputEvent q) {
+ if (mNext != null) {
+ mNext.deliver(q);
+ } else {
+ finishInputEvent(q);
+ }
+ }
+ }
+
+ /**
+ * Base class for implementing an input pipeline stage that supports
+ * asynchronous and out-of-order processing of input events.
+ * <p>
+ * In addition to what a normal input stage can do, an asynchronous
+ * input stage may also defer an input event that has been delivered to it
+ * and finish or forward it later.
+ * </p>
+ */
+ abstract class AsyncInputStage extends InputStage {
+ private final String mTraceCounter;
+
+ private QueuedInputEvent mQueueHead;
+ private QueuedInputEvent mQueueTail;
+ private int mQueueLength;
+
+ protected static final int DEFER = 3;
+
+ /**
+ * Creates an asynchronous input stage.
+ * @param next The next stage to which events should be forwarded.
+ * @param traceCounter The name of a counter to record the size of
+ * the queue of pending events.
+ */
+ public AsyncInputStage(InputStage next, String traceCounter) {
+ super(next);
+ mTraceCounter = traceCounter;
+ }
+
+ /**
+ * Marks the event as deferred, which is to say that it will be handled
+ * asynchronously. The caller is responsible for calling {@link #forward}
+ * or {@link #finish} later when it is done handling the event.
+ */
+ protected void defer(QueuedInputEvent q) {
+ q.mFlags |= QueuedInputEvent.FLAG_DEFERRED;
+ enqueue(q);
+ }
+
+ @Override
+ protected void forward(QueuedInputEvent q) {
+ // Clear the deferred flag.
+ q.mFlags &= ~QueuedInputEvent.FLAG_DEFERRED;
+
+ // Fast path if the queue is empty.
+ QueuedInputEvent curr = mQueueHead;
+ if (curr == null) {
+ super.forward(q);
+ return;
+ }
+
+ // Determine whether the event must be serialized behind any others
+ // before it can be delivered to the next stage. This is done because
+ // deferred events might be handled out of order by the stage.
+ final int deviceId = q.mEvent.getDeviceId();
+ QueuedInputEvent prev = null;
+ boolean blocked = false;
+ while (curr != null && curr != q) {
+ if (!blocked && deviceId == curr.mEvent.getDeviceId()) {
+ blocked = true;
+ }
+ prev = curr;
+ curr = curr.mNext;
+ }
+
+ // If the event is blocked, then leave it in the queue to be delivered later.
+ // Note that the event might not yet be in the queue if it was not previously
+ // deferred so we will enqueue it if needed.
+ if (blocked) {
+ if (curr == null) {
+ enqueue(q);
+ }
+ return;
+ }
+
+ // The event is not blocked. Deliver it immediately.
+ if (curr != null) {
+ curr = curr.mNext;
+ dequeue(q, prev);
+ }
+ super.forward(q);
+
+ // Dequeuing this event may have unblocked successors. Deliver them.
+ while (curr != null) {
+ if (deviceId == curr.mEvent.getDeviceId()) {
+ if ((curr.mFlags & QueuedInputEvent.FLAG_DEFERRED) != 0) {
+ break;
+ }
+ QueuedInputEvent next = curr.mNext;
+ dequeue(curr, prev);
+ super.forward(curr);
+ curr = next;
+ } else {
+ prev = curr;
+ curr = curr.mNext;
+ }
+ }
+ }
+
+ @Override
+ protected void apply(QueuedInputEvent q, int result) {
+ if (result == DEFER) {
+ defer(q);
+ } else {
+ super.apply(q, result);
+ }
+ }
+
+ private void enqueue(QueuedInputEvent q) {
+ if (mQueueTail == null) {
+ mQueueHead = q;
+ mQueueTail = q;
+ } else {
+ mQueueTail.mNext = q;
+ mQueueTail = q;
+ }
+
+ mQueueLength += 1;
+ Trace.traceCounter(Trace.TRACE_TAG_INPUT, mTraceCounter, mQueueLength);
+ }
+
+ private void dequeue(QueuedInputEvent q, QueuedInputEvent prev) {
+ if (prev == null) {
+ mQueueHead = q.mNext;
+ } else {
+ prev.mNext = q.mNext;
+ }
+ if (mQueueTail == q) {
+ mQueueTail = prev;
+ }
+ q.mNext = null;
+
+ mQueueLength -= 1;
+ Trace.traceCounter(Trace.TRACE_TAG_INPUT, mTraceCounter, mQueueLength);
+ }
+ }
+
+ /**
+ * Delivers pre-ime input events to a native activity.
+ * Does not support pointer events.
+ */
+ final class NativePreImeInputStage extends AsyncInputStage {
+ public NativePreImeInputStage(InputStage next, String traceCounter) {
+ super(next, traceCounter);
+ }
+
+ @Override
+ protected int onProcess(QueuedInputEvent q) {
+ return FORWARD;
+ }
+ }
+
+ /**
+ * Delivers pre-ime input events to the view hierarchy.
+ * Does not support pointer events.
+ */
+ final class ViewPreImeInputStage extends InputStage {
+ public ViewPreImeInputStage(InputStage next) {
+ super(next);
+ }
+
+ @Override
+ protected int onProcess(QueuedInputEvent q) {
if (q.mEvent instanceof KeyEvent) {
- return deliverKeyEvent(q);
+ return processKeyEvent(q);
+ }
+ return FORWARD;
+ }
+
+ private int processKeyEvent(QueuedInputEvent q) {
+ final KeyEvent event = (KeyEvent)q.mEvent;
+ if (mView.dispatchKeyEventPreIme(event)) {
+ return FINISH_HANDLED;
+ }
+ return FORWARD;
+ }
+ }
+
+ /**
+ * Delivers input events to the ime.
+ * Does not support pointer events.
+ */
+ final class ImeInputStage extends AsyncInputStage
+ implements InputMethodManager.FinishedInputEventCallback {
+ public ImeInputStage(InputStage next, String traceCounter) {
+ super(next, traceCounter);
+ }
+
+ @Override
+ protected int onProcess(QueuedInputEvent q) {
+ if (mLastWasImTarget) {
+ InputMethodManager imm = InputMethodManager.peekInstance();
+ if (imm != null) {
+ final InputEvent event = q.mEvent;
+ if (DEBUG_IMF) Log.v(TAG, "Sending input event to IME: " + event);
+ int result = imm.dispatchInputEvent(event, q, this, mHandler);
+ if (result == InputMethodManager.DISPATCH_HANDLED) {
+ return FINISH_HANDLED;
+ } else if (result == InputMethodManager.DISPATCH_NOT_HANDLED) {
+ return FINISH_NOT_HANDLED;
+ } else {
+ return DEFER; // callback will be invoked later
+ }
+ }
+ }
+ return FORWARD;
+ }
+
+ @Override
+ public void onFinishedInputEvent(Object token, boolean handled) {
+ QueuedInputEvent q = (QueuedInputEvent)token;
+ if (handled) {
+ finish(q, true);
+ return;
+ }
+
+ // If the window doesn't currently have input focus, then drop
+ // this event. This could be an event that came back from the
+ // IME dispatch but the window has lost focus in the meantime.
+ if (!mAttachInfo.mHasWindowFocus && !isTerminalInputEvent(q.mEvent)) {
+ Slog.w(TAG, "Dropping event due to no window focus: " + q.mEvent);
+ finish(q, false);
+ return;
+ }
+
+ forward(q);
+ }
+ }
+
+ /**
+ * Performs early processing of post-ime input events.
+ */
+ final class EarlyPostImeInputStage extends InputStage {
+ public EarlyPostImeInputStage(InputStage next) {
+ super(next);
+ }
+
+ @Override
+ protected int onProcess(QueuedInputEvent q) {
+ if (q.mEvent instanceof KeyEvent) {
+ return processKeyEvent(q);
} else {
final int source = q.mEvent.getSource();
if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
- return deliverPointerEvent(q);
- } else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
- return deliverTrackballEvent(q);
- } else {
- return deliverGenericMotionEvent(q);
+ return processPointerEvent(q);
}
}
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+ return FORWARD;
+ }
+
+ private int processKeyEvent(QueuedInputEvent q) {
+ final KeyEvent event = (KeyEvent)q.mEvent;
+
+ // If the key's purpose is to exit touch mode then we consume it
+ // and consider it handled.
+ if (checkForLeavingTouchModeAndConsume(event)) {
+ return FINISH_HANDLED;
+ }
+
+ // Make sure the fallback event policy sees all keys that will be
+ // delivered to the view hierarchy.
+ mFallbackEventHandler.preDispatchKeyEvent(event);
+ return FORWARD;
+ }
+
+ private int processPointerEvent(QueuedInputEvent q) {
+ final MotionEvent event = (MotionEvent)q.mEvent;
+
+ // Translate the pointer event for compatibility, if needed.
+ if (mTranslator != null) {
+ mTranslator.translateEventInScreenToAppWindow(event);
+ }
+
+ // Enter touch mode on down or scroll.
+ final int action = event.getAction();
+ if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_SCROLL) {
+ ensureTouchMode(true);
+ }
+
+ // Offset the scroll position.
+ if (mCurScrollY != 0) {
+ event.offsetLocation(0, mCurScrollY);
+ }
+
+ // Remember the touch position for possible drag-initiation.
+ if (event.isTouchEvent()) {
+ mLastTouchPoint.x = event.getRawX();
+ mLastTouchPoint.y = event.getRawY();
+ }
+ return FORWARD;
}
}
- private int deliverInputEventPostIme(QueuedInputEvent q) {
- Trace.traceBegin(Trace.TRACE_TAG_VIEW, "deliverInputEventPostIme");
- try {
+ /**
+ * Delivers post-ime input events to a native activity.
+ */
+ final class NativePostImeInputStage extends AsyncInputStage {
+ public NativePostImeInputStage(InputStage next, String traceCounter) {
+ super(next, traceCounter);
+ }
+
+ @Override
+ protected int onProcess(QueuedInputEvent q) {
+ return FORWARD;
+ }
+ }
+
+ /**
+ * Delivers post-ime input events to the view hierarchy.
+ */
+ final class ViewPostImeInputStage extends InputStage {
+ public ViewPostImeInputStage(InputStage next) {
+ super(next);
+ }
+
+ @Override
+ protected int onProcess(QueuedInputEvent q) {
if (q.mEvent instanceof KeyEvent) {
- return deliverKeyEventPostIme(q);
+ return processKeyEvent(q);
} else {
final int source = q.mEvent.getSource();
- if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
- return deliverTrackballEventPostIme(q);
+ if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
+ return processPointerEvent(q);
+ } else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
+ return processTrackballEvent(q);
} else {
- return deliverGenericMotionEventPostIme(q);
+ return processGenericMotionEvent(q);
}
}
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
- }
- private int deliverPointerEvent(QueuedInputEvent q) {
- final MotionEvent event = (MotionEvent)q.mEvent;
- final boolean isTouchEvent = event.isTouchEvent();
- if (mInputEventConsistencyVerifier != null) {
- if (isTouchEvent) {
- mInputEventConsistencyVerifier.onTouchEvent(event, 0);
- } else {
- mInputEventConsistencyVerifier.onGenericMotionEvent(event, 0);
+ private int processKeyEvent(QueuedInputEvent q) {
+ final KeyEvent event = (KeyEvent)q.mEvent;
+
+ // Deliver the key to the view hierarchy.
+ if (mView.dispatchKeyEvent(event)) {
+ return FINISH_HANDLED;
}
+
+ // If the Control modifier is held, try to interpret the key as a shortcut.
+ if (event.getAction() == KeyEvent.ACTION_DOWN
+ && event.isCtrlPressed()
+ && event.getRepeatCount() == 0
+ && !KeyEvent.isModifierKey(event.getKeyCode())) {
+ if (mView.dispatchKeyShortcutEvent(event)) {
+ return FINISH_HANDLED;
+ }
+ }
+
+ // Apply the fallback event policy.
+ if (mFallbackEventHandler.dispatchKeyEvent(event)) {
+ return FINISH_HANDLED;
+ }
+
+ // Handle automatic focus changes.
+ if (event.getAction() == KeyEvent.ACTION_DOWN) {
+ int direction = 0;
+ switch (event.getKeyCode()) {
+ case KeyEvent.KEYCODE_DPAD_LEFT:
+ if (event.hasNoModifiers()) {
+ direction = View.FOCUS_LEFT;
+ }
+ break;
+ case KeyEvent.KEYCODE_DPAD_RIGHT:
+ if (event.hasNoModifiers()) {
+ direction = View.FOCUS_RIGHT;
+ }
+ break;
+ case KeyEvent.KEYCODE_DPAD_UP:
+ if (event.hasNoModifiers()) {
+ direction = View.FOCUS_UP;
+ }
+ break;
+ case KeyEvent.KEYCODE_DPAD_DOWN:
+ if (event.hasNoModifiers()) {
+ direction = View.FOCUS_DOWN;
+ }
+ break;
+ case KeyEvent.KEYCODE_TAB:
+ if (event.hasNoModifiers()) {
+ direction = View.FOCUS_FORWARD;
+ } else if (event.hasModifiers(KeyEvent.META_SHIFT_ON)) {
+ direction = View.FOCUS_BACKWARD;
+ }
+ break;
+ }
+ if (direction != 0) {
+ View focused = mView.findFocus();
+ if (focused != null) {
+ View v = focused.focusSearch(direction);
+ if (v != null && v != focused) {
+ // do the math the get the interesting rect
+ // of previous focused into the coord system of
+ // newly focused view
+ focused.getFocusedRect(mTempRect);
+ if (mView instanceof ViewGroup) {
+ ((ViewGroup) mView).offsetDescendantRectToMyCoords(
+ focused, mTempRect);
+ ((ViewGroup) mView).offsetRectIntoDescendantCoords(
+ v, mTempRect);
+ }
+ if (v.requestFocus(direction, mTempRect)) {
+ playSoundEffect(SoundEffectConstants
+ .getContantForFocusDirection(direction));
+ return FINISH_HANDLED;
+ }
+ }
+
+ // Give the focused view a last chance to handle the dpad key.
+ if (mView.dispatchUnhandledMove(focused, direction)) {
+ return FINISH_HANDLED;
+ }
+ } else {
+ // find the best view to give focus to in this non-touch-mode with no-focus
+ View v = focusSearch(null, direction);
+ if (v != null && v.requestFocus(direction)) {
+ return FINISH_HANDLED;
+ }
+ }
+ }
+ }
+ return FORWARD;
}
- // If there is no view, then the event will not be handled.
- if (mView == null || !mAdded) {
- return EVENT_NOT_HANDLED;
+ private int processPointerEvent(QueuedInputEvent q) {
+ final MotionEvent event = (MotionEvent)q.mEvent;
+
+ if (mView.dispatchPointerEvent(event)) {
+ return FINISH_HANDLED;
+ }
+ return FORWARD;
}
- // Translate the pointer event for compatibility, if needed.
- if (mTranslator != null) {
- mTranslator.translateEventInScreenToAppWindow(event);
+ private int processTrackballEvent(QueuedInputEvent q) {
+ final MotionEvent event = (MotionEvent)q.mEvent;
+
+ if (mView.dispatchTrackballEvent(event)) {
+ return FINISH_HANDLED;
+ }
+ return FORWARD;
}
- // Enter touch mode on down or scroll.
- final int action = event.getAction();
- if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_SCROLL) {
- ensureTouchMode(true);
- }
+ private int processGenericMotionEvent(QueuedInputEvent q) {
+ final MotionEvent event = (MotionEvent)q.mEvent;
- // Offset the scroll position.
- if (mCurScrollY != 0) {
- event.offsetLocation(0, mCurScrollY);
+ // Deliver the event to the view.
+ if (mView.dispatchGenericMotionEvent(event)) {
+ return FINISH_HANDLED;
+ }
+ return FORWARD;
}
- if (MEASURE_LATENCY) {
- lt.sample("A Dispatching PointerEvents", System.nanoTime() - event.getEventTimeNano());
- }
-
- // Remember the touch position for possible drag-initiation.
- if (isTouchEvent) {
- mLastTouchPoint.x = event.getRawX();
- mLastTouchPoint.y = event.getRawY();
- }
-
- // Dispatch touch to view hierarchy.
- boolean handled = mView.dispatchPointerEvent(event);
- if (MEASURE_LATENCY) {
- lt.sample("B Dispatched PointerEvents ", System.nanoTime() - event.getEventTimeNano());
- }
- return handled ? EVENT_HANDLED : EVENT_NOT_HANDLED;
}
- private int deliverTrackballEvent(QueuedInputEvent q) {
- final MotionEvent event = (MotionEvent)q.mEvent;
- if (mInputEventConsistencyVerifier != null) {
- mInputEventConsistencyVerifier.onTrackballEvent(event, 0);
+ /**
+ * Performs default processing of unhandled input events.
+ */
+ final class SyntheticInputStage extends InputStage {
+ private final TrackballAxis mTrackballAxisX = new TrackballAxis();
+ private final TrackballAxis mTrackballAxisY = new TrackballAxis();
+ private long mLastTrackballTime;
+
+ private int mLastJoystickXDirection;
+ private int mLastJoystickYDirection;
+ private int mLastJoystickXKeyCode;
+ private int mLastJoystickYKeyCode;
+
+ private SimulatedDpad mSimulatedDpad;
+
+ public SyntheticInputStage() {
+ super(null);
+ mSimulatedDpad = new SimulatedDpad(mContext);
}
- int result = EVENT_POST_IME;
- if (mView != null && mAdded && (q.mFlags & QueuedInputEvent.FLAG_DELIVER_POST_IME) == 0) {
- if (LOCAL_LOGV)
- Log.v(TAG, "Dispatching trackball " + event + " to " + mView);
-
- // Dispatch to the IME before propagating down the view hierarchy.
- result = dispatchImeInputEvent(q);
- }
- return result;
- }
-
- private int deliverTrackballEventPostIme(QueuedInputEvent q) {
- final MotionEvent event = (MotionEvent) q.mEvent;
-
- // If there is no view, then the event will not be handled.
- if (mView == null || !mAdded) {
- return EVENT_NOT_HANDLED;
+ @Override
+ protected int onProcess(QueuedInputEvent q) {
+ q.mFlags |= QueuedInputEvent.FLAG_RESYNTHESIZED;
+ if (q.mEvent instanceof MotionEvent) {
+ final int source = q.mEvent.getSource();
+ if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
+ return processTrackballEvent(q);
+ } else if ((source & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
+ return processJoystickEvent(q);
+ } else if ((source & InputDevice.SOURCE_TOUCH_NAVIGATION)
+ == InputDevice.SOURCE_TOUCH_NAVIGATION) {
+ return processTouchNavigationEvent(q);
+ }
+ }
+ return FORWARD;
}
- // Deliver the trackball event to the view.
- if (mView.dispatchTrackballEvent(event)) {
- // If we reach this, we delivered a trackball event to mView and
- // mView consumed it. Because we will not translate the trackball
- // event into a key event, touch mode will not exit, so we exit
- // touch mode here.
- ensureTouchMode(false);
+ @Override
+ protected void onDeliverToNext(QueuedInputEvent q) {
+ if ((q.mFlags & QueuedInputEvent.FLAG_RESYNTHESIZED) == 0) {
+ // Cancel related synthetic events if any prior stage has handled the event.
+ if (q.mEvent instanceof MotionEvent) {
+ final int source = q.mEvent.getSource();
+ if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
+ cancelTrackballEvent(q);
+ } else if ((source & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
+ cancelJoystickEvent(q);
+ } else if ((source & InputDevice.SOURCE_TOUCH_NAVIGATION)
+ == InputDevice.SOURCE_TOUCH_NAVIGATION) {
+ cancelTouchNavigationEvent(q);
+ }
+ }
+ }
+ super.onDeliverToNext(q);
+ }
+
+ private int processTrackballEvent(QueuedInputEvent q) {
+ final MotionEvent event = (MotionEvent)q.mEvent;
+
+ // Translate the trackball event into DPAD keys and try to deliver those.
+ final TrackballAxis x = mTrackballAxisX;
+ final TrackballAxis y = mTrackballAxisY;
+ long curTime = SystemClock.uptimeMillis();
+ if ((mLastTrackballTime + MAX_TRACKBALL_DELAY) < curTime) {
+ // It has been too long since the last movement,
+ // so restart at the beginning.
+ x.reset(0);
+ y.reset(0);
+ mLastTrackballTime = curTime;
+ }
+
+ final int action = event.getAction();
+ final int metaState = event.getMetaState();
+ switch (action) {
+ case MotionEvent.ACTION_DOWN:
+ x.reset(2);
+ y.reset(2);
+ enqueueInputEvent(new KeyEvent(curTime, curTime,
+ KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_CENTER, 0, metaState,
+ KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK,
+ InputDevice.SOURCE_KEYBOARD));
+ break;
+ case MotionEvent.ACTION_UP:
+ x.reset(2);
+ y.reset(2);
+ enqueueInputEvent(new KeyEvent(curTime, curTime,
+ KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DPAD_CENTER, 0, metaState,
+ KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK,
+ InputDevice.SOURCE_KEYBOARD));
+ break;
+ }
+
+ if (DEBUG_TRACKBALL) Log.v(TAG, "TB X=" + x.position + " step="
+ + x.step + " dir=" + x.dir + " acc=" + x.acceleration
+ + " move=" + event.getX()
+ + " / Y=" + y.position + " step="
+ + y.step + " dir=" + y.dir + " acc=" + y.acceleration
+ + " move=" + event.getY());
+ final float xOff = x.collect(event.getX(), event.getEventTime(), "X");
+ final float yOff = y.collect(event.getY(), event.getEventTime(), "Y");
+
+ // Generate DPAD events based on the trackball movement.
+ // We pick the axis that has moved the most as the direction of
+ // the DPAD. When we generate DPAD events for one axis, then the
+ // other axis is reset -- we don't want to perform DPAD jumps due
+ // to slight movements in the trackball when making major movements
+ // along the other axis.
+ int keycode = 0;
+ int movement = 0;
+ float accel = 1;
+ if (xOff > yOff) {
+ movement = x.generate();
+ if (movement != 0) {
+ keycode = movement > 0 ? KeyEvent.KEYCODE_DPAD_RIGHT
+ : KeyEvent.KEYCODE_DPAD_LEFT;
+ accel = x.acceleration;
+ y.reset(2);
+ }
+ } else if (yOff > 0) {
+ movement = y.generate();
+ if (movement != 0) {
+ keycode = movement > 0 ? KeyEvent.KEYCODE_DPAD_DOWN
+ : KeyEvent.KEYCODE_DPAD_UP;
+ accel = y.acceleration;
+ x.reset(2);
+ }
+ }
+
+ if (keycode != 0) {
+ if (movement < 0) movement = -movement;
+ int accelMovement = (int)(movement * accel);
+ if (DEBUG_TRACKBALL) Log.v(TAG, "Move: movement=" + movement
+ + " accelMovement=" + accelMovement
+ + " accel=" + accel);
+ if (accelMovement > movement) {
+ if (DEBUG_TRACKBALL) Log.v(TAG, "Delivering fake DPAD: "
+ + keycode);
+ movement--;
+ int repeatCount = accelMovement - movement;
+ enqueueInputEvent(new KeyEvent(curTime, curTime,
+ KeyEvent.ACTION_MULTIPLE, keycode, repeatCount, metaState,
+ KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK,
+ InputDevice.SOURCE_KEYBOARD));
+ }
+ while (movement > 0) {
+ if (DEBUG_TRACKBALL) Log.v(TAG, "Delivering fake DPAD: "
+ + keycode);
+ movement--;
+ curTime = SystemClock.uptimeMillis();
+ enqueueInputEvent(new KeyEvent(curTime, curTime,
+ KeyEvent.ACTION_DOWN, keycode, 0, metaState,
+ KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK,
+ InputDevice.SOURCE_KEYBOARD));
+ enqueueInputEvent(new KeyEvent(curTime, curTime,
+ KeyEvent.ACTION_UP, keycode, 0, metaState,
+ KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK,
+ InputDevice.SOURCE_KEYBOARD));
+ }
+ mLastTrackballTime = curTime;
+ }
+
+ // Unfortunately we can't tell whether the application consumed the keys, so
+ // we always consider the trackball event handled.
+ return FINISH_HANDLED;
+ }
+
+ private void cancelTrackballEvent(QueuedInputEvent q) {
mLastTrackballTime = Integer.MIN_VALUE;
- return EVENT_HANDLED;
- }
- // Translate the trackball event into DPAD keys and try to deliver those.
- final TrackballAxis x = mTrackballAxisX;
- final TrackballAxis y = mTrackballAxisY;
-
- long curTime = SystemClock.uptimeMillis();
- if ((mLastTrackballTime + MAX_TRACKBALL_DELAY) < curTime) {
- // It has been too long since the last movement,
- // so restart at the beginning.
- x.reset(0);
- y.reset(0);
- mLastTrackballTime = curTime;
- }
-
- final int action = event.getAction();
- final int metaState = event.getMetaState();
- switch (action) {
- case MotionEvent.ACTION_DOWN:
- x.reset(2);
- y.reset(2);
- enqueueInputEvent(new KeyEvent(curTime, curTime,
- KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_CENTER, 0, metaState,
- KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK,
- InputDevice.SOURCE_KEYBOARD));
- break;
- case MotionEvent.ACTION_UP:
- x.reset(2);
- y.reset(2);
- enqueueInputEvent(new KeyEvent(curTime, curTime,
- KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DPAD_CENTER, 0, metaState,
- KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK,
- InputDevice.SOURCE_KEYBOARD));
- break;
- }
-
- if (DEBUG_TRACKBALL) Log.v(TAG, "TB X=" + x.position + " step="
- + x.step + " dir=" + x.dir + " acc=" + x.acceleration
- + " move=" + event.getX()
- + " / Y=" + y.position + " step="
- + y.step + " dir=" + y.dir + " acc=" + y.acceleration
- + " move=" + event.getY());
- final float xOff = x.collect(event.getX(), event.getEventTime(), "X");
- final float yOff = y.collect(event.getY(), event.getEventTime(), "Y");
-
- // Generate DPAD events based on the trackball movement.
- // We pick the axis that has moved the most as the direction of
- // the DPAD. When we generate DPAD events for one axis, then the
- // other axis is reset -- we don't want to perform DPAD jumps due
- // to slight movements in the trackball when making major movements
- // along the other axis.
- int keycode = 0;
- int movement = 0;
- float accel = 1;
- if (xOff > yOff) {
- movement = x.generate((2/event.getXPrecision()));
- if (movement != 0) {
- keycode = movement > 0 ? KeyEvent.KEYCODE_DPAD_RIGHT
- : KeyEvent.KEYCODE_DPAD_LEFT;
- accel = x.acceleration;
- y.reset(2);
- }
- } else if (yOff > 0) {
- movement = y.generate((2/event.getYPrecision()));
- if (movement != 0) {
- keycode = movement > 0 ? KeyEvent.KEYCODE_DPAD_DOWN
- : KeyEvent.KEYCODE_DPAD_UP;
- accel = y.acceleration;
- x.reset(2);
+ // If we reach this, we consumed a trackball event.
+ // Because we will not translate the trackball event into a key event,
+ // touch mode will not exit, so we exit touch mode here.
+ if (mView != null && mAdded) {
+ ensureTouchMode(false);
}
}
- if (keycode != 0) {
- if (movement < 0) movement = -movement;
- int accelMovement = (int)(movement * accel);
- if (DEBUG_TRACKBALL) Log.v(TAG, "Move: movement=" + movement
- + " accelMovement=" + accelMovement
- + " accel=" + accel);
- if (accelMovement > movement) {
- if (DEBUG_TRACKBALL) Log.v(TAG, "Delivering fake DPAD: "
- + keycode);
- movement--;
- int repeatCount = accelMovement - movement;
- enqueueInputEvent(new KeyEvent(curTime, curTime,
- KeyEvent.ACTION_MULTIPLE, keycode, repeatCount, metaState,
- KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK,
- InputDevice.SOURCE_KEYBOARD));
- }
- while (movement > 0) {
- if (DEBUG_TRACKBALL) Log.v(TAG, "Delivering fake DPAD: "
- + keycode);
- movement--;
- curTime = SystemClock.uptimeMillis();
- enqueueInputEvent(new KeyEvent(curTime, curTime,
- KeyEvent.ACTION_DOWN, keycode, 0, metaState,
- KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK,
- InputDevice.SOURCE_KEYBOARD));
- enqueueInputEvent(new KeyEvent(curTime, curTime,
- KeyEvent.ACTION_UP, keycode, 0, metaState,
- KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_FALLBACK,
- InputDevice.SOURCE_KEYBOARD));
- }
- mLastTrackballTime = curTime;
- }
-
- // Unfortunately we can't tell whether the application consumed the keys, so
- // we always consider the trackball event handled.
- return EVENT_HANDLED;
- }
-
- private int deliverGenericMotionEvent(QueuedInputEvent q) {
- final MotionEvent event = (MotionEvent)q.mEvent;
- if (mInputEventConsistencyVerifier != null) {
- mInputEventConsistencyVerifier.onGenericMotionEvent(event, 0);
- }
-
- int result = EVENT_POST_IME;
- if (mView != null && mAdded && (q.mFlags & QueuedInputEvent.FLAG_DELIVER_POST_IME) == 0) {
- if (LOCAL_LOGV)
- Log.v(TAG, "Dispatching generic motion " + event + " to " + mView);
-
- // Dispatch to the IME before propagating down the view hierarchy.
- result = dispatchImeInputEvent(q);
- }
- return result;
- }
-
- private int deliverGenericMotionEventPostIme(QueuedInputEvent q) {
- final MotionEvent event = (MotionEvent) q.mEvent;
- final int source = event.getSource();
- final boolean isJoystick = event.isFromSource(InputDevice.SOURCE_CLASS_JOYSTICK);
- final boolean isTouchNavigation = event.isFromSource(InputDevice.SOURCE_TOUCH_NAVIGATION);
-
- // If there is no view, then the event will not be handled.
- if (mView == null || !mAdded) {
- if (isJoystick) {
- updateJoystickDirection(event, false);
- } else if (isTouchNavigation) {
- mSimulatedDpad.updateTouchNavigation(this, event, false);
- }
- return EVENT_NOT_HANDLED;
- }
-
- // Deliver the event to the view.
- if (mView.dispatchGenericMotionEvent(event)) {
- if (isJoystick) {
- updateJoystickDirection(event, false);
- } else if (isTouchNavigation) {
- mSimulatedDpad.updateTouchNavigation(this, event, false);
- }
- return EVENT_HANDLED;
- }
-
- if (isJoystick) {
- // Translate the joystick event into DPAD keys and try to deliver
- // those.
+ private int processJoystickEvent(QueuedInputEvent q) {
+ final MotionEvent event = (MotionEvent)q.mEvent;
updateJoystickDirection(event, true);
- return EVENT_HANDLED;
- }
- if (isTouchNavigation) {
- mSimulatedDpad.updateTouchNavigation(this, event, true);
- return EVENT_HANDLED;
- }
- return EVENT_NOT_HANDLED;
- }
-
- private void updateJoystickDirection(MotionEvent event, boolean synthesizeNewKeys) {
- final long time = event.getEventTime();
- final int metaState = event.getMetaState();
- final int deviceId = event.getDeviceId();
- final int source = event.getSource();
-
- int xDirection = joystickAxisValueToDirection(event.getAxisValue(MotionEvent.AXIS_HAT_X));
- if (xDirection == 0) {
- xDirection = joystickAxisValueToDirection(event.getX());
+ return FINISH_HANDLED;
}
- int yDirection = joystickAxisValueToDirection(event.getAxisValue(MotionEvent.AXIS_HAT_Y));
- if (yDirection == 0) {
- yDirection = joystickAxisValueToDirection(event.getY());
+ private void cancelJoystickEvent(QueuedInputEvent q) {
+ final MotionEvent event = (MotionEvent)q.mEvent;
+ updateJoystickDirection(event, false);
}
- if (xDirection != mLastJoystickXDirection) {
- if (mLastJoystickXKeyCode != 0) {
- mHandler.removeMessages(MSG_ENQUEUE_X_AXIS_KEY_REPEAT);
- enqueueInputEvent(new KeyEvent(time, time,
- KeyEvent.ACTION_UP, mLastJoystickXKeyCode, 0, metaState,
- deviceId, 0, KeyEvent.FLAG_FALLBACK, source));
- mLastJoystickXKeyCode = 0;
+ private void updateJoystickDirection(MotionEvent event, boolean synthesizeNewKeys) {
+ final long time = event.getEventTime();
+ final int metaState = event.getMetaState();
+ final int deviceId = event.getDeviceId();
+ final int source = event.getSource();
+
+ int xDirection = joystickAxisValueToDirection(
+ event.getAxisValue(MotionEvent.AXIS_HAT_X));
+ if (xDirection == 0) {
+ xDirection = joystickAxisValueToDirection(event.getX());
}
- mLastJoystickXDirection = xDirection;
+ int yDirection = joystickAxisValueToDirection(
+ event.getAxisValue(MotionEvent.AXIS_HAT_Y));
+ if (yDirection == 0) {
+ yDirection = joystickAxisValueToDirection(event.getY());
+ }
- if (xDirection != 0 && synthesizeNewKeys) {
- mLastJoystickXKeyCode = xDirection > 0
- ? KeyEvent.KEYCODE_DPAD_RIGHT : KeyEvent.KEYCODE_DPAD_LEFT;
- final KeyEvent e = new KeyEvent(time, time,
- KeyEvent.ACTION_DOWN, mLastJoystickXKeyCode, 0, metaState,
- deviceId, 0, KeyEvent.FLAG_FALLBACK, source);
- enqueueInputEvent(e);
- Message m = mHandler.obtainMessage(MSG_ENQUEUE_X_AXIS_KEY_REPEAT, e);
- m.setAsynchronous(true);
- mHandler.sendMessageDelayed(m, mViewConfiguration.getKeyRepeatTimeout());
+ if (xDirection != mLastJoystickXDirection) {
+ if (mLastJoystickXKeyCode != 0) {
+ mHandler.removeMessages(MSG_ENQUEUE_X_AXIS_KEY_REPEAT);
+ enqueueInputEvent(new KeyEvent(time, time,
+ KeyEvent.ACTION_UP, mLastJoystickXKeyCode, 0, metaState,
+ deviceId, 0, KeyEvent.FLAG_FALLBACK, source));
+ mLastJoystickXKeyCode = 0;
+ }
+
+ mLastJoystickXDirection = xDirection;
+
+ if (xDirection != 0 && synthesizeNewKeys) {
+ mLastJoystickXKeyCode = xDirection > 0
+ ? KeyEvent.KEYCODE_DPAD_RIGHT : KeyEvent.KEYCODE_DPAD_LEFT;
+ final KeyEvent e = new KeyEvent(time, time,
+ KeyEvent.ACTION_DOWN, mLastJoystickXKeyCode, 0, metaState,
+ deviceId, 0, KeyEvent.FLAG_FALLBACK, source);
+ enqueueInputEvent(e);
+ Message m = mHandler.obtainMessage(MSG_ENQUEUE_X_AXIS_KEY_REPEAT, e);
+ m.setAsynchronous(true);
+ mHandler.sendMessageDelayed(m, ViewConfiguration.getKeyRepeatTimeout());
+ }
+ }
+
+ if (yDirection != mLastJoystickYDirection) {
+ if (mLastJoystickYKeyCode != 0) {
+ mHandler.removeMessages(MSG_ENQUEUE_Y_AXIS_KEY_REPEAT);
+ enqueueInputEvent(new KeyEvent(time, time,
+ KeyEvent.ACTION_UP, mLastJoystickYKeyCode, 0, metaState,
+ deviceId, 0, KeyEvent.FLAG_FALLBACK, source));
+ mLastJoystickYKeyCode = 0;
+ }
+
+ mLastJoystickYDirection = yDirection;
+
+ if (yDirection != 0 && synthesizeNewKeys) {
+ mLastJoystickYKeyCode = yDirection > 0
+ ? KeyEvent.KEYCODE_DPAD_DOWN : KeyEvent.KEYCODE_DPAD_UP;
+ final KeyEvent e = new KeyEvent(time, time,
+ KeyEvent.ACTION_DOWN, mLastJoystickYKeyCode, 0, metaState,
+ deviceId, 0, KeyEvent.FLAG_FALLBACK, source);
+ enqueueInputEvent(e);
+ Message m = mHandler.obtainMessage(MSG_ENQUEUE_Y_AXIS_KEY_REPEAT, e);
+ m.setAsynchronous(true);
+ mHandler.sendMessageDelayed(m, ViewConfiguration.getKeyRepeatTimeout());
+ }
}
}
- if (yDirection != mLastJoystickYDirection) {
- if (mLastJoystickYKeyCode != 0) {
- mHandler.removeMessages(MSG_ENQUEUE_Y_AXIS_KEY_REPEAT);
- enqueueInputEvent(new KeyEvent(time, time,
- KeyEvent.ACTION_UP, mLastJoystickYKeyCode, 0, metaState,
- deviceId, 0, KeyEvent.FLAG_FALLBACK, source));
- mLastJoystickYKeyCode = 0;
- }
-
- mLastJoystickYDirection = yDirection;
-
- if (yDirection != 0 && synthesizeNewKeys) {
- mLastJoystickYKeyCode = yDirection > 0
- ? KeyEvent.KEYCODE_DPAD_DOWN : KeyEvent.KEYCODE_DPAD_UP;
- final KeyEvent e = new KeyEvent(time, time,
- KeyEvent.ACTION_DOWN, mLastJoystickYKeyCode, 0, metaState,
- deviceId, 0, KeyEvent.FLAG_FALLBACK, source);
- enqueueInputEvent(e);
- Message m = mHandler.obtainMessage(MSG_ENQUEUE_Y_AXIS_KEY_REPEAT, e);
- m.setAsynchronous(true);
- mHandler.sendMessageDelayed(m, mViewConfiguration.getKeyRepeatTimeout());
+ private int joystickAxisValueToDirection(float value) {
+ if (value >= 0.5f) {
+ return 1;
+ } else if (value <= -0.5f) {
+ return -1;
+ } else {
+ return 0;
}
}
- }
- private static int joystickAxisValueToDirection(float value) {
- if (value >= 0.5f) {
- return 1;
- } else if (value <= -0.5f) {
- return -1;
- } else {
- return 0;
+ private int processTouchNavigationEvent(QueuedInputEvent q) {
+ final MotionEvent event = (MotionEvent)q.mEvent;
+ mSimulatedDpad.updateTouchNavigation(ViewRootImpl.this, event, true);
+ return FINISH_HANDLED;
+ }
+
+ private void cancelTouchNavigationEvent(QueuedInputEvent q) {
+ final MotionEvent event = (MotionEvent)q.mEvent;
+ mSimulatedDpad.updateTouchNavigation(ViewRootImpl.this, event, false);
}
}
@@ -3800,136 +4229,6 @@
return false;
}
- private int deliverKeyEvent(QueuedInputEvent q) {
- final KeyEvent event = (KeyEvent)q.mEvent;
- if (mInputEventConsistencyVerifier != null) {
- mInputEventConsistencyVerifier.onKeyEvent(event, 0);
- }
-
- int result = EVENT_POST_IME;
- if (mView != null && mAdded && (q.mFlags & QueuedInputEvent.FLAG_DELIVER_POST_IME) == 0) {
- if (LOCAL_LOGV) Log.v(TAG, "Dispatching key " + event + " to " + mView);
-
- // Perform predispatching before the IME.
- if (mView.dispatchKeyEventPreIme(event)) {
- return EVENT_HANDLED;
- }
-
- // Dispatch to the IME before propagating down the view hierarchy.
- result = dispatchImeInputEvent(q);
- }
- return result;
- }
-
- private int deliverKeyEventPostIme(QueuedInputEvent q) {
- final KeyEvent event = (KeyEvent)q.mEvent;
-
- // If the view went away, then the event will not be handled.
- if (mView == null || !mAdded) {
- return EVENT_NOT_HANDLED;
- }
-
- // If the key's purpose is to exit touch mode then we consume it and consider it handled.
- if (checkForLeavingTouchModeAndConsume(event)) {
- return EVENT_HANDLED;
- }
-
- // Make sure the fallback event policy sees all keys that will be delivered to the
- // view hierarchy.
- mFallbackEventHandler.preDispatchKeyEvent(event);
-
- // Deliver the key to the view hierarchy.
- if (mView.dispatchKeyEvent(event)) {
- return EVENT_HANDLED;
- }
-
- // If the Control modifier is held, try to interpret the key as a shortcut.
- if (event.getAction() == KeyEvent.ACTION_DOWN
- && event.isCtrlPressed()
- && event.getRepeatCount() == 0
- && !KeyEvent.isModifierKey(event.getKeyCode())) {
- if (mView.dispatchKeyShortcutEvent(event)) {
- return EVENT_HANDLED;
- }
- }
-
- // Apply the fallback event policy.
- if (mFallbackEventHandler.dispatchKeyEvent(event)) {
- return EVENT_HANDLED;
- }
-
- // Handle automatic focus changes.
- if (event.getAction() == KeyEvent.ACTION_DOWN) {
- int direction = 0;
- switch (event.getKeyCode()) {
- case KeyEvent.KEYCODE_DPAD_LEFT:
- if (event.hasNoModifiers()) {
- direction = View.FOCUS_LEFT;
- }
- break;
- case KeyEvent.KEYCODE_DPAD_RIGHT:
- if (event.hasNoModifiers()) {
- direction = View.FOCUS_RIGHT;
- }
- break;
- case KeyEvent.KEYCODE_DPAD_UP:
- if (event.hasNoModifiers()) {
- direction = View.FOCUS_UP;
- }
- break;
- case KeyEvent.KEYCODE_DPAD_DOWN:
- if (event.hasNoModifiers()) {
- direction = View.FOCUS_DOWN;
- }
- break;
- case KeyEvent.KEYCODE_TAB:
- if (event.hasNoModifiers()) {
- direction = View.FOCUS_FORWARD;
- } else if (event.hasModifiers(KeyEvent.META_SHIFT_ON)) {
- direction = View.FOCUS_BACKWARD;
- }
- break;
- }
- if (direction != 0) {
- View focused = mView.findFocus();
- if (focused != null) {
- View v = focused.focusSearch(direction);
- if (v != null && v != focused) {
- // do the math the get the interesting rect
- // of previous focused into the coord system of
- // newly focused view
- focused.getFocusedRect(mTempRect);
- if (mView instanceof ViewGroup) {
- ((ViewGroup) mView).offsetDescendantRectToMyCoords(
- focused, mTempRect);
- ((ViewGroup) mView).offsetRectIntoDescendantCoords(
- v, mTempRect);
- }
- if (v.requestFocus(direction, mTempRect)) {
- playSoundEffect(SoundEffectConstants
- .getContantForFocusDirection(direction));
- return EVENT_HANDLED;
- }
- }
-
- // Give the focused view a last chance to handle the dpad key.
- if (mView.dispatchUnhandledMove(focused, direction)) {
- return EVENT_HANDLED;
- }
- } else {
- // find the best view to give focus to in this non-touch-mode with no-focus
- View v = focusSearch(null, direction);
- if (v != null && v.requestFocus(direction)) {
- return EVENT_HANDLED;
- }
- }
- }
- }
-
- // Key was unhandled.
- return EVENT_NOT_HANDLED;
- }
-
/* drag/drop */
void setLocalDragState(Object obj) {
mLocalDragState = obj;
@@ -4361,13 +4660,25 @@
* needing a queue on the application's side.
*/
private static final class QueuedInputEvent {
- public static final int FLAG_DELIVER_POST_IME = 1;
+ public static final int FLAG_DELIVER_POST_IME = 1 << 0;
+ public static final int FLAG_DEFERRED = 1 << 1;
+ public static final int FLAG_FINISHED = 1 << 2;
+ public static final int FLAG_FINISHED_HANDLED = 1 << 3;
+ public static final int FLAG_RESYNTHESIZED = 1 << 4;
public QueuedInputEvent mNext;
public InputEvent mEvent;
public InputEventReceiver mReceiver;
public int mFlags;
+
+ public boolean shouldSkipIme() {
+ if ((mFlags & FLAG_DELIVER_POST_IME) != 0) {
+ return true;
+ }
+ return mEvent instanceof MotionEvent
+ && mEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER);
+ }
}
private QueuedInputEvent obtainQueuedInputEvent(InputEvent event,
@@ -4440,14 +4751,7 @@
}
void doProcessInputEvents() {
- // Handle all of the available pending input events. Currently this will immediately
- // process all of the events it can until it encounters one that must go through the IME.
- // After that it will continue adding events to the active input queue but will wait for a
- // response from the IME, regardless of whether that particular event needs it or not, in
- // order to guarantee ordering consistency. This could be slightly improved by only
- // queueing events whose source has previously encountered something that needs to be
- // handled by the IME, and otherwise handling them immediately since we only need to
- // guarantee ordering within a given source.
+ // Deliver all pending input events in the queue.
while (mPendingInputEventHead != null) {
QueuedInputEvent q = mPendingInputEventHead;
mPendingInputEventHead = q.mNext;
@@ -4460,25 +4764,7 @@
Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName,
mPendingInputEventCount);
- int result = deliverInputEvent(q);
-
- if (result == EVENT_HANDLED || result == EVENT_NOT_HANDLED) {
- finishInputEvent(q, result == EVENT_HANDLED);
- } else if (result == EVENT_PENDING_IME) {
- enqueueActiveInputEvent(q);
- } else {
- q.mFlags |= QueuedInputEvent.FLAG_DELIVER_POST_IME;
- // If the IME decided not to handle this event, and we have no events already being
- // handled by the IME, go ahead and handle this one and then continue to the next
- // input event. Otherwise, queue it up and handle it after whatever in front of it
- // in the queue has been handled.
- if (mActiveInputEventHead == null) {
- result = deliverInputEventPostIme(q);
- finishInputEvent(q, result == EVENT_HANDLED);
- } else {
- enqueueActiveInputEvent(q);
- }
- }
+ deliverInputEvent(q);
}
// We are done processing all input events that we can process right now
@@ -4489,114 +4775,27 @@
}
}
- private void enqueueActiveInputEvent(QueuedInputEvent q) {
- if (mActiveInputEventHead == null) {
- mActiveInputEventHead = q;
- mActiveInputEventTail = q;
- } else {
- mActiveInputEventTail.mNext = q;
- mActiveInputEventTail = q;
- }
- mActiveInputEventCount += 1;
- Trace.traceCounter(Trace.TRACE_TAG_INPUT, mActiveInputEventQueueLengthCounterName,
- mActiveInputEventCount);
- }
+ private void deliverInputEvent(QueuedInputEvent q) {
+ Trace.traceBegin(Trace.TRACE_TAG_VIEW, "deliverInputEvent");
+ try {
+ if (mInputEventConsistencyVerifier != null) {
+ mInputEventConsistencyVerifier.onInputEvent(q.mEvent, 0);
+ }
- private QueuedInputEvent dequeueActiveInputEvent() {
- return dequeueActiveInputEvent(mActiveInputEventHead);
- }
-
-
- private QueuedInputEvent dequeueActiveInputEvent(QueuedInputEvent q) {
- QueuedInputEvent curr = mActiveInputEventHead;
- QueuedInputEvent prev = null;
- while (curr != null && curr != q) {
- prev = curr;
- curr = curr.mNext;
- }
- if (curr != null) {
- if (mActiveInputEventHead == curr) {
- mActiveInputEventHead = curr.mNext;
+ InputStage stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;
+ if (stage != null) {
+ stage.deliver(q);
} else {
- prev.mNext = curr.mNext;
+ finishInputEvent(q);
}
- if (mActiveInputEventTail == curr) {
- mActiveInputEventTail = prev;
- }
- curr.mNext = null;
-
- mActiveInputEventCount -= 1;
- Trace.traceCounter(Trace.TRACE_TAG_INPUT, mActiveInputEventQueueLengthCounterName,
- mActiveInputEventCount);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
- return curr;
}
- private QueuedInputEvent findActiveInputEvent(int seq) {
- QueuedInputEvent q = mActiveInputEventHead;
- while (q != null && q.mEvent.getSequenceNumber() != seq) {
- q = q.mNext;
- }
- return q;
- }
-
- int dispatchImeInputEvent(QueuedInputEvent q) {
- if (mLastWasImTarget) {
- InputMethodManager imm = InputMethodManager.peekInstance();
- if (imm != null) {
- final InputEvent event = q.mEvent;
- final int seq = event.getSequenceNumber();
- if (DEBUG_IMF)
- Log.v(TAG, "Sending input event to IME: seq=" + seq + " event=" + event);
- return imm.dispatchInputEvent(mView.getContext(), seq, event,
- mInputMethodCallback);
- }
- }
- return EVENT_POST_IME;
- }
-
- void handleImeFinishedEvent(int seq, boolean handled) {
- QueuedInputEvent q = findActiveInputEvent(seq);
- if (q != null) {
- if (DEBUG_IMF) {
- Log.v(TAG, "IME finished event: seq=" + seq
- + " handled=" + handled + " event=" + q);
- }
-
- if (handled) {
- dequeueActiveInputEvent(q);
- finishInputEvent(q, true);
- } else {
- q.mFlags |= QueuedInputEvent.FLAG_DELIVER_POST_IME;
- }
-
-
- // Flush all of the input events that are no longer waiting on the IME
- while (mActiveInputEventHead != null && (mActiveInputEventHead.mFlags &
- QueuedInputEvent.FLAG_DELIVER_POST_IME) != 0) {
- q = dequeueActiveInputEvent();
- // If the window doesn't currently have input focus, then drop
- // this event. This could be an event that came back from the
- // IME dispatch but the window has lost focus in the meantime.
- handled = false;
- if (!mAttachInfo.mHasWindowFocus && !isTerminalInputEvent(q.mEvent)) {
- Slog.w(TAG, "Dropping event due to no window focus: " + q.mEvent);
- } else {
- handled = (deliverInputEventPostIme(q) == EVENT_HANDLED);
- }
- finishInputEvent(q, handled);
- }
- } else {
- if (DEBUG_IMF) {
- Log.v(TAG, "IME finished event: seq=" + seq
- + " handled=" + handled + ", event not found!");
- }
- }
-
- }
-
- private void finishInputEvent(QueuedInputEvent q, boolean handled) {
+ private void finishInputEvent(QueuedInputEvent q) {
if (q.mReceiver != null) {
+ boolean handled = (q.mFlags & QueuedInputEvent.FLAG_FINISHED_HANDLED) != 0;
q.mReceiver.finishInputEvent(q.mEvent, handled);
} else {
q.mEvent.recycleIfNeededAfterDispatch();
@@ -4605,7 +4804,7 @@
recycleQueuedInputEvent(q);
}
- private static boolean isTerminalInputEvent(InputEvent event) {
+ static boolean isTerminalInputEvent(InputEvent event) {
if (event instanceof KeyEvent) {
final KeyEvent keyEvent = (KeyEvent)event;
return keyEvent.getAction() == KeyEvent.ACTION_UP;
@@ -5141,22 +5340,6 @@
((RootViewSurfaceTaker)mView).setSurfaceKeepScreenOn(screenOn);
}
}
-
- static final class InputMethodCallback implements InputMethodManager.FinishedEventCallback {
- private WeakReference<ViewRootImpl> mViewAncestor;
-
- public InputMethodCallback(ViewRootImpl viewAncestor) {
- mViewAncestor = new WeakReference<ViewRootImpl>(viewAncestor);
- }
-
- @Override
- public void finishedEvent(int seq, boolean handled) {
- final ViewRootImpl viewAncestor = mViewAncestor.get();
- if (viewAncestor != null) {
- viewAncestor.handleImeFinishedEvent(seq, handled);
- }
- }
- }
static class W extends IWindow.Stub {
private final WeakReference<ViewRootImpl> mViewAncestor;
@@ -5329,8 +5512,11 @@
*/
static final float ACCEL_MOVE_SCALING_FACTOR = (1.0f/40);
+ static final float FIRST_MOVEMENT_THRESHOLD = 0.5f;
+ static final float SECOND_CUMULATIVE_MOVEMENT_THRESHOLD = 2.0f;
+ static final float SUBSEQUENT_INCREMENTAL_MOVEMENT_THRESHOLD = 1.0f;
+
float position;
- float absPosition;
float acceleration = 1;
long lastMoveTime = 0;
int step;
@@ -5407,21 +5593,18 @@
}
}
position += off;
- return (absPosition = Math.abs(position));
+ return Math.abs(position);
}
/**
* Generate the number of discrete movement events appropriate for
* the currently collected trackball movement.
*
- * @param precision The minimum movement required to generate the
- * first discrete movement.
- *
* @return Returns the number of discrete movements, either positive
* or negative, or 0 if there is not enough trackball movement yet
* for a discrete movement.
*/
- int generate(float precision) {
+ int generate() {
int movement = 0;
nonAccelMovement = 0;
do {
@@ -5431,7 +5614,7 @@
// to do this as soon as possible instead of waiting for
// a full movement, in order to make things look responsive.
case 0:
- if (absPosition < precision) {
+ if (Math.abs(position) < FIRST_MOVEMENT_THRESHOLD) {
return movement;
}
movement += dir;
@@ -5442,13 +5625,12 @@
// to wait for the second complete trackball motion before
// generating the second discrete movement.
case 1:
- if (absPosition < 2) {
+ if (Math.abs(position) < SECOND_CUMULATIVE_MOVEMENT_THRESHOLD) {
return movement;
}
movement += dir;
nonAccelMovement += dir;
- position += dir > 0 ? -2 : 2;
- absPosition = Math.abs(position);
+ position -= SECOND_CUMULATIVE_MOVEMENT_THRESHOLD * dir;
step = 2;
break;
// After the first two, we generate discrete movements
@@ -5459,12 +5641,11 @@
// a longer increasing acceleration to continuous movement
// in one direction.
default:
- if (absPosition < 1) {
+ if (Math.abs(position) < SUBSEQUENT_INCREMENTAL_MOVEMENT_THRESHOLD) {
return movement;
}
movement += dir;
- position += dir >= 0 ? -1 : 1;
- absPosition = Math.abs(position);
+ position -= dir * SUBSEQUENT_INCREMENTAL_MOVEMENT_THRESHOLD;
float acc = acceleration;
acc *= 1.1f;
acceleration = acc < MAX_ACCELERATION ? acc : acceleration;
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index 7eb26fa..0ff46e9 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -136,11 +136,11 @@
}
}
- public static IWindowSession getWindowSession(Looper mainLooper) {
+ public static IWindowSession getWindowSession() {
synchronized (WindowManagerGlobal.class) {
if (sWindowSession == null) {
try {
- InputMethodManager imm = InputMethodManager.getInstance(mainLooper);
+ InputMethodManager imm = InputMethodManager.getInstance();
IWindowManager windowManager = getWindowManagerService();
sWindowSession = windowManager.openSession(
imm.getClient(), imm.getInputContext());
@@ -351,7 +351,7 @@
View view = root.getView();
if (view != null) {
- InputMethodManager imm = InputMethodManager.getInstance(view.getContext());
+ InputMethodManager imm = InputMethodManager.getInstance();
if (imm != null) {
imm.windowDismissed(mViews[index].getWindowToken());
}
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 855b6d4..4df4734 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -34,9 +34,11 @@
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ServiceManager;
-import android.os.SystemClock;
+import android.os.Trace;
import android.text.style.SuggestionSpan;
import android.util.Log;
+import android.util.Pools.Pool;
+import android.util.Pools.SimplePool;
import android.util.PrintWriterPrinter;
import android.util.Printer;
import android.view.InputChannel;
@@ -45,6 +47,7 @@
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewRootImpl;
+import android.util.SparseArray;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -200,8 +203,9 @@
static final boolean DEBUG = false;
static final String TAG = "InputMethodManager";
- static final Object mInstanceSync = new Object();
- static InputMethodManager mInstance;
+ static final String PENDING_EVENT_COUNTER = "aq:imm";
+
+ static InputMethodManager sInstance;
/**
* @hide Flag for IInputMethodManager.windowGainedFocus: a view in
@@ -232,7 +236,14 @@
*/
static final long INPUT_METHOD_NOT_RESPONDING_TIMEOUT = 2500;
- private static final int MAX_PENDING_EVENT_POOL_SIZE = 4;
+ /** @hide */
+ public static final int DISPATCH_IN_PROGRESS = -1;
+
+ /** @hide */
+ public static final int DISPATCH_NOT_HANDLED = 0;
+
+ /** @hide */
+ public static final int DISPATCH_HANDLED = 1;
final IInputMethodManager mService;
final Looper mMainLooper;
@@ -323,10 +334,8 @@
InputChannel mCurChannel;
ImeInputEventSender mCurSender;
- PendingEvent mPendingEventPool;
- int mPendingEventPoolSize;
- PendingEvent mPendingEventHead;
- PendingEvent mPendingEventTail;
+ final Pool<PendingEvent> mPendingEventPool = new SimplePool<PendingEvent>(20);
+ final SparseArray<PendingEvent> mPendingEvents = new SparseArray<PendingEvent>(20);
// -----------------------------------------------------------
@@ -334,8 +343,10 @@
static final int MSG_BIND = 2;
static final int MSG_UNBIND = 3;
static final int MSG_SET_ACTIVE = 4;
- static final int MSG_EVENT_TIMEOUT = 5;
-
+ static final int MSG_SEND_INPUT_EVENT = 5;
+ static final int MSG_TIMEOUT_INPUT_EVENT = 6;
+ static final int MSG_FLUSH_INPUT_EVENT = 7;
+
class H extends Handler {
H(Looper looper) {
super(looper, null, true);
@@ -453,15 +464,16 @@
}
return;
}
- case MSG_EVENT_TIMEOUT: {
- // Even though the message contains both the sequence number
- // and the PendingEvent object itself, we only pass the
- // sequence number to the timeoutEvent function because it's
- // possible for the PendingEvent object to be dequeued and
- // recycled concurrently. To avoid a possible race, we make
- // a point of always looking up the PendingEvent within the
- // queue given only the sequence number of the event.
- timeoutEvent(msg.arg1);
+ case MSG_SEND_INPUT_EVENT: {
+ sendInputEventAndReportResultOnMainLooper((PendingEvent)msg.obj);
+ return;
+ }
+ case MSG_TIMEOUT_INPUT_EVENT: {
+ finishedInputEvent(msg.arg1, false, true);
+ return;
+ }
+ case MSG_FLUSH_INPUT_EVENT: {
+ finishedInputEvent(msg.arg1, false, false);
return;
}
}
@@ -538,10 +550,6 @@
mH = new H(looper);
mIInputContext = new ControlledInputConnectionWrapper(looper,
mDummyInputConnection, this);
-
- if (mInstance == null) {
- mInstance = this;
- }
}
/**
@@ -549,25 +557,15 @@
* doesn't already exist.
* @hide
*/
- static public InputMethodManager getInstance(Context context) {
- return getInstance(context.getMainLooper());
- }
-
- /**
- * Internally, the input method manager can't be context-dependent, so
- * we have this here for the places that need it.
- * @hide
- */
- static public InputMethodManager getInstance(Looper mainLooper) {
- synchronized (mInstanceSync) {
- if (mInstance != null) {
- return mInstance;
+ public static InputMethodManager getInstance() {
+ synchronized (InputMethodManager.class) {
+ if (sInstance == null) {
+ IBinder b = ServiceManager.getService(Context.INPUT_METHOD_SERVICE);
+ IInputMethodManager service = IInputMethodManager.Stub.asInterface(b);
+ sInstance = new InputMethodManager(service, Looper.getMainLooper());
}
- IBinder b = ServiceManager.getService(Context.INPUT_METHOD_SERVICE);
- IInputMethodManager service = IInputMethodManager.Stub.asInterface(b);
- mInstance = new InputMethodManager(service, mainLooper);
+ return sInstance;
}
- return mInstance;
}
/**
@@ -575,8 +573,8 @@
* if it exists.
* @hide
*/
- static public InputMethodManager peekInstance() {
- return mInstance;
+ public static InputMethodManager peekInstance() {
+ return sInstance;
}
/** @hide */
@@ -1585,13 +1583,18 @@
}
/**
+ * Dispatches an input event to the IME.
+ *
+ * Returns {@link #DISPATCH_HANDLED} if the event was handled.
+ * Returns {@link #DISPATCH_NOT_HANDLED} if the event was not handled.
+ * Returns {@link #DISPATCH_IN_PROGRESS} if the event is in progress and the
+ * callback will be invoked later.
+ *
* @hide
*/
- public int dispatchInputEvent(Context context, int seq, InputEvent event,
- FinishedEventCallback callback) {
+ public int dispatchInputEvent(InputEvent event, Object token,
+ FinishedInputEventCallback callback, Handler handler) {
synchronized (mH) {
- if (DEBUG) Log.d(TAG, "dispatchInputEvent");
-
if (mCurMethod != null) {
if (event instanceof KeyEvent) {
KeyEvent keyEvent = (KeyEvent)event;
@@ -1599,144 +1602,140 @@
&& keyEvent.getKeyCode() == KeyEvent.KEYCODE_SYM
&& keyEvent.getRepeatCount() == 0) {
showInputMethodPickerLocked();
- return ViewRootImpl.EVENT_HANDLED;
+ return DISPATCH_HANDLED;
}
}
if (DEBUG) Log.v(TAG, "DISPATCH INPUT EVENT: " + mCurMethod);
- final long startTime = SystemClock.uptimeMillis();
- if (mCurChannel != null) {
- if (mCurSender == null) {
- mCurSender = new ImeInputEventSender(mCurChannel, mH.getLooper());
- }
- if (mCurSender.sendInputEvent(seq, event)) {
- enqueuePendingEventLocked(startTime, seq, mCurId, callback);
- return ViewRootImpl.EVENT_PENDING_IME;
- } else {
- Log.w(TAG, "Unable to send input event to IME: "
- + mCurId + " dropping: " + event);
- }
+
+ PendingEvent p = obtainPendingEventLocked(
+ event, token, mCurId, callback, handler);
+ if (mMainLooper.isCurrentThread()) {
+ // Already running on the IMM thread so we can send the event immediately.
+ return sendInputEventOnMainLooperLocked(p);
}
+
+ // Post the event to the IMM thread.
+ Message msg = mH.obtainMessage(MSG_SEND_INPUT_EVENT, p);
+ msg.setAsynchronous(true);
+ mH.sendMessage(msg);
+ return DISPATCH_IN_PROGRESS;
}
}
- return ViewRootImpl.EVENT_POST_IME;
+ return DISPATCH_NOT_HANDLED;
}
- void finishedEvent(int seq, boolean handled) {
- final FinishedEventCallback callback;
+ // Must be called on the main looper
+ void sendInputEventAndReportResultOnMainLooper(PendingEvent p) {
+ final boolean handled;
synchronized (mH) {
- PendingEvent p = dequeuePendingEventLocked(seq);
- if (p == null) {
+ int result = sendInputEventOnMainLooperLocked(p);
+ if (result == DISPATCH_IN_PROGRESS) {
+ return;
+ }
+
+ handled = (result == DISPATCH_HANDLED);
+ }
+
+ invokeFinishedInputEventCallback(p, handled);
+ }
+
+ // Must be called on the main looper
+ int sendInputEventOnMainLooperLocked(PendingEvent p) {
+ if (mCurChannel != null) {
+ if (mCurSender == null) {
+ mCurSender = new ImeInputEventSender(mCurChannel, mH.getLooper());
+ }
+
+ final InputEvent event = p.mEvent;
+ final int seq = event.getSequenceNumber();
+ if (mCurSender.sendInputEvent(seq, event)) {
+ mPendingEvents.put(seq, p);
+ Trace.traceCounter(Trace.TRACE_TAG_INPUT, PENDING_EVENT_COUNTER,
+ mPendingEvents.size());
+
+ Message msg = mH.obtainMessage(MSG_TIMEOUT_INPUT_EVENT, p);
+ msg.setAsynchronous(true);
+ mH.sendMessageDelayed(msg, INPUT_METHOD_NOT_RESPONDING_TIMEOUT);
+ return DISPATCH_IN_PROGRESS;
+ }
+
+ Log.w(TAG, "Unable to send input event to IME: "
+ + mCurId + " dropping: " + event);
+ }
+ return DISPATCH_NOT_HANDLED;
+ }
+
+ void finishedInputEvent(int seq, boolean handled, boolean timeout) {
+ final PendingEvent p;
+ synchronized (mH) {
+ int index = mPendingEvents.indexOfKey(seq);
+ if (index < 0) {
return; // spurious, event already finished or timed out
}
- mH.removeMessages(MSG_EVENT_TIMEOUT, p);
- callback = p.mCallback;
- recyclePendingEventLocked(p);
- }
- callback.finishedEvent(seq, handled);
- }
- void timeoutEvent(int seq) {
- final FinishedEventCallback callback;
- synchronized (mH) {
- PendingEvent p = dequeuePendingEventLocked(seq);
- if (p == null) {
- return; // spurious, event already finished or timed out
- }
- long delay = SystemClock.uptimeMillis() - p.mStartTime;
- Log.w(TAG, "Timeout waiting for IME to handle input event after "
- + delay + "ms: " + p.mInputMethodId);
- callback = p.mCallback;
- recyclePendingEventLocked(p);
- }
- callback.finishedEvent(seq, false);
- }
+ p = mPendingEvents.valueAt(index);
+ mPendingEvents.removeAt(index);
+ Trace.traceCounter(Trace.TRACE_TAG_INPUT, PENDING_EVENT_COUNTER, mPendingEvents.size());
- private void enqueuePendingEventLocked(
- long startTime, int seq, String inputMethodId, FinishedEventCallback callback) {
- PendingEvent p = obtainPendingEventLocked(startTime, seq, inputMethodId, callback);
- if (mPendingEventTail != null) {
- mPendingEventTail.mNext = p;
- mPendingEventTail = p;
- } else {
- mPendingEventHead = p;
- mPendingEventTail = p;
- }
-
- Message msg = mH.obtainMessage(MSG_EVENT_TIMEOUT, seq, 0, p);
- msg.setAsynchronous(true);
- mH.sendMessageDelayed(msg, INPUT_METHOD_NOT_RESPONDING_TIMEOUT);
- }
-
- private PendingEvent dequeuePendingEventLocked(int seq) {
- PendingEvent p = mPendingEventHead;
- if (p == null) {
- return null;
- }
- if (p.mSeq == seq) {
- mPendingEventHead = p.mNext;
- if (mPendingEventHead == null) {
- mPendingEventTail = null;
- }
- } else {
- PendingEvent prev;
- do {
- prev = p;
- p = p.mNext;
- if (p == null) {
- return null;
- }
- } while (p.mSeq != seq);
- prev.mNext = p.mNext;
- if (mPendingEventTail == p) {
- mPendingEventTail = prev;
+ if (timeout) {
+ Log.w(TAG, "Timeout waiting for IME to handle input event after "
+ + INPUT_METHOD_NOT_RESPONDING_TIMEOUT + " ms: " + p.mInputMethodId);
+ } else {
+ mH.removeMessages(MSG_TIMEOUT_INPUT_EVENT, p);
}
}
- p.mNext = null;
- return p;
+
+ invokeFinishedInputEventCallback(p, handled);
}
- private PendingEvent obtainPendingEventLocked(
- long startTime, int seq, String inputMethodId, FinishedEventCallback callback) {
- PendingEvent p = mPendingEventPool;
- if (p != null) {
- mPendingEventPoolSize -= 1;
- mPendingEventPool = p.mNext;
- p.mNext = null;
+ // Assumes the event has already been removed from the queue.
+ void invokeFinishedInputEventCallback(PendingEvent p, boolean handled) {
+ p.mHandled = handled;
+ if (p.mHandler.getLooper().isCurrentThread()) {
+ // Already running on the callback handler thread so we can send the
+ // callback immediately.
+ p.run();
} else {
- p = new PendingEvent();
- }
-
- p.mStartTime = startTime;
- p.mSeq = seq;
- p.mInputMethodId = inputMethodId;
- p.mCallback = callback;
- return p;
- }
-
- private void recyclePendingEventLocked(PendingEvent p) {
- p.mInputMethodId = null;
- p.mCallback = null;
-
- if (mPendingEventPoolSize < MAX_PENDING_EVENT_POOL_SIZE) {
- mPendingEventPoolSize += 1;
- p.mNext = mPendingEventPool;
- mPendingEventPool = p;
+ // Post the event to the callback handler thread.
+ // In this case, the callback will be responsible for recycling the event.
+ Message msg = Message.obtain(p.mHandler, p);
+ msg.setAsynchronous(true);
+ msg.sendToTarget();
}
}
private void flushPendingEventsLocked() {
- mH.removeMessages(MSG_EVENT_TIMEOUT);
+ mH.removeMessages(MSG_FLUSH_INPUT_EVENT);
- PendingEvent p = mPendingEventHead;
- while (p != null) {
- Message msg = mH.obtainMessage(MSG_EVENT_TIMEOUT, p.mSeq, 0, p);
+ final int count = mPendingEvents.size();
+ for (int i = 0; i < count; i++) {
+ int seq = mPendingEvents.keyAt(i);
+ Message msg = mH.obtainMessage(MSG_FLUSH_INPUT_EVENT, seq, 0);
msg.setAsynchronous(true);
- mH.sendMessage(msg);
- p = p.mNext;
+ msg.sendToTarget();
}
}
+ private PendingEvent obtainPendingEventLocked(InputEvent event, Object token,
+ String inputMethodId, FinishedInputEventCallback callback, Handler handler) {
+ PendingEvent p = mPendingEventPool.acquire();
+ if (p == null) {
+ p = new PendingEvent();
+ }
+ p.mEvent = event;
+ p.mToken = token;
+ p.mInputMethodId = inputMethodId;
+ p.mCallback = callback;
+ p.mHandler = handler;
+ return p;
+ }
+
+ private void recyclePendingEventLocked(PendingEvent p) {
+ p.recycle();
+ mPendingEventPool.release(p);
+ }
+
public void showInputMethodPicker() {
synchronized (mH) {
showInputMethodPickerLocked();
@@ -1946,8 +1945,8 @@
* the IME has been finished.
* @hide
*/
- public interface FinishedEventCallback {
- public void finishedEvent(int seq, boolean handled);
+ public interface FinishedInputEventCallback {
+ public void onFinishedInputEvent(Object token, boolean handled);
}
private final class ImeInputEventSender extends InputEventSender {
@@ -1957,16 +1956,34 @@
@Override
public void onInputEventFinished(int seq, boolean handled) {
- finishedEvent(seq, handled);
+ finishedInputEvent(seq, handled, false);
}
}
- private static final class PendingEvent {
- public PendingEvent mNext;
-
- public long mStartTime;
- public int mSeq;
+ private final class PendingEvent implements Runnable {
+ public InputEvent mEvent;
+ public Object mToken;
public String mInputMethodId;
- public FinishedEventCallback mCallback;
+ public FinishedInputEventCallback mCallback;
+ public Handler mHandler;
+ public boolean mHandled;
+
+ public void recycle() {
+ mEvent = null;
+ mToken = null;
+ mInputMethodId = null;
+ mCallback = null;
+ mHandler = null;
+ mHandled = false;
+ }
+
+ @Override
+ public void run() {
+ mCallback.onFinishedInputEvent(mToken, mHandled);
+
+ synchronized (mH) {
+ recyclePendingEventLocked(this);
+ }
+ }
}
}
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index 4b62c2d..c7914f3 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -2433,7 +2433,7 @@
mFirstPosition;
} else {
final int lastPos = mFirstPosition + getChildCount() - 1;
- nextSelected = selectedPos != INVALID_POSITION && selectedPos < lastPos?
+ nextSelected = selectedPos != INVALID_POSITION && selectedPos <= lastPos ?
selectedPos - 1 :
lastPos;
}
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index b6b49a2..45a8b99 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -22,6 +22,7 @@
import android.media.MediaFormat;
import android.view.Surface;
import java.nio.ByteBuffer;
+import java.util.Arrays;
import java.util.Map;
/**
@@ -395,6 +396,27 @@
* see {@link #CRYPTO_MODE_UNENCRYPTED} and {@link #CRYPTO_MODE_AES_CTR}.
*/
public int mode;
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append(numSubSamples + " subsamples, key [");
+ String hexdigits = "0123456789abcdef";
+ for (int i = 0; i < key.length; i++) {
+ builder.append(hexdigits.charAt((key[i] & 0xf0) >> 4));
+ builder.append(hexdigits.charAt(key[i] & 0x0f));
+ }
+ builder.append("], iv [");
+ for (int i = 0; i < key.length; i++) {
+ builder.append(hexdigits.charAt((iv[i] & 0xf0) >> 4));
+ builder.append(hexdigits.charAt(iv[i] & 0x0f));
+ }
+ builder.append("], clear ");
+ builder.append(Arrays.toString(numBytesOfClearData));
+ builder.append(", encrypted ");
+ builder.append(Arrays.toString(numBytesOfEncryptedData));
+ return builder.toString();
+ }
};
/**
diff --git a/media/java/android/media/MediaExtractor.java b/media/java/android/media/MediaExtractor.java
index 749ef12..286e203 100644
--- a/media/java/android/media/MediaExtractor.java
+++ b/media/java/android/media/MediaExtractor.java
@@ -26,7 +26,10 @@
import java.io.FileDescriptor;
import java.io.IOException;
import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.HashMap;
import java.util.Map;
+import java.util.UUID;
/**
* MediaExtractor facilitates extraction of demuxed, typically encoded, media data
@@ -195,6 +198,38 @@
public native final int getTrackCount();
/**
+ * Get the PSSH info if present. This returns a map of uuid-to-bytes, with the uuid specifying
+ * the crypto scheme, and the bytes being the data specific to that scheme.
+ * {@hide}
+ */
+ public Map<UUID, byte[]> getPsshInfo() {
+ Map<UUID, byte[]> psshMap = null;
+ Map<String, Object> formatMap = getFileFormatNative();
+ if (formatMap != null && formatMap.containsKey("pssh")) {
+ ByteBuffer rawpssh = (ByteBuffer) formatMap.get("pssh");
+ rawpssh.order(ByteOrder.nativeOrder());
+ rawpssh.rewind();
+ formatMap.remove("pssh");
+ // parse the flat pssh bytebuffer into something more manageable
+ psshMap = new HashMap<UUID, byte[]>();
+ while (rawpssh.remaining() > 0) {
+ rawpssh.order(ByteOrder.BIG_ENDIAN);
+ long msb = rawpssh.getLong();
+ long lsb = rawpssh.getLong();
+ UUID uuid = new UUID(msb, lsb);
+ rawpssh.order(ByteOrder.nativeOrder());
+ int datalen = rawpssh.getInt();
+ byte [] psshdata = new byte[datalen];
+ rawpssh.get(psshdata);
+ psshMap.put(uuid, psshdata);
+ }
+ }
+ return psshMap;
+ }
+
+ private native Map<String, Object> getFileFormatNative();
+
+ /**
* Get the track format at the specified index.
* More detail on the representation can be found at {@link android.media.MediaCodec}
*/
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index 3a42db2..cd1d9ce 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -372,7 +372,7 @@
default:
{
- jniThrowException(env, "java/lang/IllegalStateException", NULL);
+ jniThrowException(env, "java/lang/IllegalStateException", msg);
break;
}
}
@@ -455,13 +455,13 @@
sp<JMediaCodec> codec = getMediaCodec(env, thiz);
if (codec == NULL) {
- jniThrowException(env, "java/lang/IllegalStateException", NULL);
+ jniThrowException(env, "java/lang/IllegalStateException", "no codec found");
return;
}
status_t err = codec->start();
- throwExceptionAsNecessary(env, err);
+ throwExceptionAsNecessary(env, err, "start failed");
}
static void android_media_MediaCodec_stop(JNIEnv *env, jobject thiz) {
diff --git a/media/jni/android_media_MediaExtractor.cpp b/media/jni/android_media_MediaExtractor.cpp
index 23949fa..1704d5c 100644
--- a/media/jni/android_media_MediaExtractor.cpp
+++ b/media/jni/android_media_MediaExtractor.cpp
@@ -162,6 +162,18 @@
return ConvertMessageToMap(env, msg, format);
}
+status_t JMediaExtractor::getFileFormat(jobject *format) const {
+ sp<AMessage> msg;
+ status_t err;
+ if ((err = mImpl->getFileFormat(&msg)) != OK) {
+ return err;
+ }
+
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+
+ return ConvertMessageToMap(env, msg, format);
+}
+
status_t JMediaExtractor::selectTrack(size_t index) {
return mImpl->selectTrack(index);
}
@@ -339,6 +351,26 @@
return format;
}
+static jobject android_media_MediaExtractor_getFileFormatNative(
+ JNIEnv *env, jobject thiz) {
+ sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
+
+ if (extractor == NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException", NULL);
+ return NULL;
+ }
+
+ jobject format;
+ status_t err = extractor->getFileFormat(&format);
+
+ if (err != OK) {
+ jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
+ return NULL;
+ }
+
+ return format;
+}
+
static void android_media_MediaExtractor_selectTrack(
JNIEnv *env, jobject thiz, jint index) {
sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
@@ -768,6 +800,9 @@
{ "getTrackCount", "()I", (void *)android_media_MediaExtractor_getTrackCount },
+ { "getFileFormatNative", "()Ljava/util/Map;",
+ (void *)android_media_MediaExtractor_getFileFormatNative },
+
{ "getTrackFormatNative", "(I)Ljava/util/Map;",
(void *)android_media_MediaExtractor_getTrackFormatNative },
diff --git a/media/jni/android_media_MediaExtractor.h b/media/jni/android_media_MediaExtractor.h
index 03900db..ccbad8c 100644
--- a/media/jni/android_media_MediaExtractor.h
+++ b/media/jni/android_media_MediaExtractor.h
@@ -45,6 +45,8 @@
size_t countTracks() const;
status_t getTrackFormat(size_t index, jobject *format) const;
+ status_t getFileFormat(jobject *format) const;
+
status_t selectTrack(size_t index);
status_t unselectTrack(size_t index);
diff --git a/packages/SystemUI/src/com/android/systemui/UniverseBackground.java b/packages/SystemUI/src/com/android/systemui/UniverseBackground.java
index 7628754..f859880 100644
--- a/packages/SystemUI/src/com/android/systemui/UniverseBackground.java
+++ b/packages/SystemUI/src/com/android/systemui/UniverseBackground.java
@@ -97,7 +97,7 @@
public UniverseBackground(Context context) {
super(context);
setBackgroundColor(0xff000000);
- mSession = WindowManagerGlobal.getWindowSession(context.getMainLooper());
+ mSession = WindowManagerGlobal.getWindowSession();
mContent = View.inflate(context, R.layout.universe, null);
addView(mContent);
mContent.findViewById(R.id.close).setOnClickListener(new View.OnClickListener() {
diff --git a/packages/SystemUI/src/com/android/systemui/recent/FirstFrameAnimatorHelper.java b/packages/SystemUI/src/com/android/systemui/recent/FirstFrameAnimatorHelper.java
new file mode 100644
index 0000000..2fc7dfc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recent/FirstFrameAnimatorHelper.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recent;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.animation.Animator.AnimatorListener;
+import android.util.Log;
+import android.view.ViewTreeObserver;
+import android.view.View;
+import android.view.ViewPropertyAnimator;
+
+/*
+ * This is a helper class that listens to updates from the corresponding animation.
+ * For the first two frames, it adjusts the current play time of the animation to
+ * prevent jank at the beginning of the animation
+ */
+public class FirstFrameAnimatorHelper implements ValueAnimator.AnimatorUpdateListener {
+ private static final boolean DEBUG = false;
+ private static final int MAX_DELAY = 1000;
+ private static final int IDEAL_FRAME_DURATION = 16;
+ private View mTarget;
+ private long mStartFrame;
+ private long mStartTime = -1;
+ private boolean mHandlingOnAnimationUpdate;
+ private boolean mAdjustedSecondFrameTime;
+
+ private static ViewTreeObserver.OnDrawListener sGlobalDrawListener;
+ private static long sGlobalFrameCounter;
+
+ public FirstFrameAnimatorHelper(ValueAnimator animator, View target) {
+ mTarget = target;
+ animator.addUpdateListener(this);
+ }
+
+ public FirstFrameAnimatorHelper(ViewPropertyAnimator vpa, View target) {
+ mTarget = target;
+ vpa.setListener(new AnimatorListenerAdapter() {
+ public void onAnimationStart (Animator animation) {
+ final ValueAnimator va = (ValueAnimator) animation;
+ va.addUpdateListener(FirstFrameAnimatorHelper.this);
+ onAnimationUpdate(va);
+ }
+ });
+ }
+
+ public static void initializeDrawListener(View view) {
+ if (sGlobalDrawListener != null) {
+ view.getViewTreeObserver().removeOnDrawListener(sGlobalDrawListener);
+ }
+ sGlobalDrawListener = new ViewTreeObserver.OnDrawListener() {
+ private long mTime = System.currentTimeMillis();
+ public void onDraw() {
+ sGlobalFrameCounter++;
+ if (DEBUG) {
+ long newTime = System.currentTimeMillis();
+ Log.d("FirstFrameAnimatorHelper", "TICK " + (newTime - mTime));
+ mTime = newTime;
+ }
+ }
+ };
+ view.getViewTreeObserver().addOnDrawListener(sGlobalDrawListener);
+ }
+
+ public void onAnimationUpdate(final ValueAnimator animation) {
+ final long currentTime = System.currentTimeMillis();
+ if (mStartTime == -1) {
+ mStartFrame = sGlobalFrameCounter;
+ mStartTime = currentTime;
+ }
+
+ if (!mHandlingOnAnimationUpdate) {
+ mHandlingOnAnimationUpdate = true;
+ long frameNum = sGlobalFrameCounter - mStartFrame;
+ // If we haven't drawn our first frame, reset the time to t = 0
+ // (give up after MAX_DELAY ms of waiting though - might happen, for example, if we
+ // are no longer in the foreground and no frames are being rendered ever)
+ if (frameNum == 0 && currentTime < mStartTime + MAX_DELAY) {
+ // The first frame on animations doesn't always trigger an invalidate...
+ // force an invalidate here to make sure the animation continues to advance
+ mTarget.getRootView().invalidate();
+ animation.setCurrentPlayTime(0);
+
+ // For the second frame, if the first frame took more than 16ms,
+ // adjust the start time and pretend it took only 16ms anyway. This
+ // prevents a large jump in the animation due to an expensive first frame
+ } else if (frameNum == 1 && currentTime < mStartTime + MAX_DELAY &&
+ !mAdjustedSecondFrameTime &&
+ currentTime > mStartTime + IDEAL_FRAME_DURATION) {
+ animation.setCurrentPlayTime(IDEAL_FRAME_DURATION);
+ mAdjustedSecondFrameTime = true;
+ } else {
+ if (frameNum > 1) {
+ mTarget.post(new Runnable() {
+ public void run() {
+ animation.removeUpdateListener(FirstFrameAnimatorHelper.this);
+ }
+ });
+ }
+ if (DEBUG) print(animation);
+ }
+ mHandlingOnAnimationUpdate = false;
+ } else {
+ if (DEBUG) print(animation);
+ }
+ }
+
+ public void print(ValueAnimator animation) {
+ float flatFraction = animation.getCurrentPlayTime() / (float) animation.getDuration();
+ Log.d("FirstFrameAnimatorHelper", sGlobalFrameCounter +
+ "(" + (sGlobalFrameCounter - mStartFrame) + ") " + mTarget + " dirty? " +
+ mTarget.isDirty() + " " + flatFraction + " " + this + " " + animation);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
index 9c2bca9..32759de 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
@@ -45,8 +45,7 @@
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
-import android.view.ViewTreeObserver;
-import android.view.ViewTreeObserver.OnGlobalLayoutListener;
+import android.view.ViewPropertyAnimator;
import android.view.accessibility.AccessibilityEvent;
import android.view.animation.AnimationUtils;
import android.view.animation.DecelerateInterpolator;
@@ -195,50 +194,27 @@
oldHolder.calloutLine.setTranslationY(0f);
}
}
- mItemToAnimateInWhenWindowAnimationIsFinished = null;
-
- final ViewTreeObserver observer = getViewTreeObserver();
- final OnGlobalLayoutListener animateFirstIcon = new OnGlobalLayoutListener() {
- public void onGlobalLayout() {
- ViewHolder oldHolder = mItemToAnimateInWhenWindowAnimationIsFinished;
- if (oldHolder != null) {
- oldHolder.iconView.setAlpha(1f);
- oldHolder.iconView.setTranslationX(0f);
- oldHolder.iconView.setTranslationY(0f);
- oldHolder.labelView.setAlpha(1f);
- oldHolder.labelView.setTranslationX(0f);
- oldHolder.labelView.setTranslationY(0f);
- if (oldHolder.calloutLine != null) {
- oldHolder.calloutLine.setAlpha(1f);
- oldHolder.calloutLine.setTranslationX(0f);
- oldHolder.calloutLine.setTranslationY(0f);
- }
- }
- mItemToAnimateInWhenWindowAnimationIsFinished = holder;
- int translation = -getResources().getDimensionPixelSize(
- R.dimen.status_bar_recents_app_icon_translate_distance);
- final Configuration config = getResources().getConfiguration();
- if (config.orientation == Configuration.ORIENTATION_PORTRAIT) {
- if (getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
- translation = -translation;
- }
- holder.iconView.setAlpha(0f);
- holder.iconView.setTranslationX(translation);
- holder.labelView.setAlpha(0f);
- holder.labelView.setTranslationX(translation);
- holder.calloutLine.setAlpha(0f);
- holder.calloutLine.setTranslationX(translation);
- } else {
- holder.iconView.setAlpha(0f);
- holder.iconView.setTranslationY(translation);
- }
- if (!mWaitingForWindowAnimation) {
- animateInIconOfFirstTask();
- }
- getViewTreeObserver().removeOnGlobalLayoutListener(this);
+ mItemToAnimateInWhenWindowAnimationIsFinished = holder;
+ int translation = -getResources().getDimensionPixelSize(
+ R.dimen.status_bar_recents_app_icon_translate_distance);
+ final Configuration config = getResources().getConfiguration();
+ if (config.orientation == Configuration.ORIENTATION_PORTRAIT) {
+ if (getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
+ translation = -translation;
}
- };
- observer.addOnGlobalLayoutListener(animateFirstIcon);
+ holder.iconView.setAlpha(0f);
+ holder.iconView.setTranslationX(translation);
+ holder.labelView.setAlpha(0f);
+ holder.labelView.setTranslationX(translation);
+ holder.calloutLine.setAlpha(0f);
+ holder.calloutLine.setTranslationX(translation);
+ } else {
+ holder.iconView.setAlpha(0f);
+ holder.iconView.setTranslationY(translation);
+ }
+ if (!mWaitingForWindowAnimation) {
+ animateInIconOfFirstTask();
+ }
}
}
@@ -586,17 +562,20 @@
!mRecentTasksLoader.isFirstScreenful()) {
int timeSinceWindowAnimation =
(int) (System.currentTimeMillis() - mWindowAnimationStartTime);
- final int minStartDelay = 150;
+ final int minStartDelay = 125;
final int startDelay = Math.max(0, Math.min(
minStartDelay - timeSinceWindowAnimation, minStartDelay));
final int duration = 250;
final ViewHolder holder = mItemToAnimateInWhenWindowAnimationIsFinished;
final TimeInterpolator cubic = new DecelerateInterpolator(1.5f);
+ FirstFrameAnimatorHelper.initializeDrawListener(holder.iconView);
for (View v :
new View[] { holder.iconView, holder.labelView, holder.calloutLine }) {
if (v != null) {
- v.animate().translationX(0).translationY(0).alpha(1f).setStartDelay(startDelay)
+ ViewPropertyAnimator vpa = v.animate().translationX(0).translationY(0)
+ .alpha(1f).setStartDelay(startDelay)
.setDuration(duration).setInterpolator(cubic);
+ FirstFrameAnimatorHelper h = new FirstFrameAnimatorHelper(vpa, v);
}
}
mItemToAnimateInWhenWindowAnimationIsFinished = null;
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 7710f13..26a20b9 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -8350,13 +8350,13 @@
final String processName = app == null ? "system_server"
: (r == null ? "unknown" : r.processName);
- handleApplicationCrashInner(r, processName, crashInfo);
+ handleApplicationCrashInner("crash", r, processName, crashInfo);
}
/* Native crash reporting uses this inner version because it needs to be somewhat
* decoupled from the AM-managed cleanup lifecycle
*/
- void handleApplicationCrashInner(ProcessRecord r, String processName,
+ void handleApplicationCrashInner(String eventType, ProcessRecord r, String processName,
ApplicationErrorReport.CrashInfo crashInfo) {
EventLog.writeEvent(EventLogTags.AM_CRASH, Binder.getCallingPid(),
UserHandle.getUserId(Binder.getCallingUid()), processName,
@@ -8366,7 +8366,7 @@
crashInfo.throwFileName,
crashInfo.throwLineNumber);
- addErrorToDropBox("crash", r, processName, null, null, null, null, null, crashInfo);
+ addErrorToDropBox(eventType, r, processName, null, null, null, null, null, crashInfo);
crashApplication(r, crashInfo);
}
diff --git a/services/java/com/android/server/am/NativeCrashListener.java b/services/java/com/android/server/am/NativeCrashListener.java
index e83433f..0688c50 100644
--- a/services/java/com/android/server/am/NativeCrashListener.java
+++ b/services/java/com/android/server/am/NativeCrashListener.java
@@ -82,7 +82,7 @@
ci.stackTrace = mCrashReport;
if (DEBUG) Slog.v(TAG, "Calling handleApplicationCrash()");
- mAm.handleApplicationCrashInner(mApp, mApp.processName, ci);
+ mAm.handleApplicationCrashInner("native_crash", mApp, mApp.processName, ci);
if (DEBUG) Slog.v(TAG, "<-- handleApplicationCrash() returned");
} catch (Exception e) {
Slog.e(TAG, "Unable to report native crash", e);
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index ca7bba2..3d7dd63 100644
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -8647,14 +8647,16 @@
mSettings.writeLPr();
}
}
- // A user ID was deleted here. Go through all users and remove it from
- // KeyStore.
- final int appId = outInfo.removedAppId;
- if (appId != -1) {
- final KeyStore keyStore = KeyStore.getInstance();
- if (keyStore != null) {
- for (final int userId : sUserManager.getUserIds()) {
- keyStore.clearUid(UserHandle.getUid(userId, appId));
+ if (outInfo != null) {
+ // A user ID was deleted here. Go through all users and remove it
+ // from KeyStore.
+ final int appId = outInfo.removedAppId;
+ if (appId != -1) {
+ final KeyStore keyStore = KeyStore.getInstance();
+ if (keyStore != null) {
+ for (final int userId : sUserManager.getUserIds()) {
+ keyStore.clearUid(UserHandle.getUid(userId, appId));
+ }
}
}
}
diff --git a/tests/ImfTest/src/com/android/imftest/samples/ButtonActivity.java b/tests/ImfTest/src/com/android/imftest/samples/ButtonActivity.java
index 854a3f4..dbaedf9 100644
--- a/tests/ImfTest/src/com/android/imftest/samples/ButtonActivity.java
+++ b/tests/ImfTest/src/com/android/imftest/samples/ButtonActivity.java
@@ -47,7 +47,7 @@
{
public void onClick (View v)
{
- InputMethodManager imm = InputMethodManager.getInstance(instance);
+ InputMethodManager imm = InputMethodManager.getInstance();
if (mKeyboardIsActive)
{
imm.hideSoftInputFromInputMethod(v.getWindowToken(), 0);
diff --git a/tests/ImfTest/tests/src/com/android/imftest/samples/ImfBaseTestCase.java b/tests/ImfTest/tests/src/com/android/imftest/samples/ImfBaseTestCase.java
index bc77e04..32f80a3 100644
--- a/tests/ImfTest/tests/src/com/android/imftest/samples/ImfBaseTestCase.java
+++ b/tests/ImfTest/tests/src/com/android/imftest/samples/ImfBaseTestCase.java
@@ -66,7 +66,7 @@
mExpectAutoPop = (keyboardType == Configuration.KEYBOARD_NOKEYS ||
keyboardType == Configuration.KEYBOARD_UNDEFINED);
- mImm = InputMethodManager.getInstance(mTargetActivity);
+ mImm = InputMethodManager.getInstance();
KeyguardManager keyguardManager =
(KeyguardManager) getInstrumentation().getContext().getSystemService(
diff --git a/tools/layoutlib/bridge/src/android/view/inputmethod/InputMethodManager_Accessor.java b/tools/layoutlib/bridge/src/android/view/inputmethod/InputMethodManager_Accessor.java
index 7a6e52e..dc4f9c8 100644
--- a/tools/layoutlib/bridge/src/android/view/inputmethod/InputMethodManager_Accessor.java
+++ b/tools/layoutlib/bridge/src/android/view/inputmethod/InputMethodManager_Accessor.java
@@ -22,6 +22,6 @@
public class InputMethodManager_Accessor {
public static void resetInstance() {
- InputMethodManager.mInstance = null;
+ InputMethodManager.sInstance = null;
}
}
diff --git a/tools/layoutlib/bridge/src/android/view/inputmethod/InputMethodManager_Delegate.java b/tools/layoutlib/bridge/src/android/view/inputmethod/InputMethodManager_Delegate.java
index f056040..7c98847 100644
--- a/tools/layoutlib/bridge/src/android/view/inputmethod/InputMethodManager_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/view/inputmethod/InputMethodManager_Delegate.java
@@ -35,28 +35,15 @@
// ---- Overridden methods ----
@LayoutlibDelegate
- /*package*/ static InputMethodManager getInstance(Looper mainLooper) {
- synchronized (InputMethodManager.mInstanceSync) {
- if (InputMethodManager.mInstance != null) {
- return InputMethodManager.mInstance;
+ /*package*/ static InputMethodManager getInstance() {
+ synchronized (InputMethodManager.class) {
+ InputMethodManager imm = InputMethodManager.peekInstance();
+ if (imm == null) {
+ imm = new InputMethodManager(
+ new BridgeIInputMethodManager(), Looper.getMainLooper());
+ InputMethodManager.sInstance = imm;
}
-
- InputMethodManager.mInstance = new InputMethodManager(new BridgeIInputMethodManager(),
- mainLooper);
+ return imm;
}
- return InputMethodManager.mInstance;
- }
-
- @LayoutlibDelegate
- /*package*/ static InputMethodManager getInstance(Context context) {
- synchronized (InputMethodManager.mInstanceSync) {
- if (InputMethodManager.mInstance != null) {
- return InputMethodManager.mInstance;
- }
-
- InputMethodManager.mInstance = new InputMethodManager(new BridgeIInputMethodManager(),
- Looper.myLooper());
- }
- return InputMethodManager.mInstance;
}
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java
index f109e39..cbefd3d 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java
@@ -232,7 +232,7 @@
sCurrentContext = mContext;
// create an InputMethodManager
- InputMethodManager.getInstance(Looper.myLooper());
+ InputMethodManager.getInstance();
LayoutLog currentLog = mParams.getLog();
Bridge.setLog(currentLog);