| Index: content/public/android/java/src/org/chromium/content/browser/input/ThreadedInputConnectionFactory.java
|
| diff --git a/content/public/android/java/src/org/chromium/content/browser/input/ThreadedInputConnectionFactory.java b/content/public/android/java/src/org/chromium/content/browser/input/ThreadedInputConnectionFactory.java
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..29de9f1b1abb112c8979af8e35adb1fe76a28775
|
| --- /dev/null
|
| +++ b/content/public/android/java/src/org/chromium/content/browser/input/ThreadedInputConnectionFactory.java
|
| @@ -0,0 +1,101 @@
|
| +// Copyright 2016 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.os.Handler;
|
| +import android.os.HandlerThread;
|
| +import android.view.View;
|
| +import android.view.inputmethod.EditorInfo;
|
| +
|
| +import org.chromium.base.Log;
|
| +
|
| +/**
|
| + * Default factory for ChromiumBaseInputConnection classes.
|
| + */
|
| +public class ThreadedInputConnectionFactory implements ChromiumBaseInputConnection.Factory {
|
| + private static final String TAG = "cr_Ime";
|
| + private static final boolean DEBUG_LOGS = false;
|
| +
|
| + private ThreadedInputConnection mThreadedInputConnection;
|
| + private final Handler mHandler;
|
| + private final InputMethodManagerWrapper mInputMethodManagerWrapper;
|
| + private ThreadedInputConnectionProxyView mProxyView;
|
| +
|
| + ThreadedInputConnectionFactory(InputMethodManagerWrapper inputMethodManagerWrapper) {
|
| + HandlerThread thread =
|
| + new HandlerThread("InputConnectionHandlerThread", HandlerThread.NORM_PRIORITY);
|
| + thread.start();
|
| + mHandler = new Handler(thread.getLooper());
|
| + mInputMethodManagerWrapper = inputMethodManagerWrapper;
|
| + }
|
| +
|
| + private boolean shouldTriggerDelayedOnCreateInputConnection() {
|
| + for (StackTraceElement ste : Thread.currentThread().getStackTrace()) {
|
| + String className = ste.getClassName();
|
| + if (className != null
|
| + && (className.contains(ThreadedInputConnectionProxyView.class.getName())
|
| + || className.contains("TestInputMethodManagerWrapper"))) {
|
| + return false;
|
| + }
|
| + }
|
| + return true;
|
| + }
|
| +
|
| + @Override
|
| + public ChromiumBaseInputConnection initializeAndGet(
|
| + View view, ImeAdapter imeAdapter, int inputType, int inputFlags, int selectionStart,
|
| + int selectionEnd, EditorInfo outAttrs) {
|
| + ImeUtils.checkOnUiThread();
|
| + if (shouldTriggerDelayedOnCreateInputConnection()) {
|
| + triggerDelayedOnCreateInputConnection(view);
|
| + return null;
|
| + }
|
| + if (DEBUG_LOGS) Log.w(TAG, "initializeAndGet: called from proxy view");
|
| + if (mThreadedInputConnection == null) {
|
| + if (DEBUG_LOGS) Log.w(TAG, "Creating ThreadedInputConnection...");
|
| + mThreadedInputConnection = new ThreadedInputConnection(imeAdapter, mHandler);
|
| + }
|
| + mThreadedInputConnection.initializeOutAttrsOnUiThread(inputType, inputFlags,
|
| + selectionStart, selectionEnd, outAttrs);
|
| + return mThreadedInputConnection;
|
| + }
|
| +
|
| + private void triggerDelayedOnCreateInputConnection(final View view) {
|
| + if (DEBUG_LOGS) Log.w(TAG, "triggerDelayedOnCreateInputConnection");
|
| + if (mProxyView == null) {
|
| + mProxyView = new ThreadedInputConnectionProxyView(view.getContext(), mHandler, view);
|
| + }
|
| + mProxyView.requestFocus();
|
| + view.getHandler().post(new Runnable() {
|
| + @Override
|
| + public void run() {
|
| + // This is a hack to make InputMethodManager believe that the proxy view
|
| + // now has a focus. As a result, InputMethodManager will think that mProxyView
|
| + // is focused, and will call getHandler() of the view when creating input
|
| + // connection.
|
| +
|
| + // Step 1: Set mProxyView as InputMethodManager#mNextServedView.
|
| + mProxyView.onWindowFocusChanged(true);
|
| + // Step 2: Have InputMethodManager focus in on mNextServedView.
|
| + mInputMethodManagerWrapper.isActive(view);
|
| + // Step 3: Verify that the above hack worked.
|
| + mHandler.post(new Runnable() {
|
| + @Override
|
| + public void run() {
|
| + ImeUtils.checkCondition(mInputMethodManagerWrapper.isActive(mProxyView));
|
| + ImeUtils.checkCondition(mInputMethodManagerWrapper.isActive(view));
|
| + }
|
| + });
|
| +
|
| + // TODO(changwan): come up with fallback plan or crash when the above hack fails.
|
| + }
|
| + });
|
| + }
|
| +
|
| + @Override
|
| + public Handler getHandler() {
|
| + return mHandler;
|
| + }
|
| +}
|
|
|