summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/widget/Editor.java76
-rw-r--r--core/res/res/values/config.xml5
-rw-r--r--core/res/res/values/symbols.xml1
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" />