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.VisibleForTesting; | |
| 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 // TODO(changwan): add unit tests once Robolectric supports Android API level >= 21. | |
| 23 // See crbug.com/588547 for details. | |
| 24 public class ThreadedInputConnectionFactory implements ChromiumBaseInputConnecti on.Factory { | |
| 25 private static final String TAG = "cr_Ime"; | |
| 26 private static final boolean DEBUG_LOGS = false; | |
| 27 | |
| 28 private final Handler mHandler; | |
| 29 private final InputMethodManagerWrapper mInputMethodManagerWrapper; | |
| 30 private final ImeAdapter mImeAdapter; | |
| 31 private final InputMethodUma mInputMethodUma; | |
| 32 private ThreadedInputConnectionProxyView mProxyView; | |
| 33 private ThreadedInputConnection mThreadedInputConnection; | |
| 34 | |
| 35 ThreadedInputConnectionFactory( | |
| 36 InputMethodManagerWrapper inputMethodManagerWrapper, ImeAdapter imeA dapter) { | |
| 37 mImeAdapter = imeAdapter; | |
| 38 mInputMethodManagerWrapper = inputMethodManagerWrapper; | |
| 39 mHandler = createHandler(); | |
| 40 mInputMethodUma = createInputMethodUma(); | |
| 41 } | |
| 42 | |
| 43 @VisibleForTesting | |
| 44 protected Handler createHandler() { | |
| 45 HandlerThread thread = | |
| 46 new HandlerThread("InputConnectionHandlerThread", HandlerThread. NORM_PRIORITY); | |
| 47 thread.start(); | |
| 48 return new Handler(thread.getLooper()); | |
| 49 } | |
| 50 | |
| 51 @VisibleForTesting | |
| 52 protected ThreadedInputConnectionProxyView createProxyView( | |
| 53 Handler handler, View containerView) { | |
| 54 return new ThreadedInputConnectionProxyView( | |
| 55 containerView.getContext(), handler, containerView); | |
| 56 } | |
| 57 | |
| 58 @VisibleForTesting | |
| 59 protected InputMethodUma createInputMethodUma() { | |
| 60 return new InputMethodUma(); | |
| 61 } | |
| 62 | |
| 63 private boolean shouldTriggerDelayedOnCreateInputConnection() { | |
| 64 for (StackTraceElement ste : Thread.currentThread().getStackTrace()) { | |
| 65 String className = ste.getClassName(); | |
| 66 if (className != null | |
| 67 && (className.contains(ThreadedInputConnectionProxyView.clas s.getName()) | |
| 68 || className.contains("TestInputMethodManagerWrapper"))) { | |
| 69 return false; | |
| 70 } | |
| 71 } | |
| 72 return true; | |
| 73 } | |
| 74 | |
| 75 @Override | |
| 76 public ThreadedInputConnection initializeAndGet( | |
| 77 View view, ImeAdapter imeAdapter, int inputType, int inputFlags, int selectionStart, | |
| 78 int selectionEnd, EditorInfo outAttrs) { | |
| 79 ImeUtils.checkOnUiThread(); | |
| 80 if (shouldTriggerDelayedOnCreateInputConnection()) { | |
| 81 triggerDelayedOnCreateInputConnection(view); | |
| 82 return null; | |
| 83 } | |
| 84 if (DEBUG_LOGS) Log.w(TAG, "initializeAndGet: called from proxy view"); | |
| 85 if (mThreadedInputConnection == null) { | |
| 86 if (DEBUG_LOGS) Log.w(TAG, "Creating ThreadedInputConnection..."); | |
| 87 mThreadedInputConnection = new ThreadedInputConnection(imeAdapter, m Handler); | |
| 88 } | |
| 89 mThreadedInputConnection.initializeOutAttrsOnUiThread(inputType, inputFl ags, | |
| 90 selectionStart, selectionEnd, outAttrs); | |
| 91 return mThreadedInputConnection; | |
| 92 } | |
| 93 | |
| 94 private void triggerDelayedOnCreateInputConnection(final View view) { | |
| 95 if (DEBUG_LOGS) Log.w(TAG, "triggerDelayedOnCreateInputConnection"); | |
| 96 if (mProxyView == null) { | |
| 97 mProxyView = createProxyView(mHandler, view); | |
| 98 } | |
| 99 mProxyView.requestFocus(); | |
| 100 view.getHandler().post(new Runnable() { | |
| 101 @Override | |
| 102 public void run() { | |
| 103 // This is a hack to make InputMethodManager believe that the pr oxy view | |
| 104 // now has a focus. As a result, InputMethodManager will think t hat mProxyView | |
| 105 // is focused, and will call getHandler() of the view when creat ing input | |
| 106 // connection. | |
| 107 | |
| 108 // Step 1: Set mProxyView as InputMethodManager#mNextServedView. | |
| 109 mProxyView.onWindowFocusChanged(true); | |
| 110 | |
| 111 // Step 2: Have InputMethodManager focus in on mNextServedView. | |
| 112 // As a result, IMM will call onCreateInputConnection() on mProx yView on the same | |
| 113 // thread as mProxyView.getHandler(). It will also call subseque nt InputConnection | |
| 114 // methods on this IME thread. | |
| 115 mInputMethodManagerWrapper.isActive(view); | |
| 116 | |
| 117 // Step 3: Check that the above hack worked. | |
| 118 mHandler.post(new Runnable() { | |
| 119 @Override | |
| 120 public void run() { | |
| 121 // Some other view already took focus. Container view sh ould be active | |
| 122 // otherwise regardless of whether proxy view is registe red or not. | |
| 123 if (!mInputMethodManagerWrapper.isActive(view)) return; | |
| 124 | |
| 125 // Success. | |
| 126 if (mInputMethodManagerWrapper.isActive(mProxyView)) { | |
| 127 mInputMethodUma.recordProxyViewSuccess(); | |
| 128 return; | |
| 129 } | |
| 130 | |
| 131 if (mThreadedInputConnection == null) { | |
| 132 // First time and failed. It is highly likely that t his does not work | |
| 133 // systematically. | |
| 134 Log.w(TAG, "Failed to register proxy view, falling b ack to " | |
| 135 + "ReplicaInputConnection..."); | |
| 136 mInputMethodUma.recordProxyViewFailure(); | |
| 137 ThreadUtils.postOnUiThread(new Runnable() { | |
| 138 @Override | |
| 139 public void run() { | |
| 140 fallbackToReplicaInputConnection(view); | |
| 141 } | |
| 142 }); | |
| 143 } else { | |
| 144 // Most likely that we already lost view focus. | |
| 145 mInputMethodUma.recordProxyViewDetectionFailure(); | |
| 146 } | |
| 147 } | |
| 148 }); | |
| 149 } | |
| 150 }); | |
| 151 } | |
| 152 | |
| 153 @VisibleForTesting | |
| 154 protected void fallbackToReplicaInputConnection(final View view) { | |
|
aelias_OOO_until_Jul13
2016/02/23 23:59:30
Because we plan to only ship ThreadedInputConnecti
Changwan Ryu
2016/02/24 00:43:29
Removed fallback mechanism and now we crash instea
| |
| 155 // Disable IME thread until Chrome gets closed. Note that this is not a permanent | |
| 156 // change. | |
| 157 CommandLine.getInstance().appendSwitch(ContentSwitches.DISABLE_IME_THREA D); | |
| 158 mImeAdapter.disableImeThread(); | |
| 159 mImeAdapter.resetInputConnectionFactory(); | |
| 160 mInputMethodManagerWrapper.restartInput(view); | |
| 161 } | |
| 162 | |
| 163 @Override | |
| 164 public Handler getHandler() { | |
| 165 return mHandler; | |
| 166 } | |
| 167 } | |
| OLD | NEW |