am 3a2854bc
: Merge "Queues, queues, queues and input." into jb-mr2-dev
* commit '3a2854bcee08e3af3ca1b042c1fef6ca68f8c70a': Queues, queues, queues and input.
This commit is contained in:
@ -371,9 +371,9 @@ class ContextImpl extends Context {
|
||||
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() {
|
||||
|
@ -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.ViewTreeObserver.OnGlobalLayoutListener;
|
||||
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 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback2,
|
||||
|
||||
private NativeContentView mNativeContentView;
|
||||
private InputMethodManager mIMM;
|
||||
private InputMethodCallback mInputMethodCallback;
|
||||
|
||||
private int mNativeHandle;
|
||||
|
||||
@ -117,22 +128,6 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback2,
|
||||
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 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback2,
|
||||
ActivityInfo ai;
|
||||
|
||||
mIMM = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
mInputMethodCallback = new InputMethodCallback(this);
|
||||
|
||||
getWindow().takeSurface(this);
|
||||
getWindow().takeInputQueue(this);
|
||||
@ -353,7 +347,8 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback2,
|
||||
}
|
||||
|
||||
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) {
|
||||
|
@ -709,7 +709,7 @@ public class WallpaperManager {
|
||||
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 @@ public class WallpaperManager {
|
||||
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 class WallpaperManager {
|
||||
*/
|
||||
public void clearWallpaperOffsets(IBinder windowToken) {
|
||||
try {
|
||||
WindowManagerGlobal.getWindowSession(mContext.getMainLooper()).setWallpaperPosition(
|
||||
WindowManagerGlobal.getWindowSession().setWallpaperPosition(
|
||||
windowToken, -1, -1, -1, -1);
|
||||
} catch (RemoteException e) {
|
||||
// Ignore.
|
||||
|
@ -256,6 +256,12 @@ public abstract class Context {
|
||||
* 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();
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -191,6 +191,14 @@ public final class Looper {
|
||||
mThread = Thread.currentThread();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the current thread is this looper's thread.
|
||||
* @hide
|
||||
*/
|
||||
public boolean isCurrentThread() {
|
||||
return Thread.currentThread() == mThread;
|
||||
}
|
||||
|
||||
/**
|
||||
* Quits the looper.
|
||||
*
|
||||
|
@ -762,7 +762,7 @@ public abstract class WallpaperService extends Service {
|
||||
mWindowToken = wrapper.mWindowToken;
|
||||
mSurfaceHolder.setSizeFromLayout();
|
||||
mInitializing = true;
|
||||
mSession = WindowManagerGlobal.getWindowSession(getMainLooper());
|
||||
mSession = WindowManagerGlobal.getWindowSession();
|
||||
|
||||
mWindow.setSession(mSession);
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -136,11 +136,11 @@ public final class WindowManagerGlobal {
|
||||
}
|
||||
}
|
||||
|
||||
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 @@ public final class WindowManagerGlobal {
|
||||
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());
|
||||
}
|
||||
|
@ -34,9 +34,11 @@ import android.os.Message;
|
||||
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.InputEventSender;
|
||||
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 @@ public final class InputMethodManager {
|
||||
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 @@ public final class InputMethodManager {
|
||||
*/
|
||||
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 @@ public final class InputMethodManager {
|
||||
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 @@ public final class InputMethodManager {
|
||||
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 @@ public final class InputMethodManager {
|
||||
}
|
||||
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 @@ public final class InputMethodManager {
|
||||
mH = new H(looper);
|
||||
mIInputContext = new ControlledInputConnectionWrapper(looper,
|
||||
mDummyInputConnection, this);
|
||||
|
||||
if (mInstance == null) {
|
||||
mInstance = this;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -549,25 +557,15 @@ public final class InputMethodManager {
|
||||
* 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 @@ public final class InputMethodManager {
|
||||
* if it exists.
|
||||
* @hide
|
||||
*/
|
||||
static public InputMethodManager peekInstance() {
|
||||
return mInstance;
|
||||
public static InputMethodManager peekInstance() {
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
@ -1585,13 +1583,18 @@ public final class InputMethodManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 @@ public final class InputMethodManager {
|
||||
&& 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 @@ public final class InputMethodManager {
|
||||
* 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 @@ public final class InputMethodManager {
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -97,7 +97,7 @@ public class UniverseBackground extends FrameLayout {
|
||||
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() {
|
||||
|
@ -22,6 +22,6 @@ package android.view.inputmethod;
|
||||
public class InputMethodManager_Accessor {
|
||||
|
||||
public static void resetInstance() {
|
||||
InputMethodManager.mInstance = null;
|
||||
InputMethodManager.sInstance = null;
|
||||
}
|
||||
}
|
||||
|
@ -35,28 +35,15 @@ public class InputMethodManager_Delegate {
|
||||
// ---- 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;
|
||||
}
|
||||
}
|
||||
|
@ -232,7 +232,7 @@ public abstract class RenderAction<T extends RenderParams> extends FrameworkReso
|
||||
sCurrentContext = mContext;
|
||||
|
||||
// create an InputMethodManager
|
||||
InputMethodManager.getInstance(Looper.myLooper());
|
||||
InputMethodManager.getInstance();
|
||||
|
||||
LayoutLog currentLog = mParams.getLog();
|
||||
Bridge.setLog(currentLog);
|
||||
|
Reference in New Issue
Block a user