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

Commit 9130fe4d authored by Biswarup Pal's avatar Biswarup Pal
Browse files

Add APIs for long press and multi press timeouts

... in ViewConfigurationParams.

Test: atest ViewConfigurationControllerTest
Test: atest VirtualDeviceManagerServiceTest
Test: atest ViewConfigurationParamsTest
Test: atest VirtualDeviceViewConfigurationTest
Test: atest VirtualDeviceParamsTest
Fixes: 405708042
Flag: android.companion.virtualdevice.flags.viewconfiguration_apis
Change-Id: I6335bac28c9afd674263b4bec7e6919774ca89bb
parent 6922ac47
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -3440,8 +3440,10 @@ package android.companion.virtual {
    method public int describeContents();
    method @NonNull public java.time.Duration getDoubleTapMinTimeDuration();
    method @NonNull public java.time.Duration getDoubleTapTimeoutDuration();
    method @NonNull public java.time.Duration getLongPressTimeoutDuration();
    method @FloatRange(from=0) public float getMaximumFlingVelocityDpPerSecond();
    method @FloatRange(from=0) public float getMinimumFlingVelocityDpPerSecond();
    method @NonNull public java.time.Duration getMultiPressTimeoutDuration();
    method @FloatRange(from=0) public float getScrollFriction();
    method @NonNull public java.time.Duration getTapTimeoutDuration();
    method @FloatRange(from=0) public float getTouchSlopDp();
@@ -3454,8 +3456,10 @@ package android.companion.virtual {
    method @NonNull public android.companion.virtual.ViewConfigurationParams build();
    method @NonNull public android.companion.virtual.ViewConfigurationParams.Builder setDoubleTapMinTimeDuration(@NonNull java.time.Duration);
    method @NonNull public android.companion.virtual.ViewConfigurationParams.Builder setDoubleTapTimeoutDuration(@NonNull java.time.Duration);
    method @NonNull public android.companion.virtual.ViewConfigurationParams.Builder setLongPressTimeoutDuration(@NonNull java.time.Duration);
    method @NonNull public android.companion.virtual.ViewConfigurationParams.Builder setMaximumFlingVelocityDpPerSecond(@FloatRange(from=0) float);
    method @NonNull public android.companion.virtual.ViewConfigurationParams.Builder setMinimumFlingVelocityDpPerSecond(@FloatRange(from=0) float);
    method @NonNull public android.companion.virtual.ViewConfigurationParams.Builder setMultiPressTimeoutDuration(@NonNull java.time.Duration);
    method @NonNull public android.companion.virtual.ViewConfigurationParams.Builder setScrollFriction(@FloatRange(from=0) float);
    method @NonNull public android.companion.virtual.ViewConfigurationParams.Builder setTapTimeoutDuration(@NonNull java.time.Duration);
    method @NonNull public android.companion.virtual.ViewConfigurationParams.Builder setTouchSlopDp(@FloatRange(from=0) float);
+82 −5
Original line number Diff line number Diff line
@@ -51,10 +51,13 @@ public final class ViewConfigurationParams implements Parcelable {
    private final int mTapTimeoutMillis;
    private final int mDoubleTapTimeoutMillis;
    private final int mDoubleTapMinTimeMillis;
    private final int mLongPressTimeoutMillis;
    private final int mMultiPressTimeoutMillis;

    private ViewConfigurationParams(float touchSlopDp, float minimumFlingVelocityDpPerSecond,
            float maximumFlingVelocityDpPerSecond, float scrollFriction, int tapTimeoutMillis,
            int doubleTapTimeoutMillis, int doubleTapMinTimeMillis) {
            int doubleTapTimeoutMillis, int doubleTapMinTimeMillis, int longPressTimeoutMillis,
            int multiPressTimeoutMillis) {
        mTouchSlopDp = touchSlopDp;
        mMinimumFlingVelocityDpPerSecond = minimumFlingVelocityDpPerSecond;
        mMaximumFlingVelocityDpPerSecond = maximumFlingVelocityDpPerSecond;
@@ -62,6 +65,8 @@ public final class ViewConfigurationParams implements Parcelable {
        mTapTimeoutMillis = tapTimeoutMillis;
        mDoubleTapTimeoutMillis = doubleTapTimeoutMillis;
        mDoubleTapMinTimeMillis = doubleTapMinTimeMillis;
        mLongPressTimeoutMillis = longPressTimeoutMillis;
        mMultiPressTimeoutMillis = multiPressTimeoutMillis;
    }

    private ViewConfigurationParams(Parcel in) {
@@ -72,6 +77,8 @@ public final class ViewConfigurationParams implements Parcelable {
        mTapTimeoutMillis = in.readInt();
        mDoubleTapTimeoutMillis = in.readInt();
        mDoubleTapMinTimeMillis = in.readInt();
        mLongPressTimeoutMillis = in.readInt();
        mMultiPressTimeoutMillis = in.readInt();
    }

    @Override
@@ -88,6 +95,8 @@ public final class ViewConfigurationParams implements Parcelable {
        dest.writeInt(mTapTimeoutMillis);
        dest.writeInt(mDoubleTapTimeoutMillis);
        dest.writeInt(mDoubleTapMinTimeMillis);
        dest.writeInt(mLongPressTimeoutMillis);
        dest.writeInt(mMultiPressTimeoutMillis);
    }

    @Override
@@ -102,14 +111,17 @@ public final class ViewConfigurationParams implements Parcelable {
                && Float.compare(mScrollFriction, that.mScrollFriction) == 0
                && mTapTimeoutMillis == that.mTapTimeoutMillis
                && mDoubleTapTimeoutMillis == that.mDoubleTapTimeoutMillis
                && mDoubleTapMinTimeMillis == that.mDoubleTapMinTimeMillis;
                && mDoubleTapMinTimeMillis == that.mDoubleTapMinTimeMillis
                && mLongPressTimeoutMillis == that.mLongPressTimeoutMillis
                && mMultiPressTimeoutMillis == that.mMultiPressTimeoutMillis;
    }

    @Override
    public int hashCode() {
        return Objects.hash(mTouchSlopDp, mMinimumFlingVelocityDpPerSecond,
                mMaximumFlingVelocityDpPerSecond, mScrollFriction, mTapTimeoutMillis,
                mDoubleTapTimeoutMillis, mDoubleTapMinTimeMillis);
                mDoubleTapTimeoutMillis, mDoubleTapMinTimeMillis, mLongPressTimeoutMillis,
                mMultiPressTimeoutMillis);
    }

    @Override
@@ -123,6 +135,8 @@ public final class ViewConfigurationParams implements Parcelable {
                + ", mTapTimeoutMillis=" + mTapTimeoutMillis
                + ", mDoubleTapTimeoutMillis=" + mDoubleTapTimeoutMillis
                + ", mDoubleTapMinTimeMillis=" + mDoubleTapMinTimeMillis
                + ", mLongPressTimeoutMillis=" + mLongPressTimeoutMillis
                + ", mMultiPressTimeoutMillis=" + mMultiPressTimeoutMillis
                + ')';
    }

@@ -194,6 +208,22 @@ public final class ViewConfigurationParams implements Parcelable {
        return Duration.ofMillis(mDoubleTapMinTimeMillis);
    }

    /**
     * Returns a {@link Duration} representing the long press timeout.
     */
    @NonNull
    public Duration getLongPressTimeoutDuration() {
        return Duration.ofMillis(mLongPressTimeoutMillis);
    }

    /**
     * Returns a {@link Duration} representing the multi press timeout.
     */
    @NonNull
    public Duration getMultiPressTimeoutDuration() {
        return Duration.ofMillis(mMultiPressTimeoutMillis);
    }

    @NonNull
    public static final Creator<ViewConfigurationParams> CREATOR =
            new Creator<>() {
@@ -221,6 +251,8 @@ public final class ViewConfigurationParams implements Parcelable {
        private int mTapTimeoutMillis = INVALID_VALUE;
        private int mDoubleTapTimeoutMillis = INVALID_VALUE;
        private int mDoubleTapMinTimeMillis = INVALID_VALUE;
        private int mLongPressTimeoutMillis = INVALID_VALUE;
        private int mMultiPressTimeoutMillis = INVALID_VALUE;

        /**
         * Sets the touch slop in density independent pixels (dp). When this is set,
@@ -357,6 +389,48 @@ public final class ViewConfigurationParams implements Parcelable {
            return this;
        }

        /**
         * Sets the long press timeout as {@link Duration}.
         *
         * @throws IllegalArgumentException if the corresponding milliseconds value is negative, or
         * greater than {@link Integer#MAX_VALUE}.
         */
        @NonNull
        public Builder setLongPressTimeoutDuration(@NonNull Duration duration) {
            Objects.requireNonNull(duration);
            if (duration.isNegative()) {
                throw new IllegalArgumentException("Long press timeout cannot be negative");
            }
            long millis = duration.toMillis();
            if (millis > Integer.MAX_VALUE) {
                throw new IllegalArgumentException(
                        "Long press timeout cannot be larger than " + Integer.MAX_VALUE);
            }
            mLongPressTimeoutMillis = (int) millis;
            return this;
        }

        /**
         * Sets the multi press timeout as {@link Duration}.
         *
         * @throws IllegalArgumentException if the corresponding milliseconds value is negative, or
         * greater than {@link Integer#MAX_VALUE}.
         */
        @NonNull
        public Builder setMultiPressTimeoutDuration(@NonNull Duration duration) {
            Objects.requireNonNull(duration);
            if (duration.isNegative()) {
                throw new IllegalArgumentException("Multi press timeout cannot be negative");
            }
            long millis = duration.toMillis();
            if (millis > Integer.MAX_VALUE) {
                throw new IllegalArgumentException(
                        "Multi press timeout cannot be larger than " + Integer.MAX_VALUE);
            }
            mMultiPressTimeoutMillis = (int) millis;
            return this;
        }

        /**
         * Builds the {@link ViewConfigurationParams} instance.
         *
@@ -371,7 +445,9 @@ public final class ViewConfigurationParams implements Parcelable {
                    && mScrollFriction == INVALID_VALUE
                    && mTapTimeoutMillis == INVALID_VALUE
                    && mDoubleTapTimeoutMillis == INVALID_VALUE
                    && mDoubleTapMinTimeMillis == INVALID_VALUE) {
                    && mDoubleTapMinTimeMillis == INVALID_VALUE
                    && mLongPressTimeoutMillis == INVALID_VALUE
                    && mMultiPressTimeoutMillis == INVALID_VALUE) {
                throw new IllegalArgumentException("None of the parameters are set");
            }
            if (mMinimumFlingVelocityDpPerSecond != INVALID_VALUE
@@ -382,7 +458,8 @@ public final class ViewConfigurationParams implements Parcelable {
            }
            return new ViewConfigurationParams(mTouchSlopDp, mMinimumFlingVelocityDpPerSecond,
                    mMaximumFlingVelocityDpPerSecond, mScrollFriction, mTapTimeoutMillis,
                    mDoubleTapTimeoutMillis, mDoubleTapMinTimeMillis);
                    mDoubleTapTimeoutMillis, mDoubleTapMinTimeMillis, mLongPressTimeoutMillis,
                    mMultiPressTimeoutMillis);
        }
    }
}
+79 −31
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.server.companion.virtual;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.companion.virtual.ViewConfigurationParams;
import android.content.ContentResolver;
import android.content.Context;
import android.content.om.FabricatedOverlay;
import android.content.om.OverlayConstraint;
@@ -26,6 +27,7 @@ import android.content.om.OverlayIdentifier;
import android.content.om.OverlayManager;
import android.content.om.OverlayManagerTransaction;
import android.os.Binder;
import android.provider.Settings;
import android.util.Slog;
import android.util.TypedValue;

@@ -33,6 +35,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;

import java.util.List;
import java.util.Objects;

/**
 * Controls the application of {@link ViewConfigurationParams} for a virtual device.
@@ -56,6 +59,7 @@ public class ViewConfigurationController {
            "dimen/config_viewMaxFlingVelocity";
    private static final String SCROLL_FRICTION_RESOURCE_NAME = "dimen/config_scrollFriction";

    private final Context mContext;
    private final OverlayManager mOverlayManager;
    private final Object mLock = new Object();

@@ -63,6 +67,7 @@ public class ViewConfigurationController {
    private OverlayIdentifier mOverlayIdentifier = null;

    public ViewConfigurationController(@NonNull Context context) {
        mContext = Objects.requireNonNull(context);
        mOverlayManager = context.getSystemService(OverlayManager.class);
    }

@@ -75,26 +80,53 @@ public class ViewConfigurationController {
            return;
        }

        applyResourceOverlays(deviceId, viewConfigurationParams);
        applySettings(deviceId, viewConfigurationParams);
    }

    /**
     * Clears the applied {@link ViewConfigurationParams}.
     */
    public void close() {
        OverlayManagerTransaction transaction;
        synchronized (mLock) {
            if (mOverlayIdentifier == null) {
                return;
            }

            transaction = new OverlayManagerTransaction.Builder().unregisterFabricatedOverlay(
                            mOverlayIdentifier).build();
        }

        Binder.withCleanCallingIdentity(() -> mOverlayManager.commit(transaction));
    }

    private void applyResourceOverlays(int deviceId,
            @NonNull ViewConfigurationParams viewConfigurationParams) {
        FabricatedOverlay overlay = new FabricatedOverlay.Builder(
                FRAMEWORK_PACKAGE_NAME /* owningPackage */,
                "vdOverlay" + deviceId /* overlayName */,
                FRAMEWORK_PACKAGE_NAME /* targetPackage */)
                .build();
        OverlayIdentifier overlayIdentifier = overlay.getIdentifier();
        setResourceDpValue(overlay, TOUCH_SLOP_RESOURCE_NAME,
        boolean change = false;
        change |= setResourceDpValue(overlay, TOUCH_SLOP_RESOURCE_NAME,
                viewConfigurationParams.getTouchSlopDp());
        setResourceDpValue(overlay, MIN_FLING_VELOCITY_RESOURCE_NAME,
        change |= setResourceDpValue(overlay, MIN_FLING_VELOCITY_RESOURCE_NAME,
                viewConfigurationParams.getMinimumFlingVelocityDpPerSecond());
        setResourceDpValue(overlay, MAX_FLING_VELOCITY_RESOURCE_NAME,
        change |= setResourceDpValue(overlay, MAX_FLING_VELOCITY_RESOURCE_NAME,
                viewConfigurationParams.getMaximumFlingVelocityDpPerSecond());
        setResourceFloatValue(overlay, SCROLL_FRICTION_RESOURCE_NAME,
        change |= setResourceFloatValue(overlay, SCROLL_FRICTION_RESOURCE_NAME,
                viewConfigurationParams.getScrollFriction());
        setResourceIntValue(overlay, TAP_TIMEOUT_RESOURCE_NAME,
        change |= setResourceIntValue(overlay, TAP_TIMEOUT_RESOURCE_NAME,
                (int) viewConfigurationParams.getTapTimeoutDuration().toMillis());
        setResourceIntValue(overlay, DOUBLE_TAP_TIMEOUT_RESOURCE_NAME,
        change |= setResourceIntValue(overlay, DOUBLE_TAP_TIMEOUT_RESOURCE_NAME,
                (int) viewConfigurationParams.getDoubleTapTimeoutDuration().toMillis());
        setResourceIntValue(overlay, DOUBLE_TAP_MIN_TIME_RESOURCE_NAME,
        change |= setResourceIntValue(overlay, DOUBLE_TAP_MIN_TIME_RESOURCE_NAME,
                (int) viewConfigurationParams.getDoubleTapMinTimeDuration().toMillis());
        if (!change) {
            return;
        }

        int callingUserId = Binder.getCallingUserHandle().getIdentifier();
        Binder.withCleanCallingIdentity(() -> {
@@ -111,59 +143,75 @@ public class ViewConfigurationController {
        });
    }

    /**
     * Clears the applied {@link ViewConfigurationParams}.
     */
    public void close() {
        OverlayManagerTransaction transaction;
        synchronized (mLock) {
            if (mOverlayIdentifier == null) {
    private void applySettings(int deviceId,
            @NonNull ViewConfigurationParams viewConfigurationParams) {
        int longPressTimeout =
                (int) viewConfigurationParams.getLongPressTimeoutDuration().toMillis();
        int multiPressTimeout =
                (int) viewConfigurationParams.getMultiPressTimeoutDuration().toMillis();
        boolean isLongPressTimeoutInvalid = isInvalid(longPressTimeout);
        boolean isMultiPressTimeoutInvalid = isInvalid(multiPressTimeout);
        if (isLongPressTimeoutInvalid && isMultiPressTimeoutInvalid) {
            return;
        }

            transaction = new OverlayManagerTransaction.Builder().unregisterFabricatedOverlay(
                            mOverlayIdentifier).build();
        Context deviceContext = mContext.createDeviceContext(deviceId);
        ContentResolver contentResolver = deviceContext.getContentResolver();
        Binder.withCleanCallingIdentity(() -> {
            if (!isLongPressTimeoutInvalid) {
                Settings.Secure.putInt(contentResolver, Settings.Secure.LONG_PRESS_TIMEOUT,
                        longPressTimeout);
            }

        Binder.withCleanCallingIdentity(() -> mOverlayManager.commit(transaction));
            if (!isMultiPressTimeoutInvalid) {
                Settings.Secure.putInt(contentResolver, Settings.Secure.MULTI_PRESS_TIMEOUT,
                        multiPressTimeout);
            }
        });
    }

    private static void setResourceDpValue(@NonNull FabricatedOverlay overlay,
    private static boolean setResourceDpValue(@NonNull FabricatedOverlay overlay,
            @NonNull String resourceName, float value) {
        if (value == ViewConfigurationParams.INVALID_VALUE) {
            return;
        if (isInvalid(value)) {
            return false;
        }

        if (!android.content.res.Flags.dimensionFrro()) {
            Slog.e(TAG, "Dimension resource overlay is not supported");
            return;
            return false;
        }

        overlay.setResourceValue(resourceName, value, TypedValue.COMPLEX_UNIT_DIP,
                null /* configuration */);
        return true;
    }

    private static void setResourceFloatValue(@NonNull FabricatedOverlay overlay,
    private static boolean setResourceFloatValue(@NonNull FabricatedOverlay overlay,
            @NonNull String resourceName, float value) {
        if (value == ViewConfigurationParams.INVALID_VALUE) {
            return;
        if (isInvalid(value)) {
            return false;
        }

        if (!android.content.res.Flags.dimensionFrro()) {
            Slog.e(TAG, "Dimension resource overlay is not supported");
            return;
            return false;
        }

        overlay.setResourceValue(resourceName, value, null /* configuration */);
        return true;
    }

    private static void setResourceIntValue(@NonNull FabricatedOverlay overlay,
    private static boolean setResourceIntValue(@NonNull FabricatedOverlay overlay,
            @NonNull String resourceName, int value) {
        if (value == ViewConfigurationParams.INVALID_VALUE) {
            return;
        if (isInvalid(value)) {
            return false;
        }

        overlay.setResourceValue(resourceName, TypedValue.TYPE_INT_DEC, value,
                null /* configuration */);
        return true;
    }

    private static boolean isInvalid(float value) {
        return value == ViewConfigurationParams.INVALID_VALUE;
    }
}
+8 −4
Original line number Diff line number Diff line
@@ -49,6 +49,7 @@ import android.companion.virtual.IVirtualDevice;
import android.companion.virtual.IVirtualDeviceActivityListener;
import android.companion.virtual.IVirtualDeviceIntentInterceptor;
import android.companion.virtual.IVirtualDeviceSoundEffectListener;
import android.companion.virtual.ViewConfigurationParams;
import android.companion.virtual.VirtualDevice;
import android.companion.virtual.VirtualDeviceManager;
import android.companion.virtual.VirtualDeviceParams;
@@ -490,10 +491,6 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
        }
        mVirtualCameraController = virtualCameraController;
        mViewConfigurationController = viewConfigurationController;
        if (mViewConfigurationController != null) {
            mViewConfigurationController.applyViewConfigurationParams(deviceId,
                    params.getViewConfigurationParams());
        }
        try {
            token.linkToDeath(this, 0);
        } catch (RemoteException e) {
@@ -532,6 +529,13 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
        }
    }

    void applyViewConfigurationParams(@Nullable ViewConfigurationParams viewConfigurationParams) {
        if (mViewConfigurationController != null) {
            mViewConfigurationController.applyViewConfigurationParams(mDeviceId,
                    viewConfigurationParams);
        }
    }

    @VisibleForTesting
    SensorController getSensorControllerForTest() {
        return mSensorController;
+5 −0
Original line number Diff line number Diff line
@@ -41,6 +41,7 @@ import android.companion.virtual.VirtualDevice;
import android.companion.virtual.VirtualDeviceManager;
import android.companion.virtual.VirtualDeviceParams;
import android.companion.virtual.sensor.VirtualSensor;
import android.companion.virtualdevice.flags.Flags;
import android.companion.virtualnative.IVirtualDeviceManagerNative;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledAfter;
@@ -494,6 +495,10 @@ public class VirtualDeviceManagerService extends SystemService {
                mVirtualDevices.put(deviceId, virtualDevice);
            }

            if (Flags.viewconfigurationApis()) {
                virtualDevice.applyViewConfigurationParams(params.getViewConfigurationParams());
            }

            mVirtualDeviceListeners.broadcast(listener -> {
                try {
                    listener.onVirtualDeviceCreated(deviceId);
Loading