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

Side by Side Diff: content/public/android/java/src/org/chromium/content/browser/input/CursorAnchorInfoController.java

Issue 2650113004: [WIP] Add support for Android SuggestionSpans when editing text (Closed)
Patch Set: Uploading the latest version from my repo so I can reference it Created 3 years, 7 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 unified diff | Download patch
OLDNEW
1 // Copyright 2016 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 package org.chromium.content.browser.input; 5 package org.chromium.content.browser.input;
6 6
7 import android.annotation.TargetApi; 7 import android.annotation.TargetApi;
8 import android.graphics.Matrix; 8 import android.graphics.Matrix;
9 import android.os.Build; 9 import android.os.Build;
10 import android.view.View; 10 import android.view.View;
11 import android.view.inputmethod.CursorAnchorInfo; 11 import android.view.inputmethod.CursorAnchorInfo;
12 12
13 import org.chromium.base.VisibleForTesting; 13 import org.chromium.base.VisibleForTesting;
14 import org.chromium.base.annotations.SuppressFBWarnings; 14 import org.chromium.base.annotations.SuppressFBWarnings;
15 import org.chromium.content.browser.RenderCoordinates; 15 import org.chromium.content.browser.RenderCoordinates;
16 16
17 import java.util.ArrayList;
17 import java.util.Arrays; 18 import java.util.Arrays;
19 import java.util.HashMap;
18 20
19 import javax.annotation.Nonnull; 21 import javax.annotation.Nonnull;
20 import javax.annotation.Nullable; 22 import javax.annotation.Nullable;
21 23
22 /** 24 /**
23 * A state machine interface which receives Chromium internal events to determin es when to call 25 * A state machine interface which receives Chromium internal events to determin es when to call
24 * {@link InputMethodManager#updateCursorAnchorInfo(View, CursorAnchorInfo)}. Th is interface is 26 * {@link InputMethodManager#updateCursorAnchorInfo(View, CursorAnchorInfo)}. Th is interface is
25 * also used in unit tests to mock out {@link CursorAnchorInfo}, which is availa ble only in 27 * also used in unit tests to mock out {@link CursorAnchorInfo}, which is availa ble only in
26 * Android 5.0 (Lollipop) and later. 28 * Android 5.0 (Lollipop) and later.
27 */ 29 */
(...skipping 10 matching lines...) Expand all
38 * An interface to mock out composing text retrieval from ImeAdapter. 40 * An interface to mock out composing text retrieval from ImeAdapter.
39 */ 41 */
40 public interface ComposingTextDelegate { 42 public interface ComposingTextDelegate {
41 CharSequence getText(); 43 CharSequence getText();
42 int getSelectionStart(); 44 int getSelectionStart();
43 int getSelectionEnd(); 45 int getSelectionEnd();
44 int getComposingTextStart(); 46 int getComposingTextStart();
45 int getComposingTextEnd(); 47 int getComposingTextEnd();
46 } 48 }
47 49
50 /**
51 * An interface implemented by classes that want to listener to
52 * CursorAnchorInfo updates.
53 */
54 public interface Listener { void onCursorAnchorInfoUpdate(CursorAnchorInfo i nfo); }
55
48 // Current focus and monitoring states. 56 // Current focus and monitoring states.
49 private boolean mIsEditable; 57 private boolean mIsEditable;
50 private boolean mHasPendingImmediateRequest; 58 private boolean mHasPendingImmediateRequest;
51 private boolean mMonitorModeEnabled; 59 private boolean mMonitorModeEnabled;
52 60
53 // Parmeter for CursorAnchorInfo, updated by setCompositionCharacterBounds. 61 // Parmeter for CursorAnchorInfo, updated by setCompositionCharacterBounds.
54 @Nullable 62 @Nullable
55 private float[] mCompositionCharacterBounds; 63 private float[] mCompositionCharacterBounds;
56 // Paremeters for CursorAnchorInfo, updated by onUpdateFrameInfo. 64 // Paremeters for CursorAnchorInfo, updated by onUpdateFrameInfo.
57 private boolean mHasCoordinateInfo; 65 private boolean mHasCoordinateInfo;
(...skipping 17 matching lines...) Expand all
75 private final CursorAnchorInfo.Builder mCursorAnchorInfoBuilder = 83 private final CursorAnchorInfo.Builder mCursorAnchorInfoBuilder =
76 new CursorAnchorInfo.Builder(); 84 new CursorAnchorInfo.Builder();
77 85
78 @Nullable 86 @Nullable
79 private InputMethodManagerWrapper mInputMethodManagerWrapper; 87 private InputMethodManagerWrapper mInputMethodManagerWrapper;
80 @Nullable 88 @Nullable
81 private final ComposingTextDelegate mComposingTextDelegate; 89 private final ComposingTextDelegate mComposingTextDelegate;
82 @Nonnull 90 @Nonnull
83 private final ViewDelegate mViewDelegate; 91 private final ViewDelegate mViewDelegate;
84 92
93 @Nonnull
94 private final HashMap<View, ArrayList<Listener>> mListeners =
95 new HashMap<View, ArrayList<Listener>>();
96
85 private CursorAnchorInfoController(InputMethodManagerWrapper inputMethodMana gerWrapper, 97 private CursorAnchorInfoController(InputMethodManagerWrapper inputMethodMana gerWrapper,
86 ComposingTextDelegate composingTextDelegate, ViewDelegate viewDelega te) { 98 ComposingTextDelegate composingTextDelegate, ViewDelegate viewDelega te) {
87 mInputMethodManagerWrapper = inputMethodManagerWrapper; 99 mInputMethodManagerWrapper = inputMethodManagerWrapper;
88 mComposingTextDelegate = composingTextDelegate; 100 mComposingTextDelegate = composingTextDelegate;
89 mViewDelegate = viewDelegate; 101 mViewDelegate = viewDelegate;
90 } 102 }
91 103
92 public static CursorAnchorInfoController create( 104 public static CursorAnchorInfoController create(
93 InputMethodManagerWrapper inputMethodManagerWrapper, 105 InputMethodManagerWrapper inputMethodManagerWrapper,
94 ComposingTextDelegate composingTextDelegate) { 106 ComposingTextDelegate composingTextDelegate) {
95 return new CursorAnchorInfoController(inputMethodManagerWrapper, 107 return new CursorAnchorInfoController(
96 composingTextDelegate, new ViewDelegate() { 108 inputMethodManagerWrapper, composingTextDelegate, new ViewDelega te() {
97 @Override 109 @Override
98 public void getLocationOnScreen(View view, int[] location) { 110 public void getLocationOnScreen(View view, int[] location) {
99 view.getLocationOnScreen(location); 111 view.getLocationOnScreen(location);
100 } 112 }
101 }); 113 });
102 } 114 }
103 115
104 @VisibleForTesting 116 @VisibleForTesting
105 public void setInputMethodManagerWrapperForTest( 117 public void setInputMethodManagerWrapperForTest(
106 InputMethodManagerWrapper inputMethodManagerWrapper) { 118 InputMethodManagerWrapper inputMethodManagerWrapper) {
107 mInputMethodManagerWrapper = inputMethodManagerWrapper; 119 mInputMethodManagerWrapper = inputMethodManagerWrapper;
108 } 120 }
109 121
110 @VisibleForTesting 122 @VisibleForTesting
111 public static CursorAnchorInfoController createForTest( 123 public static CursorAnchorInfoController createForTest(
112 InputMethodManagerWrapper inputMethodManagerWrapper, 124 InputMethodManagerWrapper inputMethodManagerWrapper,
113 ComposingTextDelegate composingTextDelegate, 125 ComposingTextDelegate composingTextDelegate, ViewDelegate viewDelega te) {
114 ViewDelegate viewDelegate) { 126 return new CursorAnchorInfoController(
115 return new CursorAnchorInfoController(inputMethodManagerWrapper, composi ngTextDelegate, 127 inputMethodManagerWrapper, composingTextDelegate, viewDelegate);
116 viewDelegate);
117 } 128 }
118 129
119 /** 130 /**
120 * Called by ImeAdapter when a IME related web content state is changed. 131 * Called by ImeAdapter when a IME related web content state is changed.
121 */ 132 */
122 public void invalidateLastCursorAnchorInfo() { 133 public void invalidateLastCursorAnchorInfo() {
123 if (!mIsEditable) return; 134 if (!mIsEditable) return;
124 135
125 mLastCursorAnchorInfo = null; 136 mLastCursorAnchorInfo = null;
126 } 137 }
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after
186 mScale = scale; 197 mScale = scale;
187 mTranslationX = translationX; 198 mTranslationX = translationX;
188 mTranslationY = translationY; 199 mTranslationY = translationY;
189 mHasInsertionMarker = hasInsertionMarker; 200 mHasInsertionMarker = hasInsertionMarker;
190 mIsInsertionMarkerVisible = isInsertionMarkerVisible; 201 mIsInsertionMarkerVisible = isInsertionMarkerVisible;
191 mInsertionMarkerHorizontal = insertionMarkerHorizontal; 202 mInsertionMarkerHorizontal = insertionMarkerHorizontal;
192 mInsertionMarkerTop = insertionMarkerTop; 203 mInsertionMarkerTop = insertionMarkerTop;
193 mInsertionMarkerBottom = insertionMarkerBottom; 204 mInsertionMarkerBottom = insertionMarkerBottom;
194 } 205 }
195 206
196 // Notify to IME if there is a pending request, or if it is in monitor m ode and we have 207 // Notify to IME if there is a pending request, or if we're monitoring
197 // some change in the state. 208 // for the view and we have some change in the state.
198 if (mHasPendingImmediateRequest 209 if (mHasPendingImmediateRequest
199 || (mMonitorModeEnabled && mLastCursorAnchorInfo == null)) { 210 || (monitoringForView(view) && mLastCursorAnchorInfo == null)) {
200 updateCursorAnchorInfo(view); 211 updateCursorAnchorInfo(view);
201 } 212 }
202 } 213 }
203 214
204 public void focusedNodeChanged(boolean isEditable) { 215 public void focusedNodeChanged(boolean isEditable) {
205 mIsEditable = isEditable; 216 mIsEditable = isEditable;
206 mCompositionCharacterBounds = null; 217 mCompositionCharacterBounds = null;
207 mHasCoordinateInfo = false; 218 mHasCoordinateInfo = false;
208 mLastCursorAnchorInfo = null; 219 mLastCursorAnchorInfo = null;
209 } 220 }
210 221
211 public boolean onRequestCursorUpdates(boolean immediateRequest, boolean moni torRequest, 222 public boolean onRequestCursorUpdates(boolean immediateRequest, boolean moni torRequest,
212 View view) { 223 View view) {
213 if (!mIsEditable) return false; 224 if (!mIsEditable) return false;
214 225
215 if (mMonitorModeEnabled && !monitorRequest) { 226 if (!monitorRequest && monitoringForView(view)) {
216 // Invalidate saved cursor anchor info if monitor request is cancell ed since no longer 227 // Invalidate saved cursor anchor info if we're no longer going to
217 // new values will be arrived from renderer and immediate request ma y return too old 228 // be monitoring for this view to prevent returning stale values for
218 // position. 229 // immediate requests.
219 invalidateLastCursorAnchorInfo(); 230 if (!mListeners.containsKey(view) || mListeners.get(view).isEmpty()) {
231 invalidateLastCursorAnchorInfo();
232 }
220 } 233 }
234
221 mMonitorModeEnabled = monitorRequest; 235 mMonitorModeEnabled = monitorRequest;
222 if (immediateRequest) { 236 if (immediateRequest) {
223 mHasPendingImmediateRequest = true; 237 mHasPendingImmediateRequest = true;
224 updateCursorAnchorInfo(view); 238 updateCursorAnchorInfo(view);
225 } 239 }
226 return true; 240 return true;
227 } 241 }
228 242
243 public void addListener(Listener listener, View view) {
244 if (!mListeners.containsKey(view)) {
245 mListeners.put(view, new ArrayList<Listener>());
246 }
247 mListeners.get(view).add(listener);
248
249 mHasPendingImmediateRequest = true;
250 }
251
252 public void removeListener(Listener listener, View view) {
253 mListeners.get(view).remove(listener);
254 }
255
256 private boolean monitoringForView(View view) {
257 if (mMonitorModeEnabled) {
258 return true;
259 }
260
261 return mListeners.containsKey(view) && !mListeners.get(view).isEmpty();
262 }
263
229 /** 264 /**
230 * Computes the CursorAnchorInfo instance and notify to InputMethodManager i f needed. 265 * Computes the CursorAnchorInfo instance and notify to InputMethodManager i f needed.
231 */ 266 */
232 private void updateCursorAnchorInfo(View view) { 267 private void updateCursorAnchorInfo(View view) {
233 if (!mHasCoordinateInfo) return; 268 if (!mHasCoordinateInfo) return;
234 269
235 if (mLastCursorAnchorInfo == null) { 270 if (mLastCursorAnchorInfo == null) {
236 // Reuse the builder object. 271 // Reuse the builder object.
237 mCursorAnchorInfoBuilder.reset(); 272 mCursorAnchorInfoBuilder.reset();
238 273
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
270 mInsertionMarkerBottom, 305 mInsertionMarkerBottom,
271 mIsInsertionMarkerVisible ? CursorAnchorInfo.FLAG_HAS_VI SIBLE_REGION : 306 mIsInsertionMarkerVisible ? CursorAnchorInfo.FLAG_HAS_VI SIBLE_REGION :
272 CursorAnchorInfo.FLAG_HAS_INVISIBLE_REGION); 307 CursorAnchorInfo.FLAG_HAS_INVISIBLE_REGION);
273 } 308 }
274 mLastCursorAnchorInfo = mCursorAnchorInfoBuilder.build(); 309 mLastCursorAnchorInfo = mCursorAnchorInfoBuilder.build();
275 } 310 }
276 311
277 if (mInputMethodManagerWrapper != null) { 312 if (mInputMethodManagerWrapper != null) {
278 mInputMethodManagerWrapper.updateCursorAnchorInfo(view, mLastCursorA nchorInfo); 313 mInputMethodManagerWrapper.updateCursorAnchorInfo(view, mLastCursorA nchorInfo);
279 } 314 }
315
316 if (mListeners.containsKey(view)) {
317 for (Listener listener : mListeners.get(view)) {
318 listener.onCursorAnchorInfoUpdate(mLastCursorAnchorInfo);
319 }
320 }
280 mHasPendingImmediateRequest = false; 321 mHasPendingImmediateRequest = false;
281 } 322 }
282 } 323 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698