Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2590)

Unified Diff: content/public/android/java/src/org/chromium/content/browser/input/ChromiumBaseInputConnectionFactory.java

Issue 1278593004: Introduce ThreadedInputConnection behind a switch (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: content/public/android/java/src/org/chromium/content/browser/input/ChromiumBaseInputConnectionFactory.java
diff --git a/content/public/android/java/src/org/chromium/content/browser/input/ChromiumBaseInputConnectionFactory.java b/content/public/android/java/src/org/chromium/content/browser/input/ChromiumBaseInputConnectionFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..04734f35a6ff9b45a21a083f63c317ed99bffaf1
--- /dev/null
+++ b/content/public/android/java/src/org/chromium/content/browser/input/ChromiumBaseInputConnectionFactory.java
@@ -0,0 +1,153 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.content.browser.input;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.MessageQueue;
+import android.text.Editable;
+import android.view.View;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputMethodManager;
+
+import org.chromium.base.CommandLine;
+import org.chromium.base.Log;
+import org.chromium.content.common.ContentSwitches;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+
+/**
+ * Default factory for ChromiumBaseInputConnection classes.
+ */
+public class ChromiumBaseInputConnectionFactory {
+ private static final String TAG = "cr.Ime";
+
+ private ChromiumInputConnection mChromiumInputConnection;
+ private View mView;
aelias_OOO_until_Jul13 2015/09/30 00:10:03 This member variable is only used to recreate if t
Changwan Ryu 2016/01/19 07:31:53 Removed and moved the logic up to ImeAdapter.
+
+ public ChromiumBaseInputConnection get(
+ View view, ImeAdapter imeAdapter, Editable editable, EditorInfo outAttrs) {
+ if (CommandLine.getInstance().hasSwitch(ContentSwitches.USE_IME_THREAD)) {
+ ImeUtils.assertOnUiThread();
+ if (mChromiumInputConnection == null || view != mView) {
+ Log.w(TAG, "Creating ChromiumInputConnection...");
+ mChromiumInputConnection = new ChromiumInputConnection(view, imeAdapter);
+ ImeUtils.assertReally("View: " + view, mView == null || view == mView);
+ mView = view;
+ }
+ mChromiumInputConnection.reset();
+ mChromiumInputConnection.updateEditorInfo(
+ outAttrs, imeAdapter.getTextInputType(), imeAdapter.getTextInputFlags());
+ switchInputConnectionLooper(view.getContext(), view.getHandler());
+ return mChromiumInputConnection;
+ } else {
+ return new AdapterInputConnection(view, imeAdapter, editable, outAttrs);
+ }
+ }
+
+ /**
+ * Pump messages from a handler and add them to another handler.
+ *
+ * @param sourceHandler The source handler.
+ * @param targetHandler The target handler.
+ * @throws NoSuchFieldException
+ * @throws IllegalAccessException
+ * @throws IllegalArgumentException
+ */
+ private void pumpHandlerMessages(Handler sourceHandler, Handler targetHandler)
+ throws NoSuchFieldException, IllegalAccessException, IllegalArgumentException {
+ MessageQueue queue = sourceHandler.getLooper().getQueue();
+ Field messagesField = MessageQueue.class.getDeclaredField("mMessages");
+ Field nextField = Message.class.getDeclaredField("next");
+
+ synchronized (queue) {
+ // Copy messages first.
+ messagesField.setAccessible(true);
+ nextField.setAccessible(true);
+ Message msg = (Message) messagesField.get(queue);
+ while (msg != null) {
+ if (msg.getTarget() == sourceHandler) {
+ Log.d(TAG, "Copying a message to the new handler...");
+ Message newMsg = targetHandler.obtainMessage();
+ newMsg.copyFrom(msg);
+ targetHandler.sendMessage(newMsg);
+ }
+ msg = (Message) nextField.get(msg);
+ }
+ messagesField.setAccessible(false);
+ nextField.setAccessible(false);
+
+ // Now remove messages from the first handler.
+ sourceHandler.removeCallbacksAndMessages(null);
aelias_OOO_until_Jul13 2015/09/18 04:59:30 Is there any signal we can use to filter for IME-r
Changwan Ryu 2015/09/18 07:34:59 This removes callbacks and messages that belong to
+ }
+ }
+
+ /**
+ * Switch the looper in InputMethodManager so that future calls to InputConnection
+ * can run in IME thread.
+ * TODO(changwan): add more description
+ *
+ * @param context
+ * @param viewHandler
+ */
+ private void switchInputConnectionLooper(Context context, Handler viewHandler) {
Changwan Ryu 2015/09/17 06:58:01 Alex, this is the logic that implements the idea w
+ Log.d(TAG, "switchInputConnectionLooper");
+ // Retrieve the same singleton instance of InputMethodManager that called
+ // onCreateInputConnection().
+ final InputMethodManager inputMethodManager =
+ (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
+ final Handler icHandler = InputConnectionHandlerFactory.getInputConnectionHandler();
+ viewHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ Log.d(TAG, "switchInputConnectionLooper: replacing looper and handler");
+ try {
+ // inputMethodManager.mServedInputConnectionWrapper.mMainLooper =
+ // icHandler.getLooper();
+ Field inputConnectionWrapperField = InputMethodManager.class.getDeclaredField(
+ "mServedInputConnectionWrapper");
+ inputConnectionWrapperField.setAccessible(true);
+ Object inputConnectionWrapper =
+ inputConnectionWrapperField.get(inputMethodManager);
+ Class<?> iinputConnectionWrapperClass =
+ Class.forName("com.android.internal.view.IInputConnectionWrapper");
+ Field looperField =
+ iinputConnectionWrapperClass.getDeclaredField("mMainLooper");
+ looperField.setAccessible(true);
+ looperField.set(inputConnectionWrapper, icHandler.getLooper());
+ looperField.setAccessible(false);
+
+ // inputMethodManager.mServedInputConnectionWrapper.mH =
+ // new IInputConnectionWrapper.MyHandler(icHandler.getLooper());
+ Class<?> myHandlerClass = Class.forName(
+ "com.android.internal.view.IInputConnectionWrapper$MyHandler");
+ Constructor<?> ctor = myHandlerClass.getDeclaredConstructor(
+ iinputConnectionWrapperClass, Looper.class);
+ ctor.setAccessible(true);
+ Handler myHandler = (Handler) ctor.newInstance(
+ inputConnectionWrapper, icHandler.getLooper());
+ Field handlerField = iinputConnectionWrapperClass.getDeclaredField("mH");
+ ctor.setAccessible(false);
+ handlerField.setAccessible(true);
+ Handler oldHandler = (Handler) handlerField.get(inputConnectionWrapper);
+ handlerField.set(inputConnectionWrapper, myHandler);
+ handlerField.setAccessible(false);
+
+ pumpHandlerMessages(oldHandler, myHandler);
+ } catch (IllegalAccessException | IllegalArgumentException | NoSuchFieldException
+ | ClassNotFoundException | NoSuchMethodException | InstantiationException
+ | InvocationTargetException | NullPointerException e) {
+ e.printStackTrace();
+ return;
+ }
+ Log.d(TAG, "switchInputConnectionLooper: replacing done.");
+ }
+ });
+ }
+}

Powered by Google App Engine
This is Rietveld 408576698