Hide hint text when starting handwriting

Bug: 277153261
Test: atest android.view.stylus.HandwritingInitiatorTest
Change-Id: I64007e8ea9a52a76cff78b04bc5b307c117b77c8
This commit is contained in:
Justin Ghan
2023-04-17 14:53:49 -07:00
parent 53c8ac6f3f
commit 4cf7ba6342
3 changed files with 143 additions and 2 deletions

View File

@@ -21,6 +21,7 @@ import android.annotation.Nullable;
import android.content.Context;
import android.graphics.Rect;
import android.view.inputmethod.InputMethodManager;
import android.widget.TextView;
import com.android.internal.annotations.VisibleForTesting;
@@ -305,6 +306,9 @@ public class HandwritingInitiator {
mImm.startStylusHandwriting(view);
mState.mHasInitiatedHandwriting = true;
mState.mShouldInitHandwriting = false;
if (view instanceof TextView) {
((TextView) view).hideHint();
}
}
/**
@@ -323,6 +327,9 @@ public class HandwritingInitiator {
mState.mHasInitiatedHandwriting = true;
mState.mShouldInitHandwriting = false;
}
if (view instanceof TextView) {
((TextView) view).hideHint();
}
return true;
}
return false;

View File

@@ -805,6 +805,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
private CharSequence mHint;
@UnsupportedAppUsage
private Layout mHintLayout;
private boolean mHideHint;
private MovementMethod mMovement;
@@ -7110,6 +7111,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
sendOnTextChanged(text, 0, oldlen, textLength);
onTextChanged(text, 0, oldlen, textLength);
mHideHint = false;
if (a11yTextChangeType == AccessibilityUtils.TEXT) {
notifyViewAccessibilityStateChangedIfNeeded(
AccessibilityEvent.CONTENT_CHANGE_TYPE_TEXT);
@@ -7268,6 +7271,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
private void setHintInternal(CharSequence hint) {
mHideHint = false;
mHint = TextUtils.stringOrSpannedString(hint);
if (mLayout != null) {
@@ -7308,6 +7312,19 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
return mHint;
}
/**
* Temporarily hides the hint text until the text is modified, or the hint text is modified, or
* the view gains or loses focus.
*
* @hide
*/
public void hideHint() {
if (isShowingHint()) {
mHideHint = true;
invalidate();
}
}
/**
* Returns if the text is constrained to a single horizontally scrolling line ignoring new
* line characters instead of letting it wrap onto multiple lines.
@@ -8904,7 +8921,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
Layout layout = mLayout;
if (mHint != null && mText.length() == 0) {
if (mHint != null && !mHideHint && mText.length() == 0) {
if (mHintTextColor != null) {
color = mCurHintTextColor;
}
@@ -11223,7 +11240,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
private boolean isShowingHint() {
return TextUtils.isEmpty(mText) && !TextUtils.isEmpty(mHint);
return TextUtils.isEmpty(mText) && !TextUtils.isEmpty(mHint) && !mHideHint;
}
/**
@@ -12367,6 +12384,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
sendOnTextChanged(buffer, start, before, after);
onTextChanged(buffer, start, before, after);
mHideHint = false;
clearGesturePreviewHighlight();
}
@@ -12507,6 +12525,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
return;
}
mHideHint = false;
if (mEditor != null) mEditor.onFocusChanged(focused, direction);
if (focused) {

View File

@@ -23,14 +23,20 @@ import static android.view.stylus.HandwritingTestUtil.createView;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.Instrumentation;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
import android.view.HandwritingInitiator;
@@ -38,7 +44,9 @@ import android.view.InputDevice;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
@@ -47,6 +55,7 @@ import androidx.test.platform.app.InstrumentationRegistry;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
/**
* Tests for {@link HandwritingInitiator}
@@ -543,6 +552,111 @@ public class HandwritingInitiatorTest {
assertThat(mHandwritingInitiator.mConnectedView.get()).isEqualTo(mTestView1);
}
@Test
public void startHandwriting_hidesHint() {
EditText editText =
new EditText(InstrumentationRegistry.getInstrumentation().getTargetContext());
editText.setHint("hint");
editText.setLayoutParams(new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
verifyEditTextDrawsText(editText, "hint");
mHandwritingInitiator.onTouchEvent(createStylusEvent(ACTION_DOWN, 0, 0, 0));
mHandwritingInitiator.startHandwriting(editText);
verifyEditTextDrawsText(editText, null);
}
@Test
public void startHandwriting_clearFocus_restoresHint() {
EditText editText =
new EditText(InstrumentationRegistry.getInstrumentation().getTargetContext());
editText.setHint("hint");
editText.setLayoutParams(new ViewGroup.LayoutParams(1024, 1024));
editText.requestFocus();
verifyEditTextDrawsText(editText, "hint");
mHandwritingInitiator.onTouchEvent(createStylusEvent(ACTION_DOWN, 0, 0, 0));
mHandwritingInitiator.startHandwriting(editText);
verifyEditTextDrawsText(editText, null);
editText.clearFocus();
verifyEditTextDrawsText(editText, "hint");
}
@Test
public void startHandwriting_setHint_restoresHint() {
EditText editText =
new EditText(InstrumentationRegistry.getInstrumentation().getTargetContext());
editText.setHint("hint");
editText.setLayoutParams(new ViewGroup.LayoutParams(1024, 1024));
verifyEditTextDrawsText(editText, "hint");
mHandwritingInitiator.onTouchEvent(createStylusEvent(ACTION_DOWN, 0, 0, 0));
mHandwritingInitiator.startHandwriting(editText);
verifyEditTextDrawsText(editText, null);
editText.setHint("new hint");
verifyEditTextDrawsText(editText, "new hint");
}
@Test
public void startHandwriting_setText_restoresHint() {
EditText editText =
new EditText(InstrumentationRegistry.getInstrumentation().getTargetContext());
editText.setHint("hint");
editText.setLayoutParams(new ViewGroup.LayoutParams(1024, 1024));
verifyEditTextDrawsText(editText, "hint");
mHandwritingInitiator.onTouchEvent(createStylusEvent(ACTION_DOWN, 0, 0, 0));
mHandwritingInitiator.startHandwriting(editText);
verifyEditTextDrawsText(editText, null);
editText.setText("a");
editText.setText("");
verifyEditTextDrawsText(editText, "hint");
}
private void verifyEditTextDrawsText(EditText editText, String text) {
editText.measure(
View.MeasureSpec.makeMeasureSpec(1024, View.MeasureSpec.AT_MOST),
View.MeasureSpec.makeMeasureSpec(1024, View.MeasureSpec.AT_MOST));
Canvas canvas = prepareMockCanvas(editText);
editText.draw(canvas);
if (text != null) {
ArgumentCaptor<CharSequence> textCaptor = ArgumentCaptor.forClass(CharSequence.class);
verify(canvas).drawText(
textCaptor.capture(), anyInt(), anyInt(), anyFloat(), anyFloat(), any());
assertThat(textCaptor.getValue().toString()).isEqualTo(text);
} else {
verify(canvas, never()).drawText(
any(CharSequence.class), anyInt(), anyInt(), anyFloat(), anyFloat(), any());
}
}
private Canvas prepareMockCanvas(View view) {
Canvas canvas = mock(Canvas.class);
when(canvas.getClipBounds(any())).thenAnswer(invocation -> {
Rect outRect = invocation.getArgument(0);
outRect.top = 0;
outRect.left = 0;
outRect.right = view.getMeasuredWidth();
outRect.bottom = view.getMeasuredHeight();
return true;
});
return canvas;
}
private MotionEvent createStylusEvent(int action, int x, int y, long eventTime) {
MotionEvent.PointerProperties[] properties = MotionEvent.PointerProperties.createArray(1);
properties[0].toolType = MotionEvent.TOOL_TYPE_STYLUS;