Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9fa6872e authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Handwriting initiation delegation"

parents c0cb906d 9299b0b4
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -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
@@ -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);
@@ -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);
+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;
    }
}
+26 −2
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package android.view;

import android.annotation.IdRes;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.Rect;
@@ -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);
@@ -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;
@@ -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();
    }

@@ -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;
+32 −1
Original line number Diff line number Diff line
@@ -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.
@@ -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.
@@ -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));
        }
    }
+25 −0
Original line number Diff line number Diff line
@@ -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;
@@ -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);