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 |