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

Commit 82319601 authored by Lais Andrade's avatar Lais Andrade
Browse files

Expose vibration primitives durations in Vibrator

Fix: 182477149
Test: VibratorTest
Change-Id: I4dd500fe6551645454ac9af24499098a74fd866e
parent 1eb043a9
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -32064,6 +32064,7 @@ package android.os {
    method @NonNull public boolean[] arePrimitivesSupported(@NonNull int...);
    method @RequiresPermission(android.Manifest.permission.VIBRATE) public abstract void cancel();
    method public int getId();
    method @NonNull public int[] getPrimitiveDurations(@NonNull int...);
    method public abstract boolean hasAmplitudeControl();
    method public abstract boolean hasVibrator();
    method @Deprecated @RequiresPermission(android.Manifest.permission.VIBRATE) public void vibrate(long);
+21 −0
Original line number Diff line number Diff line
@@ -551,6 +551,27 @@ public abstract class Vibrator {
        return true;
    }

    /**
     * Query the estimated durations of the given primitives.
     *
     * The returned array will be the same length as the query array and the value at a given index
     * will contain the duration in milliseconds of the effect at the same index in the querying
     * array.
     *
     * @param primitiveIds Which primitives to query for.
     * @return The duration of each primitive, with zeroes for primitives that are not supported.
     */
    @NonNull
    public int[] getPrimitiveDurations(
            @NonNull @VibrationEffect.Composition.PrimitiveType int... primitiveIds) {
        VibratorInfo info = getInfo();
        int[] durations = new int[primitiveIds.length];
        for (int i = 0; i < primitiveIds.length; i++) {
            durations[i] = info.getPrimitiveDuration(primitiveIds[i]);
        }
        return durations;
    }

    /**
     * Turn the vibrator off.
     */
+121 −18
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import android.util.Log;
import android.util.MathUtils;
import android.util.Range;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;

import java.util.ArrayList;
import java.util.Arrays;
@@ -51,7 +52,7 @@ public class VibratorInfo implements Parcelable {
    @Nullable
    private final SparseBooleanArray mSupportedBraking;
    @Nullable
    private final SparseBooleanArray mSupportedPrimitives;
    private final SparseIntArray mSupportedPrimitives;
    private final float mQFactor;
    private final FrequencyMapping mFrequencyMapping;

@@ -60,19 +61,37 @@ public class VibratorInfo implements Parcelable {
        mCapabilities = in.readLong();
        mSupportedEffects = in.readSparseBooleanArray();
        mSupportedBraking = in.readSparseBooleanArray();
        mSupportedPrimitives = in.readSparseBooleanArray();
        mSupportedPrimitives = in.readSparseIntArray();
        mQFactor = in.readFloat();
        mFrequencyMapping = in.readParcelable(VibratorInfo.class.getClassLoader());
    }

    /** @hide */
    /**
     * Default constructor.
     *
     * @param id                  The vibrator id.
     * @param capabilities        All capability flags of the vibrator, defined in IVibrator.CAP_*.
     * @param supportedEffects    All supported predefined effects, enum values from {@link
     *                            android.hardware.vibrator.Effect}.
     * @param supportedBraking    All supported braking types, enum values from {@link Braking}.
     * @param supportedPrimitives All supported primitive effects, enum values from {@link
     *                            android.hardware.vibrator.CompositePrimitive}.
     * @param primitiveDurations  A mapping of primitive durations, where indexes are enum values
     *                            from {@link android.hardware.vibrator.CompositePrimitive} and the
     *                            values are estimated durations in milliseconds.
     * @param qFactor             The vibrator quality factor.
     * @param frequencyMapping    The description of the vibrator supported frequencies and max
     *                            amplitude mappings.
     * @hide
     */
    public VibratorInfo(int id, long capabilities, int[] supportedEffects, int[] supportedBraking,
            int[] supportedPrimitives, float qFactor, @NonNull FrequencyMapping frequencyMapping) {
            int[] supportedPrimitives, int[] primitiveDurations, float qFactor,
            @NonNull FrequencyMapping frequencyMapping) {
        mId = id;
        mCapabilities = capabilities;
        mSupportedEffects = toSparseBooleanArray(supportedEffects);
        mSupportedBraking = toSparseBooleanArray(supportedBraking);
        mSupportedPrimitives = toSparseBooleanArray(supportedPrimitives);
        mSupportedPrimitives = toSparseIntArray(supportedPrimitives, primitiveDurations);
        mQFactor = qFactor;
        mFrequencyMapping = frequencyMapping;
    }
@@ -100,7 +119,7 @@ public class VibratorInfo implements Parcelable {
        dest.writeLong(mCapabilities);
        dest.writeSparseBooleanArray(mSupportedEffects);
        dest.writeSparseBooleanArray(mSupportedBraking);
        dest.writeSparseBooleanArray(mSupportedPrimitives);
        dest.writeSparseIntArray(mSupportedPrimitives);
        dest.writeFloat(mQFactor);
        dest.writeParcelable(mFrequencyMapping, flags);
    }
@@ -119,18 +138,41 @@ public class VibratorInfo implements Parcelable {
            return false;
        }
        VibratorInfo that = (VibratorInfo) o;
        if (mSupportedPrimitives == null || that.mSupportedPrimitives == null) {
            if (mSupportedPrimitives != that.mSupportedPrimitives) {
                return false;
            }
        } else {
            if (mSupportedPrimitives.size() != that.mSupportedPrimitives.size()) {
                return false;
            }
            for (int i = 0; i < mSupportedPrimitives.size(); i++) {
                if (mSupportedPrimitives.keyAt(i) != that.mSupportedPrimitives.keyAt(i)) {
                    return false;
                }
                if (mSupportedPrimitives.valueAt(i) != that.mSupportedPrimitives.valueAt(i)) {
                    return false;
                }
            }
        }
        return mId == that.mId && mCapabilities == that.mCapabilities
                && Objects.equals(mSupportedEffects, that.mSupportedEffects)
                && Objects.equals(mSupportedBraking, that.mSupportedBraking)
                && Objects.equals(mSupportedPrimitives, that.mSupportedPrimitives)
                && Objects.equals(mQFactor, that.mQFactor)
                && Objects.equals(mFrequencyMapping, that.mFrequencyMapping);
    }

    @Override
    public int hashCode() {
        return Objects.hash(mId, mCapabilities, mSupportedEffects, mSupportedBraking,
                mSupportedPrimitives, mQFactor, mFrequencyMapping);
        int hashCode = Objects.hash(mId, mCapabilities, mSupportedEffects, mSupportedBraking,
                mQFactor, mFrequencyMapping);
        if (mSupportedPrimitives != null) {
            for (int i = 0; i < mSupportedPrimitives.size(); i++) {
                hashCode = 31 * hashCode + mSupportedPrimitives.keyAt(i);
                hashCode = 31 * hashCode + mSupportedPrimitives.valueAt(i);
            }
        }
        return hashCode;
    }

    @Override
@@ -206,7 +248,19 @@ public class VibratorInfo implements Parcelable {
    public boolean isPrimitiveSupported(
            @VibrationEffect.Composition.PrimitiveType int primitiveId) {
        return hasCapability(IVibrator.CAP_COMPOSE_EFFECTS) && mSupportedPrimitives != null
                && mSupportedPrimitives.get(primitiveId);
                && (mSupportedPrimitives.indexOfKey(primitiveId) >= 0);
    }

    /**
     * Query the estimated duration of given primitive.
     *
     * @param primitiveId Which primitives to query for.
     * @return The duration in milliseconds estimated for the primitive, or zero if primitive not
     * supported.
     */
    public int getPrimitiveDuration(
            @VibrationEffect.Composition.PrimitiveType int primitiveId) {
        return mSupportedPrimitives.get(primitiveId);
    }

    /**
@@ -364,14 +418,37 @@ public class VibratorInfo implements Parcelable {
        return names;
    }

    /**
     * Create a {@link SparseBooleanArray} from given {@code supportedKeys} where each key is mapped
     * to {@code true}.
     */
    @Nullable
    private static SparseBooleanArray toSparseBooleanArray(int[] values) {
        if (values == null) {
    private static SparseBooleanArray toSparseBooleanArray(int[] supportedKeys) {
        if (supportedKeys == null) {
            return null;
        }
        SparseBooleanArray array = new SparseBooleanArray();
        for (int value : values) {
            array.put(value, true);
        for (int key : supportedKeys) {
            array.put(key, true);
        }
        return array;
    }

    /**
     * Create a {@link SparseIntArray} from given {@code supportedKeys} where each key is mapped
     * to the value indexed by it.
     *
     * <p>If {@code values} is null or does not contain a given key as a index, then zero is stored
     * to the sparse array so it can still be used to query the supported keys.
     */
    @Nullable
    private static SparseIntArray toSparseIntArray(int[] supportedKeys, int[] values) {
        if (supportedKeys == null) {
            return null;
        }
        SparseIntArray array = new SparseIntArray();
        for (int key : supportedKeys) {
            array.put(key, (values == null || key >= values.length) ? 0 : values[key]);
        }
        return array;
    }
@@ -419,7 +496,20 @@ public class VibratorInfo implements Parcelable {
                    in.createFloatArray());
        }

        /** @hide */
        /**
         * Default constructor.
         *
         * @param minFrequencyHz        Minimum supported frequency, in hertz.
         * @param resonantFrequencyHz   The vibrator resonant frequency, in hertz.
         * @param frequencyResolutionHz The frequency resolution, in hertz, used by the max
         *                              amplitudes mapping.
         * @param suggestedSafeRangeHz  The suggested range, in hertz, for the safe relative
         *                              frequency range represented by [-1, 1].
         * @param maxAmplitudes         The max amplitude supported by each supported frequency,
         *                              starting at minimum frequency with jumps of frequency
         *                              resolution.
         * @hide
         */
        public FrequencyMapping(float minFrequencyHz, float resonantFrequencyHz,
                float frequencyResolutionHz, float suggestedSafeRangeHz, float[] maxAmplitudes) {
            mMinFrequencyHz = minFrequencyHz;
@@ -547,8 +637,10 @@ public class VibratorInfo implements Parcelable {

        @Override
        public int hashCode() {
            return Objects.hash(mMinFrequencyHz, mFrequencyResolutionHz, mFrequencyResolutionHz,
                    mSuggestedSafeRangeHz, mMaxAmplitudes);
            int hashCode = Objects.hash(mMinFrequencyHz, mFrequencyResolutionHz,
                    mFrequencyResolutionHz, mSuggestedSafeRangeHz);
            hashCode = 31 * hashCode + Arrays.hashCode(mMaxAmplitudes);
            return hashCode;
        }

        @Override
@@ -587,6 +679,7 @@ public class VibratorInfo implements Parcelable {
        private int[] mSupportedEffects = null;
        private int[] mSupportedBraking = null;
        private int[] mSupportedPrimitives = null;
        private int[] mPrimitiveDurations = new int[0];
        private float mQFactor = Float.NaN;
        private FrequencyMapping mFrequencyMapping =
                new FrequencyMapping(Float.NaN, Float.NaN, Float.NaN, Float.NaN, null);
@@ -627,6 +720,16 @@ public class VibratorInfo implements Parcelable {
            return this;
        }

        /** Configure the duration of a {@link android.hardware.vibrator.CompositePrimitive}. */
        @NonNull
        public Builder setPrimitiveDuration(int primitiveId, int duration) {
            if (mPrimitiveDurations.length <= primitiveId) {
                mPrimitiveDurations = Arrays.copyOf(mPrimitiveDurations, primitiveId + 1);
            }
            mPrimitiveDurations[primitiveId] = duration;
            return this;
        }

        /** Configure the vibrator quality factor. */
        @NonNull
        public Builder setQFactor(float qFactor) {
@@ -645,7 +748,7 @@ public class VibratorInfo implements Parcelable {
        @NonNull
        public VibratorInfo build() {
            return new VibratorInfo(mId, mCapabilities, mSupportedEffects, mSupportedBraking,
                    mSupportedPrimitives, mQFactor, mFrequencyMapping);
                    mSupportedPrimitives, mPrimitiveDurations, mQFactor, mFrequencyMapping);
        }
    }

+21 −1
Original line number Diff line number Diff line
@@ -99,6 +99,17 @@ public class VibratorInfoTest {
        assertFalse(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_CLICK));
    }

    @Test
    public void testGetPrimitiveDuration() {
        VibratorInfo info = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
                .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
                .setSupportedPrimitives(VibrationEffect.Composition.PRIMITIVE_CLICK)
                .setPrimitiveDuration(VibrationEffect.Composition.PRIMITIVE_CLICK, 20)
                .build();
        assertEquals(20, info.getPrimitiveDuration(VibrationEffect.Composition.PRIMITIVE_CLICK));
        assertEquals(0, info.getPrimitiveDuration(VibrationEffect.Composition.PRIMITIVE_TICK));
    }

    @Test
    public void testGetDefaultBraking_returnsFirstSupportedBraking() {
        assertEquals(Braking.NONE, new VibratorInfo.Builder(
@@ -251,12 +262,14 @@ public class VibratorInfoTest {
                .setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL)
                .setSupportedEffects(VibrationEffect.EFFECT_CLICK)
                .setSupportedPrimitives(VibrationEffect.Composition.PRIMITIVE_CLICK)
                .setPrimitiveDuration(VibrationEffect.Composition.PRIMITIVE_CLICK, 20)
                .setQFactor(2f)
                .setFrequencyMapping(TEST_FREQUENCY_MAPPING);
        VibratorInfo complete = completeBuilder.build();

        assertEquals(complete, complete);
        assertEquals(complete, completeBuilder.build());
        assertEquals(complete.hashCode(), completeBuilder.build().hashCode());

        VibratorInfo completeWithComposeControl = completeBuilder
                .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
@@ -279,6 +292,11 @@ public class VibratorInfoTest {
                .build();
        assertNotEquals(complete, completeWithUnknownPrimitives);

        VibratorInfo completeWithDifferentPrimitiveDuration = completeBuilder
                .setPrimitiveDuration(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
                .build();
        assertNotEquals(complete, completeWithDifferentPrimitiveDuration);

        VibratorInfo completeWithDifferentFrequencyMapping = completeBuilder
                .setFrequencyMapping(new VibratorInfo.FrequencyMapping(TEST_MIN_FREQUENCY + 10,
                        TEST_RESONANT_FREQUENCY + 20, TEST_FREQUENCY_RESOLUTION + 5,
@@ -314,7 +332,8 @@ public class VibratorInfoTest {
        VibratorInfo original = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
                .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
                .setSupportedEffects(VibrationEffect.EFFECT_CLICK)
                .setSupportedPrimitives(null)
                .setSupportedPrimitives(VibrationEffect.Composition.PRIMITIVE_CLICK)
                .setPrimitiveDuration(VibrationEffect.Composition.PRIMITIVE_CLICK, 20)
                .setQFactor(Float.NaN)
                .setFrequencyMapping(TEST_FREQUENCY_MAPPING)
                .build();
@@ -324,5 +343,6 @@ public class VibratorInfoTest {
        parcel.setDataPosition(0);
        VibratorInfo restored = VibratorInfo.CREATOR.createFromParcel(parcel);
        assertEquals(original, restored);
        assertEquals(original.hashCode(), restored.hashCode());
    }
}
+10 −0
Original line number Diff line number Diff line
@@ -79,6 +79,16 @@ public class VibratorTest {
                        VibrationEffect.Composition.PRIMITIVE_QUICK_RISE}).length);
    }

    @Test
    public void getPrimitivesDurations_returnsArrayOfSameSize() {
        assertEquals(0, mVibratorSpy.getPrimitiveDurations(new int[0]).length);
        assertEquals(1, mVibratorSpy.getPrimitiveDurations(
                new int[]{VibrationEffect.Composition.PRIMITIVE_CLICK}).length);
        assertEquals(2, mVibratorSpy.getPrimitiveDurations(
                new int[]{VibrationEffect.Composition.PRIMITIVE_CLICK,
                        VibrationEffect.Composition.PRIMITIVE_QUICK_RISE}).length);
    }

    @Test
    public void vibrate_withAudioAttributes_createsVibrationAttributesWithSameUsage() {
        VibrationEffect effect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
Loading