Loading core/java/android/view/HandwritingInitiator.java +7 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -305,6 +306,9 @@ public class HandwritingInitiator { mImm.startStylusHandwriting(view); mState.mHasInitiatedHandwriting = true; mState.mShouldInitHandwriting = false; if (view instanceof TextView) { ((TextView) view).hideHint(); } } /** Loading @@ -323,6 +327,9 @@ public class HandwritingInitiator { mState.mHasInitiatedHandwriting = true; mState.mShouldInitHandwriting = false; } if (view instanceof TextView) { ((TextView) view).hideHint(); } return true; } return false; Loading core/java/android/widget/TextView.java +22 −2 Original line number Diff line number Diff line Loading @@ -806,6 +806,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private CharSequence mHint; @UnsupportedAppUsage private Layout mHintLayout; private boolean mHideHint; private MovementMethod mMovement; Loading Loading @@ -7180,6 +7181,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); Loading Loading @@ -7338,6 +7341,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } private void setHintInternal(CharSequence hint) { mHideHint = false; mHint = TextUtils.stringOrSpannedString(hint); if (mLayout != null) { Loading Loading @@ -7378,6 +7382,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. Loading Loading @@ -8974,7 +8991,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; } Loading Loading @@ -11293,7 +11310,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; } /** Loading Loading @@ -12437,6 +12454,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener sendOnTextChanged(buffer, start, before, after); onTextChanged(buffer, start, before, after); mHideHint = false; clearGesturePreviewHighlight(); } Loading Loading @@ -12577,6 +12595,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return; } mHideHint = false; if (mEditor != null) mEditor.onFocusChanged(focused, direction); if (focused) { Loading core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java +114 −0 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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} Loading Loading @@ -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; Loading Loading
core/java/android/view/HandwritingInitiator.java +7 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -305,6 +306,9 @@ public class HandwritingInitiator { mImm.startStylusHandwriting(view); mState.mHasInitiatedHandwriting = true; mState.mShouldInitHandwriting = false; if (view instanceof TextView) { ((TextView) view).hideHint(); } } /** Loading @@ -323,6 +327,9 @@ public class HandwritingInitiator { mState.mHasInitiatedHandwriting = true; mState.mShouldInitHandwriting = false; } if (view instanceof TextView) { ((TextView) view).hideHint(); } return true; } return false; Loading
core/java/android/widget/TextView.java +22 −2 Original line number Diff line number Diff line Loading @@ -806,6 +806,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private CharSequence mHint; @UnsupportedAppUsage private Layout mHintLayout; private boolean mHideHint; private MovementMethod mMovement; Loading Loading @@ -7180,6 +7181,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); Loading Loading @@ -7338,6 +7341,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } private void setHintInternal(CharSequence hint) { mHideHint = false; mHint = TextUtils.stringOrSpannedString(hint); if (mLayout != null) { Loading Loading @@ -7378,6 +7382,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. Loading Loading @@ -8974,7 +8991,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; } Loading Loading @@ -11293,7 +11310,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; } /** Loading Loading @@ -12437,6 +12454,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener sendOnTextChanged(buffer, start, before, after); onTextChanged(buffer, start, before, after); mHideHint = false; clearGesturePreviewHighlight(); } Loading Loading @@ -12577,6 +12595,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return; } mHideHint = false; if (mEditor != null) mEditor.onFocusChanged(focused, direction); if (focused) { Loading
core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java +114 −0 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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} Loading Loading @@ -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; Loading