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

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

Issue 699333003: Support InputMethodManager#updateCursorAnchorInfo for Android 5.0 (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Move the core logic into Java side Created 5 years, 10 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
(Empty)
1 // Copyright 2014 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.graphics.Matrix;
8 import android.graphics.RectF;
9 import android.os.Build;
10 import android.text.TextUtils;
11 import android.view.View;
12 import android.view.inputmethod.CursorAnchorInfo;
13 import android.view.inputmethod.InputConnection;
14
15 import org.chromium.base.CommandLine;
16 import org.chromium.base.VisibleForTesting;
17 import org.chromium.content.browser.RenderCoordinates;
18 import org.chromium.content.common.ContentSwitches;
19
20 import java.util.Arrays;
21
22 import javax.annotation.Nonnull;
23 import javax.annotation.Nullable;
24
25 final class CursorAnchorInfoController {
26 public interface ViewDelegate {
27 void getLocationOnScreen(View view, int[] location);
28 }
29
30 private static final class CursorAnchorInfoWrapperImpl implements CursorAnch orInfoWrapper {
31 private static final class Builder implements CursorAnchorInfoWrapper.Bu ilder {
32 private final CursorAnchorInfo.Builder mBuilder = new CursorAnchorIn fo.Builder();
33
34 @Override
35 public CursorAnchorInfoWrapper.Builder setSelectionRange(int newStar t, int newEnd) {
36 mBuilder.setSelectionRange(newStart, newEnd);
37 return this;
38 }
39
40 @Override
41 public CursorAnchorInfoWrapper.Builder setComposingText(
42 int composingTextStart, CharSequence composingText) {
43 mBuilder.setComposingText(composingTextStart, composingText);
44 return this;
45 }
46
47 @Override
48 public CursorAnchorInfoWrapper.Builder setInsertionMarkerLocation(
49 float horizontalPosition, float lineTop, float lineBaseline, float lineBottom,
50 int flags) {
51 mBuilder.setInsertionMarkerLocation(horizontalPosition, lineTop, lineBaseline,
52 lineBottom, flags);
53 return this;
54 }
55
56 @Override
57 public CursorAnchorInfoWrapper.Builder addCharacterBounds(
58 int index, float left, float top, float right, float bottom, int flags) {
59 mBuilder.addCharacterBounds(index, left, top, right, bottom, fla gs);
60 return this;
61 }
62
63 @Override
64 public CursorAnchorInfoWrapper.Builder setMatrix(Matrix matrix) {
65 mBuilder.setMatrix(matrix);
66 return this;
67 }
68
69 @Override
70 public CursorAnchorInfoWrapper build() {
71 return new CursorAnchorInfoWrapperImpl(mBuilder.build());
72 }
73
74 @Override
75 public void reset() {
76 mBuilder.reset();
77 }
78 }
79
80 private final CursorAnchorInfo mObj;
81 public CursorAnchorInfoWrapperImpl(CursorAnchorInfo obj) {
82 mObj = obj;
83 }
84
85 @Override
86 public int getSelectionStart() {
87 return mObj.getSelectionStart();
88 }
89
90 @Override
91 public int getSelectionEnd() {
92 return mObj.getSelectionEnd();
93 }
94
95 @Override
96 public int getComposingTextStart() {
97 return mObj.getComposingTextStart();
98 }
99
100 @Override
101 public CharSequence getComposingText() {
102 return mObj.getComposingText();
103 }
104
105 @Override
106 public int getInsertionMarkerFlags() {
107 return mObj.getInsertionMarkerFlags();
108 }
109
110 @Override
111 public float getInsertionMarkerHorizontal() {
112 return mObj.getInsertionMarkerHorizontal();
113 }
114
115 @Override
116 public float getInsertionMarkerTop() {
117 return mObj.getInsertionMarkerTop();
118 }
119
120 @Override
121 public float getInsertionMarkerBaseline() {
122 return mObj.getInsertionMarkerBaseline();
123 }
124
125 @Override
126 public float getInsertionMarkerBottom() {
127 return mObj.getInsertionMarkerBottom();
128 }
129
130 @Override
131 public RectF getCharacterBounds(int index) {
132 return mObj.getCharacterBounds(index);
133 }
134
135 @Override
136 public int getCharacterBoundsFlags(int index) {
137 return mObj.getCharacterBoundsFlags(index);
138 }
139
140 @Override
141 public Matrix getMatrix() {
142 return mObj.getMatrix();
143 }
144
145 @Override
146 public Object getObject() {
147 return mObj;
148 }
149 }
150
151 @Nullable
152 private CharSequence mText;
153 private int mSelectionStart;
154 private int mSelectionEnd;
155 private int mComposingTextStart;
156 private int mComposingTextEnd;
157 @Nullable
158 private float[] mCompositionCharacterBounds;
159 private boolean mHasInsertionMarker;
160 private boolean mIsInsertionMarkerVisible;
161 private float mInsertionMarkerHorizontal;
162 private float mInsertionMarkerTop;
163 private float mInsertionMarkerBottom;
164 private float mScale;
165 private float mTranslationX;
166 private float mTranslationY;
167 private boolean mIsEditable;
168 private boolean mHasPendingRequest;
169 private boolean mMonitorModeEnabled;
170 private boolean mHasCoordinateInfo;
171
172 @Nonnull
173 private final CursorAnchorInfoWrapper.Builder mCursorAnchorInfoBuilder;
174 @Nullable
175 private volatile CursorAnchorInfoWrapper mLastCursorAnchorInfo;
176
177 @Nonnull
178 private final Matrix mMatrix = new Matrix();
179 @Nonnull
180 private final int[] mViewOrigin = new int[2];
181 @Nonnull
182 private final ViewDelegate mViewDelegate;
183
184 @Nullable
185 private InputMethodManagerWrapper mInputMethodManagerWrapper;
186
187 private static final boolean sIsSupported = isSupportedInit();
188
189 /**
190 * @return {@code true} if {@link CursorAnchorInfo} is supported on this dev ice.
191 */
192 private static boolean isSupportedInit() {
193 if (CommandLine.getInstance() != null
194 && !CommandLine.getInstance().hasSwitch(ContentSwitches.ENABLE_C URSOR_ANCHOR_INFO))
195 return false;
196 return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP;
197 }
198
199 /**
200 * @return {@code true} if {@link CursorAnchorInfo} is supported on this dev ice.
201 */
202 public static boolean isSupported() {
203 return sIsSupported;
204 }
205
206 private CursorAnchorInfoController(InputMethodManagerWrapper inputMethodMana gerWrapper,
207 CursorAnchorInfoWrapper.Builder builder,
208 ViewDelegate viewDelegate) {
209 mInputMethodManagerWrapper = inputMethodManagerWrapper;
210 mCursorAnchorInfoBuilder = builder;
211 mViewDelegate = viewDelegate;
212 }
213
214 public static CursorAnchorInfoController create(
215 InputMethodManagerWrapper inputMethodManagerWrapper) {
216 return isSupported() ? new CursorAnchorInfoController(inputMethodManager Wrapper,
217 new CursorAnchorInfoWrapperImpl.Builder(), new ViewDelegate() {
218 @Override
219 public void getLocationOnScreen(View view, int[] location) {
220 view.getLocationOnScreen(location);
221 }
222 }) : null;
223 }
224
225 @VisibleForTesting
226 public void setInputMethodManagerWrapper(InputMethodManagerWrapper inputMeth odManagerWrapper) {
227 mInputMethodManagerWrapper = inputMethodManagerWrapper;
228 }
229
230 @VisibleForTesting
231 public static CursorAnchorInfoController createForTest(
232 InputMethodManagerWrapper inputMethodManagerWrapper,
233 CursorAnchorInfoWrapper.Builder builder,
234 ViewDelegate viewDelegate) {
235 return new CursorAnchorInfoController(inputMethodManagerWrapper, builder , viewDelegate);
236 }
237
238 /**
239 * @return Current composing text (if any). {@code null} otherwise.
240 */
241 @Nullable
242 public CharSequence getComposingText() {
243 if (mText == null) {
244 return null;
245 }
246 if (0 <= mComposingTextStart && mComposingTextStart <= mText.length()) {
247 return mText.subSequence(mComposingTextStart, mComposingTextEnd);
248 }
249 return "";
250 }
251
252 /**
253 * Updates text in the focused text area, selection range, and the composing text range.
254 * @param text Text in the focused text field.
255 * @param composingTextStart Index where the text composition starts. {@code -1} if there is
256 * no selection.
257 * @param composingTextEnd Index where the text composition ends. {@code -1} if there is no
258 * selection.
259 * @param selectionStart Index where the text selection starts. {@code -1} i f there is no
260 * selection.
261 * @param selectionEnd Index where the text selection ends. {@code -1} if th ere is no
262 * selection.
263 */
264 public void updateTextAndSelection(CharSequence text, int composingTextStart ,
265 int composingTextEnd, int selectionStart, int selectionEnd) {
266 if (!mIsEditable) {
267 return;
268 }
269 if (mLastCursorAnchorInfo == null && mMonitorModeEnabled) {
270 mHasPendingRequest = true;
271 }
272 if (mLastCursorAnchorInfo != null && !mHasPendingRequest) {
273 if (!TextUtils.equals(text, mText) || selectionStart != mSelectionSt art
274 || selectionEnd != mSelectionEnd || composingTextStart != mC omposingTextStart
275 || composingTextEnd != mComposingTextEnd) {
276 mLastCursorAnchorInfo = null;
277 if (mMonitorModeEnabled) {
278 mHasPendingRequest = true;
279 }
280 }
281 }
282 mText = text;
283 mSelectionStart = selectionStart;
284 mSelectionEnd = selectionEnd;
285 mComposingTextStart = composingTextStart;
286 mComposingTextEnd = composingTextEnd;
287 }
288
289 /**
290 * Sets positional information of composing text as an array of character bo unds.
291 * @param compositionCharacterBounds Array of character bounds in local coor dinates.
292 */
293 public void setCompositionCharacterBounds(float[] compositionCharacterBounds ) {
294 if (!mIsEditable) {
295 return;
296 }
297 if (mLastCursorAnchorInfo == null && mMonitorModeEnabled) {
298 mHasPendingRequest = true;
299 }
300 if (mLastCursorAnchorInfo != null && !mHasPendingRequest) {
301 if (!Arrays.equals(compositionCharacterBounds, mCompositionCharacter Bounds)) {
302 mLastCursorAnchorInfo = null;
303 if (mMonitorModeEnabled) {
304 mHasPendingRequest = true;
305 }
306 }
307 }
308 mCompositionCharacterBounds = compositionCharacterBounds;
309 }
310
311 /**
312 * Sets coordinates system parameters and selection marker information.
313 * @param hasInsertionMarker {@code true} if the insertion marker exists.
314 * @param isInsertionMarkerVisible {@code true} if the insertion insertion m arker is visible.
315 * @param insertionMarkerHorizontal X coordinate of the top of the first sel ection marker.
316 * @param insertionMarkerTop Y coordinate of the top of the first selection marker.
317 * @param insertionMarkerBottom Y coordinate of the bottom of the first sele ction marker.
318 * @param view The attached view.
319 */
320 public void onUpdateFrameInfo(@Nonnull RenderCoordinates renderCoordinates,
321 boolean hasInsertionMarker, boolean isInsertionMarkerVisible,
322 float insertionMarkerHorizontal, float insertionMarkerTop,
323 float insertionMarkerBottom, @Nonnull View view) {
324 if (!mIsEditable) {
325 return;
326 }
327
328 float scale =
329 renderCoordinates.getPageScaleFactor() * renderCoordinates.getDe viceScaleFactor();
330 float translationX;
331 float translationY;
332 synchronized (mViewOrigin) {
333 mViewDelegate.getLocationOnScreen(view, mViewOrigin);
334 translationX = -renderCoordinates.getScrollX() * scale + mViewOrigin [0];
335 translationY = -renderCoordinates.getScrollY() * scale
336 + renderCoordinates.getContentOffsetYPix() + mViewOrigin[1];
337 }
338
339 if (mLastCursorAnchorInfo == null && mMonitorModeEnabled) {
340 mHasPendingRequest = true;
341 }
342 if (mLastCursorAnchorInfo != null && !mHasPendingRequest) {
343 if (!mHasCoordinateInfo
344 || scale != mScale
345 || translationX != mTranslationX
346 || translationY != mTranslationY
347 || hasInsertionMarker != mHasInsertionMarker
348 || isInsertionMarkerVisible != mIsInsertionMarkerVisible
349 || insertionMarkerHorizontal != mInsertionMarkerHorizontal
350 || insertionMarkerTop != mInsertionMarkerTop
351 || insertionMarkerBottom != mInsertionMarkerBottom) {
352 mLastCursorAnchorInfo = null;
353 if (mMonitorModeEnabled) {
354 mHasPendingRequest = true;
355 }
356 }
357 }
358
359 mHasInsertionMarker = hasInsertionMarker;
360 mIsInsertionMarkerVisible = isInsertionMarkerVisible;
361 mInsertionMarkerHorizontal = insertionMarkerHorizontal;
362 mInsertionMarkerTop = insertionMarkerTop;
363 mInsertionMarkerBottom = insertionMarkerBottom;
364 mScale = scale;
365 mTranslationX = translationX;
366 mTranslationY = translationY;
367 mHasCoordinateInfo = true;
368 if (mLastCursorAnchorInfo == null && mHasPendingRequest) {
369 updateCursorAnchorInfo();
370 }
371 if (mLastCursorAnchorInfo != null && mHasPendingRequest) {
372 if (mInputMethodManagerWrapper != null) {
373 mInputMethodManagerWrapper.updateCursorAnchorInfo(view, mLastCur sorAnchorInfo);
374 }
375 mHasPendingRequest = false;
376 }
377 }
378
379 public void focusedNodeChanged(boolean isEditable) {
380 mIsEditable = isEditable;
381 mHasCoordinateInfo = false;
382 mText = null;
383 mSelectionStart = -1;
384 mSelectionEnd = -1;
385 mComposingTextStart = -1;
386 mComposingTextEnd = -1;
387 mCompositionCharacterBounds = null;
388 mHasInsertionMarker = false;
389 mInsertionMarkerHorizontal = Float.NaN;
390 mInsertionMarkerTop = Float.NaN;
391 mInsertionMarkerBottom = Float.NaN;
392 mScale = 1.0f;
393 mTranslationX = 0.0f;
394 mTranslationY = 0.0f;
395 mLastCursorAnchorInfo = null;
396 mCursorAnchorInfoBuilder.reset();
397 }
398
399 public boolean onRequestCursorUpdates(int cursorUpdateMode, @Nonnull View vi ew) {
400 if (!mIsEditable) {
401 return false;
402 }
403 final int knownRequestCursorUpdatesFlags =
404 InputConnection.CURSOR_UPDATE_MONITOR | InputConnection.CURSOR_U PDATE_IMMEDIATE;
405 if ((cursorUpdateMode & ~knownRequestCursorUpdatesFlags) != 0) {
406 // Does nothing when at least one unknown bit flag is set.
407 return false;
408 }
409 boolean updateImmediate =
410 (cursorUpdateMode & InputConnection.CURSOR_UPDATE_IMMEDIATE) != 0;
411 boolean updateMonitor =
412 (cursorUpdateMode & InputConnection.CURSOR_UPDATE_MONITOR) != 0;
413 mMonitorModeEnabled = updateMonitor;
414 if (updateImmediate) {
415 if (mLastCursorAnchorInfo == null && mHasCoordinateInfo) {
416 updateCursorAnchorInfo();
417 }
418 if (mLastCursorAnchorInfo != null) {
419 if (mInputMethodManagerWrapper != null) {
420 mInputMethodManagerWrapper.updateCursorAnchorInfo(view, mLas tCursorAnchorInfo);
421 }
422 } else {
423 mHasPendingRequest = true;
424 }
425 }
426 return true;
427 }
428
429 private void updateCursorAnchorInfo() {
430 synchronized (mCursorAnchorInfoBuilder) {
431 CharSequence composingText = getComposingText();
432 int composingTextStart = mComposingTextStart;
433 if (composingText != null) {
434 mCursorAnchorInfoBuilder.setComposingText(composingTextStart, co mposingText);
435 float[] compositionCharacterBounds = mCompositionCharacterBounds ;
436 if (compositionCharacterBounds != null) {
437 int numCharacter = compositionCharacterBounds.length / 4;
438 for (int i = 0; i < numCharacter; ++i) {
439 float left = compositionCharacterBounds[i * 4];
440 float top = compositionCharacterBounds[i * 4 + 1];
441 float right = compositionCharacterBounds[i * 4 + 2];
442 float bottom = compositionCharacterBounds[i * 4 + 3];
443 int charIndex = composingTextStart + i;
444 mCursorAnchorInfoBuilder.addCharacterBounds(charIndex, l eft, top, right,
445 bottom, CursorAnchorInfo.FLAG_HAS_VISIBLE_REGION );
446 }
447 }
448 }
449 mCursorAnchorInfoBuilder.setSelectionRange(mSelectionStart, mSelecti onEnd);
450 mMatrix.setScale(mScale, mScale);
451 mMatrix.postTranslate(mTranslationX, mTranslationY);
452 mCursorAnchorInfoBuilder.setMatrix(mMatrix);
453 if (mHasInsertionMarker) {
454 mCursorAnchorInfoBuilder.setInsertionMarkerLocation(
455 mInsertionMarkerHorizontal,
456 mInsertionMarkerTop,
457 mInsertionMarkerBottom,
458 mInsertionMarkerBottom,
459 mIsInsertionMarkerVisible ? CursorAnchorInfo.FLAG_HAS_VI SIBLE_REGION :
460 CursorAnchorInfo.FLAG_HAS_INVISIBLE_REGION);
461 }
462 mLastCursorAnchorInfo = mCursorAnchorInfoBuilder.build();
463 mCursorAnchorInfoBuilder.reset();
464 }
465 }
466 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698