diff options
| -rw-r--r-- | core/java/android/widget/Editor.java | 76 | ||||
| -rw-r--r-- | core/res/res/values/config.xml | 5 | ||||
| -rw-r--r-- | core/res/res/values/symbols.xml | 1 |
3 files changed, 58 insertions, 24 deletions
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index 45e5f8adc468..fa1bd910bdbb 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -83,6 +83,7 @@ import android.view.DisplayListCanvas; import android.view.DragAndDropPermissions; import android.view.DragEvent; import android.view.Gravity; +import android.view.HapticFeedbackConstants; import android.view.InputDevice; import android.view.LayoutInflater; import android.view.Menu; @@ -176,6 +177,8 @@ public class Editor { private boolean mInsertionControllerEnabled; private boolean mSelectionControllerEnabled; + private final boolean mHapticTextHandleEnabled; + // Used to highlight a word when it is corrected by the IME private CorrectionHighlighter mCorrectionHighlighter; @@ -320,6 +323,8 @@ public class Editor { // Synchronize the filter list, which places the undo input filter at the end. mTextView.setFilters(mTextView.getFilters()); mProcessTextIntentActionsHandler = new ProcessTextIntentActionsHandler(this); + mHapticTextHandleEnabled = mTextView.getContext().getResources().getBoolean( + com.android.internal.R.bool.config_enableHapticTextHandle); } ParcelableParcel saveInstanceState() { @@ -4253,7 +4258,7 @@ public class Editor { mNumberPreviousOffsets++; } - private void filterOnTouchUp() { + private void filterOnTouchUp(boolean fromTouchScreen) { final long now = SystemClock.uptimeMillis(); int i = 0; int index = mPreviousOffsetIndex; @@ -4265,7 +4270,7 @@ public class Editor { if (i > 0 && i < iMax && (now - mPreviousOffsetsTimes[index]) > TOUCH_UP_FILTER_DELAY_BEFORE) { - positionAtCursorOffset(mPreviousOffsets[index], false); + positionAtCursorOffset(mPreviousOffsets[index], false, fromTouchScreen); } } @@ -4282,7 +4287,7 @@ public class Editor { public void invalidate() { super.invalidate(); if (isShowing()) { - positionAtCursorOffset(getCurrentCursorOffset(), true); + positionAtCursorOffset(getCurrentCursorOffset(), true, false); } }; @@ -4301,7 +4306,7 @@ public class Editor { // Make sure the offset is always considered new, even when focusing at same position mPreviousOffset = -1; - positionAtCursorOffset(getCurrentCursorOffset(), false); + positionAtCursorOffset(getCurrentCursorOffset(), false, false); } protected void dismiss() { @@ -4338,7 +4343,7 @@ public class Editor { protected abstract void updateSelection(int offset); - public abstract void updatePosition(float x, float y); + protected abstract void updatePosition(float x, float y, boolean fromTouchScreen); protected boolean isAtRtlRun(@NonNull Layout layout, int offset) { return layout.isRtlCharAt(offset); @@ -4357,8 +4362,11 @@ public class Editor { * @param offset Cursor offset. Must be in [-1, length]. * @param forceUpdatePosition whether to force update the position. This should be true * when If the parent has been scrolled, for example. + * @param fromTouchScreen {@code true} if the cursor is moved with motion events from the + * touch screen. */ - protected void positionAtCursorOffset(int offset, boolean forceUpdatePosition) { + protected void positionAtCursorOffset(int offset, boolean forceUpdatePosition, + boolean fromTouchScreen) { // A HandleView relies on the layout, which may be nulled by external methods Layout layout = mTextView.getLayout(); if (layout == null) { @@ -4372,6 +4380,9 @@ public class Editor { if (offsetChanged || forceUpdatePosition) { if (offsetChanged) { updateSelection(offset); + if (fromTouchScreen && mHapticTextHandleEnabled) { + mTextView.performHapticFeedback(HapticFeedbackConstants.TEXT_HANDLE_MOVE); + } addPositionToTouchUpFilter(offset); } final int line = layout.getLineForOffset(offset); @@ -4404,7 +4415,7 @@ public class Editor { @Override public void updatePosition(int parentPositionX, int parentPositionY, boolean parentPositionChanged, boolean parentScrolled) { - positionAtCursorOffset(getCurrentCursorOffset(), parentScrolled); + positionAtCursorOffset(getCurrentCursorOffset(), parentScrolled, false); if (parentPositionChanged || mPositionHasChanged) { if (mIsDragging) { // Update touchToWindow offset in case of parent scrolling while dragging @@ -4516,12 +4527,13 @@ public class Editor { xInWindow - mTouchToWindowOffsetX + mHotspotX + getHorizontalOffset(); final float newPosY = yInWindow - mTouchToWindowOffsetY + mTouchOffsetY; - updatePosition(newPosX, newPosY); + updatePosition(newPosX, newPosY, + ev.isFromSource(InputDevice.SOURCE_TOUCHSCREEN)); break; } case MotionEvent.ACTION_UP: - filterOnTouchUp(); + filterOnTouchUp(ev.isFromSource(InputDevice.SOURCE_TOUCHSCREEN)); mIsDragging = false; updateDrawable(); break; @@ -4702,7 +4714,7 @@ public class Editor { } @Override - public void updatePosition(float x, float y) { + protected void updatePosition(float x, float y, boolean fromTouchScreen) { Layout layout = mTextView.getLayout(); int offset; if (layout != null) { @@ -4715,7 +4727,7 @@ public class Editor { } else { offset = -1; } - positionAtCursorOffset(offset, false); + positionAtCursorOffset(offset, false, fromTouchScreen); if (mTextActionMode != null) { invalidateActionMode(); } @@ -4806,12 +4818,13 @@ public class Editor { } @Override - public void updatePosition(float x, float y) { + protected void updatePosition(float x, float y, boolean fromTouchScreen) { final Layout layout = mTextView.getLayout(); if (layout == null) { // HandleView will deal appropriately in positionAtCursorOffset when // layout is null. - positionAndAdjustForCrossingHandles(mTextView.getOffsetForPosition(x, y)); + positionAndAdjustForCrossingHandles(mTextView.getOffsetForPosition(x, y), + fromTouchScreen); return; } @@ -4854,12 +4867,12 @@ public class Editor { // to the current position. mLanguageDirectionChanged = true; mTouchWordDelta = 0.0f; - positionAndAdjustForCrossingHandles(offset); + positionAndAdjustForCrossingHandles(offset, fromTouchScreen); return; } else if (mLanguageDirectionChanged && !isLvlBoundary) { // We've just moved past the boundary so update the position. After this we can // figure out if the user is expanding or shrinking to go by word or character. - positionAndAdjustForCrossingHandles(offset); + positionAndAdjustForCrossingHandles(offset, fromTouchScreen); mTouchWordDelta = 0.0f; mLanguageDirectionChanged = false; return; @@ -4893,7 +4906,7 @@ public class Editor { final int nextOffset = (atRtl == isStartHandle()) ? layout.getOffsetToRightOf(mPreviousOffset) : layout.getOffsetToLeftOf(mPreviousOffset); - positionAndAdjustForCrossingHandles(nextOffset); + positionAndAdjustForCrossingHandles(nextOffset, fromTouchScreen); return; } } @@ -4972,14 +4985,15 @@ public class Editor { if (positionCursor) { mPreviousLineTouched = currLine; - positionAndAdjustForCrossingHandles(offset); + positionAndAdjustForCrossingHandles(offset, fromTouchScreen); } mPrevX = x; } @Override - protected void positionAtCursorOffset(int offset, boolean forceUpdatePosition) { - super.positionAtCursorOffset(offset, forceUpdatePosition); + protected void positionAtCursorOffset(int offset, boolean forceUpdatePosition, + boolean fromTouchScreen) { + super.positionAtCursorOffset(offset, forceUpdatePosition, fromTouchScreen); mInWord = (offset != -1) && !getWordIteratorWithText().isBoundary(offset); } @@ -4995,7 +5009,7 @@ public class Editor { return superResult; } - private void positionAndAdjustForCrossingHandles(int offset) { + private void positionAndAdjustForCrossingHandles(int offset, boolean fromTouchScreen) { final int anotherHandleOffset = isStartHandle() ? mTextView.getSelectionEnd() : mTextView.getSelectionStart(); if ((isStartHandle() && offset >= anotherHandleOffset) @@ -5020,14 +5034,14 @@ public class Editor { } else { offset = TextUtils.unpackRangeEndFromLong(range); } - positionAtCursorOffset(offset, false); + positionAtCursorOffset(offset, false, fromTouchScreen); return; } } // Handles can not cross and selection is at least one character. offset = getNextCursorOffset(anotherHandleOffset, !isStartHandle()); } - positionAtCursorOffset(offset, false); + positionAtCursorOffset(offset, false, fromTouchScreen); } private boolean positionNearEdgeOfScrollingView(float x, boolean atRtl) { @@ -5470,7 +5484,8 @@ public class Editor { private void updateCharacterBasedSelection(MotionEvent event) { final int offset = mTextView.getOffsetForPosition(event.getX(), event.getY()); - Selection.setSelection((Spannable) mTextView.getText(), mStartOffset, offset); + updateSelectionInternal(mStartOffset, offset, + event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN)); } private void updateWordBasedSelection(MotionEvent event) { @@ -5528,7 +5543,8 @@ public class Editor { } } mLineSelectionIsOn = currLine; - Selection.setSelection((Spannable) mTextView.getText(), startOffset, offset); + updateSelectionInternal(startOffset, offset, + event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN)); } private void updateParagraphBasedSelection(MotionEvent event) { @@ -5539,7 +5555,19 @@ public class Editor { final long paragraphsRange = getParagraphsRange(start, end); final int selectionStart = TextUtils.unpackRangeStartFromLong(paragraphsRange); final int selectionEnd = TextUtils.unpackRangeEndFromLong(paragraphsRange); + updateSelectionInternal(selectionStart, selectionEnd, + event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN)); + } + + private void updateSelectionInternal(int selectionStart, int selectionEnd, + boolean fromTouchScreen) { + final boolean performHapticFeedback = fromTouchScreen && mHapticTextHandleEnabled + && ((mTextView.getSelectionStart() != selectionStart) + || (mTextView.getSelectionEnd() != selectionEnd)); Selection.setSelection((Spannable) mTextView.getText(), selectionStart, selectionEnd); + if (performHapticFeedback) { + mTextView.performHapticFeedback(HapticFeedbackConstants.TEXT_HANDLE_MOVE); + } } /** diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 4a000b91c4a7..96b30da7c15a 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -147,6 +147,11 @@ fading edges is prohibitively expensive on most GPUs. --> <bool name="config_ui_enableFadingMarquee">false</bool> + <!-- Enables or disables haptic effect when the text insertion/selection handle is moved + manually by the user. Off by default, since the expected haptic feedback may not be + available on some devices. --> + <bool name="config_enableHapticTextHandle">false</bool> + <!-- Whether dialogs should close automatically when the user touches outside of them. This should not normally be modified. --> <bool name="config_closeDialogWhenTouchOutside">true</bool> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 7cee6d698c81..d60c8d2febc0 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -273,6 +273,7 @@ <java-symbol type="bool" name="config_syncstorageengine_masterSyncAutomatically" /> <java-symbol type="bool" name="config_telephony_use_own_number_for_voicemail" /> <java-symbol type="bool" name="config_ui_enableFadingMarquee" /> + <java-symbol type="bool" name="config_enableHapticTextHandle" /> <java-symbol type="bool" name="config_use_strict_phone_number_comparation" /> <java-symbol type="bool" name="config_single_volume" /> <java-symbol type="bool" name="config_voice_capable" /> |