Loading core/api/current.txt +8 −0 Original line number Diff line number Diff line Loading @@ -48571,6 +48571,12 @@ package android.view { field public static final int VERTICAL_GRAVITY_MASK = 112; // 0x70 } public class HandwritingDelegateConfiguration { ctor public HandwritingDelegateConfiguration(@IdRes int, @NonNull Runnable); method public int getDelegatorViewId(); method @NonNull public Runnable getInitiationCallback(); } public class HapticFeedbackConstants { field public static final int CLOCK_TICK = 4; // 0x4 field public static final int CONFIRM = 16; // 0x10 Loading Loading @@ -50187,6 +50193,7 @@ package android.view { method public float getHandwritingBoundsOffsetLeft(); method public float getHandwritingBoundsOffsetRight(); method public float getHandwritingBoundsOffsetTop(); method @Nullable public android.view.HandwritingDelegateConfiguration getHandwritingDelegateConfiguration(); method public final boolean getHasOverlappingRendering(); method public final int getHeight(); method public void getHitRect(android.graphics.Rect); Loading Loading @@ -50553,6 +50560,7 @@ package android.view { method public void setForegroundTintList(@Nullable android.content.res.ColorStateList); method public void setForegroundTintMode(@Nullable android.graphics.PorterDuff.Mode); method public void setHandwritingBoundsOffsets(float, float, float, float); method public void setHandwritingDelegateConfiguration(@Nullable android.view.HandwritingDelegateConfiguration); method public void setHapticFeedbackEnabled(boolean); method public void setHasTransientState(boolean); method public void setHorizontalFadingEdgeEnabled(boolean); core/java/android/view/HandwritingDelegateConfiguration.java 0 → 100644 +74 −0 Original line number Diff line number Diff line /* * Copyright (C) 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.view; import android.annotation.IdRes; import android.annotation.NonNull; /** * Configuration for a view to act as a handwriting initiation delegate. This allows handwriting * mode for a delegator editor view to be initiated by stylus movement on the delegate view. * * <p>If a stylus {@link MotionEvent} occurs within the delegate view's bounds, the callback * returned by {@link #getInitiationCallback()} will be called. The callback implementation is * expected to show and focus the delegator editor view. If a view with identifier matching {@link * #getDelegatorViewId()} creates an input connection while the same stylus {@link MotionEvent} * sequence is ongoing, handwriting mode will be initiated for that view. * * <p>A common use case is a custom view which looks like a text editor but does not actually * support text editing itself, and clicking on the custom view causes an EditText to be shown. To * support handwriting initiation in this case, {@link View#setHandwritingDelegateConfiguration} can * be called on the custom view to configure it as a delegate, and set the EditText as the delegator * by passing the EditText's identifier as the {@code delegatorViewId}. The {@code * initiationCallback} implementation is typically the same as the click listener implementation * which shows the EditText. */ public class HandwritingDelegateConfiguration { @IdRes private final int mDelegatorViewId; @NonNull private final Runnable mInitiationCallback; /** * Constructs a HandwritingDelegateConfiguration instance. * * @param delegatorViewId identifier of the delegator editor view for which handwriting mode * should be initiated * @param initiationCallback callback called when a stylus {@link MotionEvent} occurs within * this view's bounds */ public HandwritingDelegateConfiguration( @IdRes int delegatorViewId, @NonNull Runnable initiationCallback) { mDelegatorViewId = delegatorViewId; mInitiationCallback = initiationCallback; } /** * Returns the identifier of the delegator editor view for which handwriting mode should be * initiated. */ public int getDelegatorViewId() { return mDelegatorViewId; } /** * Returns the callback which should be called when a stylus {@link MotionEvent} occurs within * the delegate view's bounds. */ @NonNull public Runnable getInitiationCallback() { return mInitiationCallback; } } core/java/android/view/HandwritingInitiator.java +26 −2 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package android.view; import android.annotation.IdRes; import android.annotation.NonNull; import android.annotation.Nullable; import android.graphics.Rect; Loading Loading @@ -161,6 +162,15 @@ public class HandwritingInitiator { if (candidateView != null) { if (candidateView == getConnectedView()) { startHandwriting(candidateView); } else if (candidateView.getHandwritingDelegateConfiguration() != null) { mState.mDelegatorViewId = candidateView .getHandwritingDelegateConfiguration() .getDelegatorViewId(); candidateView .getHandwritingDelegateConfiguration() .getInitiationCallback() .run(); } else { if (candidateView.getRevealOnFocusHint()) { candidateView.setRevealOnFocusHint(false); Loading Loading @@ -259,8 +269,10 @@ public class HandwritingInitiator { } final Rect handwritingArea = getViewHandwritingArea(connectedView); if (isInHandwritingArea(handwritingArea, mState.mStylusDownX, mState.mStylusDownY, connectedView)) { if ((mState.mDelegatorViewId != View.NO_ID && mState.mDelegatorViewId == connectedView.getId()) || isInHandwritingArea( handwritingArea, mState.mStylusDownX, mState.mStylusDownY, connectedView)) { startHandwriting(connectedView); } else { mState.mShouldInitHandwriting = false; Loading @@ -287,6 +299,11 @@ public class HandwritingInitiator { if (!view.isAutoHandwritingEnabled()) { return false; } // The view may be a handwriting initiation delegate, in which case it is not the editor // view for which handwriting would be started. However, in almost all cases, the return // values of View#isStylusHandwritingAvailable will be the same for the delegate view and // the delegator editor view. So the delegate view can be used to decide whether handwriting // should be triggered. return view.isStylusHandwritingAvailable(); } Loading Loading @@ -473,6 +490,13 @@ public class HandwritingInitiator { * built InputConnection. */ private boolean mExceedHandwritingSlop; /** * If the current ongoing stylus MotionEvent sequence started over a handwriting initiation * delegate view, then this is the view identifier of the corresponding delegator view. If * the delegator view creates an input connection while the MotionEvent sequence is still * ongoing, then handwriting mode will be initiated for the delegator view. */ @IdRes private int mDelegatorViewId = View.NO_ID; /** The pointer id of the stylus pointer that is being tracked. */ private final int mStylusPointerId; Loading core/java/android/view/View.java +32 −1 Original line number Diff line number Diff line Loading @@ -5062,6 +5062,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ private boolean mHoveringTouchDelegate = false; /** * Configuration for this view to act as a handwriting initiation delegate. This allows * handwriting mode for a delegator editor view to be initiated by stylus movement on this * delegate view. */ private HandwritingDelegateConfiguration mHandwritingDelegateConfiguration; /** * Solid color to use as a background when creating the drawing cache. Enables * the cache to use 16 bit bitmaps instead of 32 bit. Loading Loading @@ -12254,6 +12261,30 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } } /** * Configures this view to act as a handwriting initiation delegate. This allows handwriting * mode for a delegator editor view to be initiated by stylus movement on this delegate view. * * <p>If {@code null} is passed, this view will no longer act as a handwriting initiation * delegate. */ public void setHandwritingDelegateConfiguration( @Nullable HandwritingDelegateConfiguration configuration) { mHandwritingDelegateConfiguration = configuration; if (configuration != null) { setHandwritingArea(new Rect(0, 0, getWidth(), getHeight())); } } /** * If this view has been configured as a handwriting initiation delegate, returns the delegate * configuration. */ @Nullable public HandwritingDelegateConfiguration getHandwritingDelegateConfiguration() { return mHandwritingDelegateConfiguration; } /** * Gets the coordinates of this view in the coordinate space of the * {@link Surface} that contains the view. Loading Loading @@ -24205,7 +24236,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } } rebuildOutline(); if (onCheckIsTextEditor()) { if (onCheckIsTextEditor() || mHandwritingDelegateConfiguration != null) { setHandwritingArea(new Rect(0, 0, newWidth, newHeight)); } } core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java +25 −0 Original line number Diff line number Diff line Loading @@ -32,6 +32,7 @@ import android.app.Instrumentation; import android.content.Context; import android.graphics.Rect; import android.platform.test.annotations.Presubmit; import android.view.HandwritingDelegateConfiguration; import android.view.HandwritingInitiator; import android.view.InputDevice; import android.view.MotionEvent; Loading Loading @@ -207,6 +208,30 @@ public class HandwritingInitiatorTest { verify(mHandwritingInitiator, times(1)).startHandwriting(mTestView); } @Test public void onTouchEvent_startHandwriting_delegate() { int delegatorViewId = 234; View delegatorView = new View(mContext); delegatorView.setId(delegatorViewId); mTestView.setHandwritingDelegateConfiguration( new HandwritingDelegateConfiguration( delegatorViewId, () -> mHandwritingInitiator.onInputConnectionCreated(delegatorView))); final int x1 = (sHwArea.left + sHwArea.right) / 2; final int y1 = (sHwArea.top + sHwArea.bottom) / 2; MotionEvent stylusEvent1 = createStylusEvent(ACTION_DOWN, x1, y1, 0); mHandwritingInitiator.onTouchEvent(stylusEvent1); final int x2 = x1 + mHandwritingSlop * 2; final int y2 = y1; MotionEvent stylusEvent2 = createStylusEvent(ACTION_MOVE, x2, y2, 0); mHandwritingInitiator.onTouchEvent(stylusEvent2); verify(mHandwritingInitiator, times(1)).startHandwriting(delegatorView); } @Test public void onTouchEvent_notStartHandwriting_whenHandwritingNotAvailable() { final Rect rect = new Rect(600, 600, 900, 900); Loading Loading
core/api/current.txt +8 −0 Original line number Diff line number Diff line Loading @@ -48571,6 +48571,12 @@ package android.view { field public static final int VERTICAL_GRAVITY_MASK = 112; // 0x70 } public class HandwritingDelegateConfiguration { ctor public HandwritingDelegateConfiguration(@IdRes int, @NonNull Runnable); method public int getDelegatorViewId(); method @NonNull public Runnable getInitiationCallback(); } public class HapticFeedbackConstants { field public static final int CLOCK_TICK = 4; // 0x4 field public static final int CONFIRM = 16; // 0x10 Loading Loading @@ -50187,6 +50193,7 @@ package android.view { method public float getHandwritingBoundsOffsetLeft(); method public float getHandwritingBoundsOffsetRight(); method public float getHandwritingBoundsOffsetTop(); method @Nullable public android.view.HandwritingDelegateConfiguration getHandwritingDelegateConfiguration(); method public final boolean getHasOverlappingRendering(); method public final int getHeight(); method public void getHitRect(android.graphics.Rect); Loading Loading @@ -50553,6 +50560,7 @@ package android.view { method public void setForegroundTintList(@Nullable android.content.res.ColorStateList); method public void setForegroundTintMode(@Nullable android.graphics.PorterDuff.Mode); method public void setHandwritingBoundsOffsets(float, float, float, float); method public void setHandwritingDelegateConfiguration(@Nullable android.view.HandwritingDelegateConfiguration); method public void setHapticFeedbackEnabled(boolean); method public void setHasTransientState(boolean); method public void setHorizontalFadingEdgeEnabled(boolean);
core/java/android/view/HandwritingDelegateConfiguration.java 0 → 100644 +74 −0 Original line number Diff line number Diff line /* * Copyright (C) 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.view; import android.annotation.IdRes; import android.annotation.NonNull; /** * Configuration for a view to act as a handwriting initiation delegate. This allows handwriting * mode for a delegator editor view to be initiated by stylus movement on the delegate view. * * <p>If a stylus {@link MotionEvent} occurs within the delegate view's bounds, the callback * returned by {@link #getInitiationCallback()} will be called. The callback implementation is * expected to show and focus the delegator editor view. If a view with identifier matching {@link * #getDelegatorViewId()} creates an input connection while the same stylus {@link MotionEvent} * sequence is ongoing, handwriting mode will be initiated for that view. * * <p>A common use case is a custom view which looks like a text editor but does not actually * support text editing itself, and clicking on the custom view causes an EditText to be shown. To * support handwriting initiation in this case, {@link View#setHandwritingDelegateConfiguration} can * be called on the custom view to configure it as a delegate, and set the EditText as the delegator * by passing the EditText's identifier as the {@code delegatorViewId}. The {@code * initiationCallback} implementation is typically the same as the click listener implementation * which shows the EditText. */ public class HandwritingDelegateConfiguration { @IdRes private final int mDelegatorViewId; @NonNull private final Runnable mInitiationCallback; /** * Constructs a HandwritingDelegateConfiguration instance. * * @param delegatorViewId identifier of the delegator editor view for which handwriting mode * should be initiated * @param initiationCallback callback called when a stylus {@link MotionEvent} occurs within * this view's bounds */ public HandwritingDelegateConfiguration( @IdRes int delegatorViewId, @NonNull Runnable initiationCallback) { mDelegatorViewId = delegatorViewId; mInitiationCallback = initiationCallback; } /** * Returns the identifier of the delegator editor view for which handwriting mode should be * initiated. */ public int getDelegatorViewId() { return mDelegatorViewId; } /** * Returns the callback which should be called when a stylus {@link MotionEvent} occurs within * the delegate view's bounds. */ @NonNull public Runnable getInitiationCallback() { return mInitiationCallback; } }
core/java/android/view/HandwritingInitiator.java +26 −2 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package android.view; import android.annotation.IdRes; import android.annotation.NonNull; import android.annotation.Nullable; import android.graphics.Rect; Loading Loading @@ -161,6 +162,15 @@ public class HandwritingInitiator { if (candidateView != null) { if (candidateView == getConnectedView()) { startHandwriting(candidateView); } else if (candidateView.getHandwritingDelegateConfiguration() != null) { mState.mDelegatorViewId = candidateView .getHandwritingDelegateConfiguration() .getDelegatorViewId(); candidateView .getHandwritingDelegateConfiguration() .getInitiationCallback() .run(); } else { if (candidateView.getRevealOnFocusHint()) { candidateView.setRevealOnFocusHint(false); Loading Loading @@ -259,8 +269,10 @@ public class HandwritingInitiator { } final Rect handwritingArea = getViewHandwritingArea(connectedView); if (isInHandwritingArea(handwritingArea, mState.mStylusDownX, mState.mStylusDownY, connectedView)) { if ((mState.mDelegatorViewId != View.NO_ID && mState.mDelegatorViewId == connectedView.getId()) || isInHandwritingArea( handwritingArea, mState.mStylusDownX, mState.mStylusDownY, connectedView)) { startHandwriting(connectedView); } else { mState.mShouldInitHandwriting = false; Loading @@ -287,6 +299,11 @@ public class HandwritingInitiator { if (!view.isAutoHandwritingEnabled()) { return false; } // The view may be a handwriting initiation delegate, in which case it is not the editor // view for which handwriting would be started. However, in almost all cases, the return // values of View#isStylusHandwritingAvailable will be the same for the delegate view and // the delegator editor view. So the delegate view can be used to decide whether handwriting // should be triggered. return view.isStylusHandwritingAvailable(); } Loading Loading @@ -473,6 +490,13 @@ public class HandwritingInitiator { * built InputConnection. */ private boolean mExceedHandwritingSlop; /** * If the current ongoing stylus MotionEvent sequence started over a handwriting initiation * delegate view, then this is the view identifier of the corresponding delegator view. If * the delegator view creates an input connection while the MotionEvent sequence is still * ongoing, then handwriting mode will be initiated for the delegator view. */ @IdRes private int mDelegatorViewId = View.NO_ID; /** The pointer id of the stylus pointer that is being tracked. */ private final int mStylusPointerId; Loading
core/java/android/view/View.java +32 −1 Original line number Diff line number Diff line Loading @@ -5062,6 +5062,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ private boolean mHoveringTouchDelegate = false; /** * Configuration for this view to act as a handwriting initiation delegate. This allows * handwriting mode for a delegator editor view to be initiated by stylus movement on this * delegate view. */ private HandwritingDelegateConfiguration mHandwritingDelegateConfiguration; /** * Solid color to use as a background when creating the drawing cache. Enables * the cache to use 16 bit bitmaps instead of 32 bit. Loading Loading @@ -12254,6 +12261,30 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } } /** * Configures this view to act as a handwriting initiation delegate. This allows handwriting * mode for a delegator editor view to be initiated by stylus movement on this delegate view. * * <p>If {@code null} is passed, this view will no longer act as a handwriting initiation * delegate. */ public void setHandwritingDelegateConfiguration( @Nullable HandwritingDelegateConfiguration configuration) { mHandwritingDelegateConfiguration = configuration; if (configuration != null) { setHandwritingArea(new Rect(0, 0, getWidth(), getHeight())); } } /** * If this view has been configured as a handwriting initiation delegate, returns the delegate * configuration. */ @Nullable public HandwritingDelegateConfiguration getHandwritingDelegateConfiguration() { return mHandwritingDelegateConfiguration; } /** * Gets the coordinates of this view in the coordinate space of the * {@link Surface} that contains the view. Loading Loading @@ -24205,7 +24236,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } } rebuildOutline(); if (onCheckIsTextEditor()) { if (onCheckIsTextEditor() || mHandwritingDelegateConfiguration != null) { setHandwritingArea(new Rect(0, 0, newWidth, newHeight)); } }
core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java +25 −0 Original line number Diff line number Diff line Loading @@ -32,6 +32,7 @@ import android.app.Instrumentation; import android.content.Context; import android.graphics.Rect; import android.platform.test.annotations.Presubmit; import android.view.HandwritingDelegateConfiguration; import android.view.HandwritingInitiator; import android.view.InputDevice; import android.view.MotionEvent; Loading Loading @@ -207,6 +208,30 @@ public class HandwritingInitiatorTest { verify(mHandwritingInitiator, times(1)).startHandwriting(mTestView); } @Test public void onTouchEvent_startHandwriting_delegate() { int delegatorViewId = 234; View delegatorView = new View(mContext); delegatorView.setId(delegatorViewId); mTestView.setHandwritingDelegateConfiguration( new HandwritingDelegateConfiguration( delegatorViewId, () -> mHandwritingInitiator.onInputConnectionCreated(delegatorView))); final int x1 = (sHwArea.left + sHwArea.right) / 2; final int y1 = (sHwArea.top + sHwArea.bottom) / 2; MotionEvent stylusEvent1 = createStylusEvent(ACTION_DOWN, x1, y1, 0); mHandwritingInitiator.onTouchEvent(stylusEvent1); final int x2 = x1 + mHandwritingSlop * 2; final int y2 = y1; MotionEvent stylusEvent2 = createStylusEvent(ACTION_MOVE, x2, y2, 0); mHandwritingInitiator.onTouchEvent(stylusEvent2); verify(mHandwritingInitiator, times(1)).startHandwriting(delegatorView); } @Test public void onTouchEvent_notStartHandwriting_whenHandwritingNotAvailable() { final Rect rect = new Rect(600, 600, 900, 900); Loading