Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 package org.chromium.content.browser.input; | |
| 6 | |
| 7 import android.os.Handler; | |
| 8 import android.os.HandlerThread; | |
| 9 import android.view.View; | |
| 10 import android.view.inputmethod.EditorInfo; | |
| 11 | |
| 12 import org.chromium.base.CommandLine; | |
| 13 import org.chromium.base.Log; | |
| 14 import org.chromium.base.ThreadUtils; | |
| 15 import org.chromium.base.metrics.RecordHistogram; | |
| 16 import org.chromium.content.common.ContentSwitches; | |
| 17 | |
| 18 /** | |
| 19 * A factory class for {@link ThreadedInputConnection}. The class also includes triggering | |
| 20 * mechanism (hack) to run our InputConnection on non-UI thread. | |
| 21 */ | |
| 22 public class ThreadedInputConnectionFactory implements ChromiumBaseInputConnecti on.Factory { | |
| 23 private static final String TAG = "cr_Ime"; | |
| 24 private static final boolean DEBUG_LOGS = true; | |
|
Ted C
2016/02/19 18:26:17
don't forget to set this to false before committin
Changwan Ryu
2016/02/22 06:28:24
Done.
| |
| 25 | |
| 26 private final Handler mHandler; | |
| 27 private final InputMethodManagerWrapper mInputMethodManagerWrapper; | |
| 28 private final ImeAdapter mImeAdapter; | |
| 29 private ThreadedInputConnectionProxyView mProxyView; | |
| 30 private ThreadedInputConnection mThreadedInputConnection; | |
| 31 | |
| 32 ThreadedInputConnectionFactory( | |
| 33 InputMethodManagerWrapper inputMethodManagerWrapper, ImeAdapter imeA dapter) { | |
| 34 mImeAdapter = imeAdapter; | |
| 35 HandlerThread thread = | |
| 36 new HandlerThread("InputConnectionHandlerThread", HandlerThread. NORM_PRIORITY); | |
| 37 thread.start(); | |
| 38 mHandler = new Handler(thread.getLooper()); | |
| 39 mInputMethodManagerWrapper = inputMethodManagerWrapper; | |
| 40 } | |
| 41 | |
| 42 private boolean shouldTriggerDelayedOnCreateInputConnection() { | |
| 43 for (StackTraceElement ste : Thread.currentThread().getStackTrace()) { | |
| 44 String className = ste.getClassName(); | |
| 45 if (className != null | |
| 46 && (className.contains(ThreadedInputConnectionProxyView.clas s.getName()) | |
| 47 || className.contains("TestInputMethodManagerWrapper"))) { | |
| 48 return false; | |
| 49 } | |
| 50 } | |
| 51 return true; | |
| 52 } | |
| 53 | |
| 54 @Override | |
| 55 public ChromiumBaseInputConnection initializeAndGet( | |
| 56 View view, ImeAdapter imeAdapter, int inputType, int inputFlags, int selectionStart, | |
| 57 int selectionEnd, EditorInfo outAttrs) { | |
| 58 ImeUtils.checkOnUiThread(); | |
| 59 if (shouldTriggerDelayedOnCreateInputConnection()) { | |
| 60 triggerDelayedOnCreateInputConnection(view); | |
| 61 return null; | |
| 62 } | |
| 63 if (DEBUG_LOGS) Log.w(TAG, "initializeAndGet: called from proxy view"); | |
| 64 if (mThreadedInputConnection == null) { | |
| 65 if (DEBUG_LOGS) Log.w(TAG, "Creating ThreadedInputConnection..."); | |
| 66 mThreadedInputConnection = new ThreadedInputConnection(imeAdapter, m Handler); | |
| 67 } | |
| 68 mThreadedInputConnection.initializeOutAttrsOnUiThread(inputType, inputFl ags, | |
| 69 selectionStart, selectionEnd, outAttrs); | |
| 70 return mThreadedInputConnection; | |
| 71 } | |
| 72 | |
| 73 private void triggerDelayedOnCreateInputConnection(final View view) { | |
| 74 if (DEBUG_LOGS) Log.w(TAG, "triggerDelayedOnCreateInputConnection"); | |
| 75 if (mProxyView == null) { | |
| 76 mProxyView = new ThreadedInputConnectionProxyView(view.getContext(), mHandler, view); | |
| 77 } | |
| 78 mProxyView.requestFocus(); | |
| 79 view.getHandler().post(new Runnable() { | |
| 80 @Override | |
| 81 public void run() { | |
| 82 // This is a hack to make InputMethodManager believe that the pr oxy view | |
| 83 // now has a focus. As a result, InputMethodManager will think t hat mProxyView | |
| 84 // is focused, and will call getHandler() of the view when creat ing input | |
| 85 // connection. | |
| 86 | |
| 87 // Step 1: Set mProxyView as InputMethodManager#mNextServedView. | |
| 88 // mProxyView.onWindowFocusChanged(true); | |
| 89 | |
| 90 // Step 2: Have InputMethodManager focus in on mNextServedView. | |
| 91 // As a result, IMM will call onCreateInputConnection() on mProx yView on the same | |
| 92 // thread as mProxyView.getHandler(). It will also call subseque nt InputConnection | |
| 93 // methods on this IME thread. | |
| 94 mInputMethodManagerWrapper.isActive(view); | |
| 95 | |
| 96 // Step 3: Check that the above hack worked. | |
| 97 mHandler.post(new Runnable() { | |
| 98 @Override | |
| 99 public void run() { | |
| 100 // Some other view already took focus. Container view sh ould be active | |
| 101 // otherwise regardless of whether proxy view is registe red or not. | |
| 102 if (!mInputMethodManagerWrapper.isActive(view)) return; | |
| 103 | |
| 104 // Success. | |
| 105 if (mInputMethodManagerWrapper.isActive(mProxyView)) { | |
| 106 RecordHistogram.recordEnumeratedHistogram( | |
| 107 InputMethodUma.UMA_REGISTER_PROXYVIEW, | |
| 108 InputMethodUma.UMA_PROXYVIEW_SUCCESS, | |
| 109 InputMethodUma.UMA_PROXYVIEW_COUNT); | |
| 110 return; | |
| 111 } | |
| 112 | |
| 113 if (mThreadedInputConnection == null) { | |
| 114 // First time and failed. It is highly likely that t his does not work | |
| 115 // systematically. | |
| 116 onProxyViewFailedToRegisterOnFirstTry(view); | |
| 117 } else { | |
| 118 // Most likely that we already lost view focus. | |
| 119 RecordHistogram.recordEnumeratedHistogram( | |
| 120 InputMethodUma.UMA_REGISTER_PROXYVIEW, | |
| 121 InputMethodUma.UMA_PROXYVIEW_DETECTION_FAILU RE, | |
| 122 InputMethodUma.UMA_PROXYVIEW_COUNT); | |
| 123 } | |
| 124 } | |
| 125 }); | |
| 126 } | |
| 127 }); | |
| 128 } | |
| 129 | |
| 130 private void onProxyViewFailedToRegisterOnFirstTry(final View view) { | |
|
Ted C
2016/02/19 18:26:17
would it be possible add a test for this (i.e. the
Changwan Ryu
2016/02/22 06:28:24
Hmm.. We're not using real InputMethodManager in t
| |
| 131 Log.w(TAG, "Failed to register proxy view. Falling back to ReplicaInputC onnection..."); | |
| 132 ThreadUtils.postOnUiThread(new Runnable() { | |
| 133 @Override | |
| 134 public void run() { | |
| 135 RecordHistogram.recordEnumeratedHistogram(InputMethodUma.UMA_REG ISTER_PROXYVIEW, | |
| 136 InputMethodUma.UMA_PROXYVIEW_FAILURE, InputMethodUma.UMA _PROXYVIEW_COUNT); | |
| 137 // Disable IME thread until Chrome gets closed. Note that this i s not a permanent | |
| 138 // change. | |
|
Ted C
2016/02/19 18:26:18
what about it isn't permanent? are you clearing t
Changwan Ryu
2016/02/19 22:17:11
I just wanted to comment that switch enabled by ap
| |
| 139 CommandLine.getInstance().appendSwitch(ContentSwitches.DISABLE_I ME_THREAD); | |
| 140 mImeAdapter.resetInputConnectionFactory(); | |
| 141 mInputMethodManagerWrapper.restartInput(view); | |
| 142 } | |
| 143 | |
| 144 }); | |
| 145 } | |
| 146 | |
| 147 @Override | |
| 148 public Handler getHandler() { | |
| 149 return mHandler; | |
| 150 } | |
| 151 } | |
| OLD | NEW |