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

Commit 6ef6ed73 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Automerger Merge Worker
Browse files

Merge "Replace vibration ramps with steps on devices without PWLE" into sc-dev am: 8257b7b3

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/14126919

Change-Id: Ie5b7faabf83d10a17c1d8a2042827d84d51e6c9f
parents fb7392bc 8257b7b3
Loading
Loading
Loading
Loading
+78 −6
Original line number Diff line number Diff line
@@ -26,11 +26,14 @@ import android.util.MathUtils;
import android.util.Range;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/** Adapts a {@link VibrationEffect} to a specific device, taking into account its capabilities. */
final class DeviceVibrationEffectAdapter implements VibrationEffectModifier<VibratorInfo> {

    private static final int RAMP_STEP_DURATION_MILLIS = 5;

    /** Adapts a sequence of {@link VibrationEffectSegment} to device's capabilities. */
    interface SegmentsAdapter {

@@ -38,16 +41,17 @@ final class DeviceVibrationEffectAdapter implements VibrationEffectModifier<Vibr
         * Modifies the given segments list by adding/removing segments to it based on the
         * device capabilities specified by given {@link VibratorInfo}.
         *
         * @param segments    List of {@link VibrationEffectSegment} to be adapter to the device.
         * @param repeatIndex Repeat index on the current segment list.
         * @param segments    List of {@link VibrationEffectSegment} to be modified.
         * @param repeatIndex Repeat index of the vibration with given segment list.
         * @param info        The device vibrator info that the segments must be adapted to.
         * @return The new repeat index on the modifies list.
         * @return The new repeat index to be used for the modified list.
         */
        int apply(List<VibrationEffectSegment> segments, int repeatIndex, VibratorInfo info);
    }

    private final SegmentsAdapter mAmplitudeFrequencyAdapter;
    private final SegmentsAdapter mStepToRampAdapter;
    private final SegmentsAdapter mRampToStepsAdapter;

    DeviceVibrationEffectAdapter() {
        this(new ClippingAmplitudeFrequencyAdapter());
@@ -56,6 +60,7 @@ final class DeviceVibrationEffectAdapter implements VibrationEffectModifier<Vibr
    DeviceVibrationEffectAdapter(SegmentsAdapter amplitudeFrequencyAdapter) {
        mAmplitudeFrequencyAdapter = amplitudeFrequencyAdapter;
        mStepToRampAdapter = new StepToRampAdapter();
        mRampToStepsAdapter = new RampToStepsAdapter(RAMP_STEP_DURATION_MILLIS);
    }

    @Override
@@ -68,7 +73,10 @@ final class DeviceVibrationEffectAdapter implements VibrationEffectModifier<Vibr
        List<VibrationEffectSegment> newSegments = new ArrayList<>(composed.getSegments());
        int newRepeatIndex = composed.getRepeatIndex();

        // Maps steps that should be handled by PWLE to ramps.
        // Replace ramps with a sequence of fixed steps, or no-op if PWLE capability present.
        newRepeatIndex = mRampToStepsAdapter.apply(newSegments, newRepeatIndex, info);

        // Replace steps that should be handled by PWLE to ramps, or no-op if capability missing.
        // This should be done before frequency is converted from relative to absolute values.
        newRepeatIndex = mStepToRampAdapter.apply(newSegments, newRepeatIndex, info);

@@ -76,7 +84,6 @@ final class DeviceVibrationEffectAdapter implements VibrationEffectModifier<Vibr
        // to absolute values in Hertz.
        newRepeatIndex = mAmplitudeFrequencyAdapter.apply(newSegments, newRepeatIndex, info);

        // TODO(b/167947076): add ramp to step adapter
        // TODO(b/167947076): add filter that removes unsupported primitives
        // TODO(b/167947076): add filter that replaces unsupported prebaked with fallback

@@ -86,7 +93,7 @@ final class DeviceVibrationEffectAdapter implements VibrationEffectModifier<Vibr
    /**
     * Adapter that converts step segments that should be handled as PWLEs to ramp segments.
     *
     * <p>This leves the list unchanged if the device do not have compose PWLE capability.
     * <p>This leaves the list unchanged if the device do not have compose PWLE capability.
     */
    private static final class StepToRampAdapter implements SegmentsAdapter {
        @Override
@@ -123,6 +130,71 @@ final class DeviceVibrationEffectAdapter implements VibrationEffectModifier<Vibr
        }
    }

    /**
     * Adapter that converts ramp segments that to a sequence of fixed step segments.
     *
     * <p>This leaves the list unchanged if the device have compose PWLE capability.
     */
    private static final class RampToStepsAdapter implements SegmentsAdapter {
        private final int mStepDuration;

        RampToStepsAdapter(int stepDuration) {
            mStepDuration = stepDuration;
        }

        @Override
        public int apply(List<VibrationEffectSegment> segments, int repeatIndex,
                VibratorInfo info) {
            if (info.hasCapability(IVibrator.CAP_COMPOSE_PWLE_EFFECTS)) {
                // The vibrator have PWLE capability, so keep the segments unchanged.
                return repeatIndex;
            }
            int segmentCount = segments.size();
            for (int i = 0; i < segmentCount; i++) {
                VibrationEffectSegment segment = segments.get(i);
                if (!(segment instanceof RampSegment)) {
                    continue;
                }
                List<StepSegment> steps = apply((RampSegment) segment);
                segments.remove(i);
                segments.addAll(i, steps);
                int addedSegments = steps.size() - 1;
                if (repeatIndex > i) {
                    repeatIndex += addedSegments;
                }
                i += addedSegments;
                segmentCount += addedSegments;
            }
            return repeatIndex;
        }

        private List<StepSegment> apply(RampSegment ramp) {
            if (Float.compare(ramp.getStartAmplitude(), ramp.getEndAmplitude()) == 0) {
                // Amplitude is the same, so return a single step to simulate this ramp.
                return Arrays.asList(
                        new StepSegment(ramp.getStartAmplitude(), ramp.getStartFrequency(),
                                (int) ramp.getDuration()));
            }

            List<StepSegment> steps = new ArrayList<>();
            int stepCount = (int) (ramp.getDuration() + mStepDuration - 1) / mStepDuration;
            for (int i = 0; i < stepCount - 1; i++) {
                float pos = (float) i / stepCount;
                steps.add(new StepSegment(
                        interpolate(ramp.getStartAmplitude(), ramp.getEndAmplitude(), pos),
                        interpolate(ramp.getStartFrequency(), ramp.getEndFrequency(), pos),
                        mStepDuration));
            }
            int duration = (int) ramp.getDuration() - mStepDuration * (stepCount - 1);
            steps.add(new StepSegment(ramp.getEndAmplitude(), ramp.getEndFrequency(), duration));
            return steps;
        }

        private static float interpolate(float start, float end, float position) {
            return start + position * (end - start);
        }
    }

    /**
     * Adapter that clips frequency values to {@link VibratorInfo#getFrequencyRange()} and
     * amplitude values to respective {@link VibratorInfo#getMaxAmplitude}.
+51 −8
Original line number Diff line number Diff line
@@ -76,6 +76,38 @@ public class DeviceVibrationEffectAdapterTest {
        assertEquals(effect, mAdapter.apply(effect, createVibratorInfo(TEST_FREQUENCY_MAPPING)));
    }

    @Test
    public void testStepAndRampSegments_withoutPwleCapability_convertsRampsToSteps() {
        VibrationEffect.Composed effect = new VibrationEffect.Composed(Arrays.asList(
                new StepSegment(/* amplitude= */ 0, /* frequency= */ 1, /* duration= */ 10),
                new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 0, /* duration= */ 100),
                new RampSegment(/* startAmplitude= */ 1, /* endAmplitude= */ 0.2f,
                        /* startFrequency= */ -4, /* endFrequency= */ 2, /* duration= */ 10),
                new RampSegment(/* startAmplitude= */ 0.8f, /* endAmplitude= */ 0.2f,
                        /* startFrequency= */ 0, /* endFrequency= */ 0, /* duration= */ 11),
                new RampSegment(/* startAmplitude= */ 0.65f, /* endAmplitude= */ 0.65f,
                        /* startFrequency= */ 0, /* endFrequency= */ 1, /* duration= */ 200)),
                /* repeatIndex= */ 3);

        VibrationEffect.Composed expected = new VibrationEffect.Composed(Arrays.asList(
                new StepSegment(/* amplitude= */ 0, Float.NaN, /* duration= */ 10),
                new StepSegment(/* amplitude= */ 0.5f, Float.NaN, /* duration= */ 100),
                // 10ms ramp becomes 2 steps
                new StepSegment(/* amplitude= */ 1, Float.NaN, /* duration= */ 5),
                new StepSegment(/* amplitude= */ 0.2f, Float.NaN, /* duration= */ 5),
                // 11ms ramp becomes 3 steps
                new StepSegment(/* amplitude= */ 0.8f, Float.NaN, /* duration= */ 5),
                new StepSegment(/* amplitude= */ 0.6f, Float.NaN, /* duration= */ 5),
                new StepSegment(/* amplitude= */ 0.2f, Float.NaN, /* duration= */ 1),
                // 200ms ramp with same amplitude becomes a single step
                new StepSegment(/* amplitude= */ 0.65f, Float.NaN, /* duration= */ 200)),
                // Repeat index fixed after intermediate steps added
                /* repeatIndex= */ 4);

        VibratorInfo info = createVibratorInfo(EMPTY_FREQUENCY_MAPPING);
        assertEquals(expected, mAdapter.apply(effect, info));
    }

    @Test
    public void testStepAndRampSegments_withPwleCapability_convertsStepsToRamps() {
        VibrationEffect.Composed effect = new VibrationEffect.Composed(Arrays.asList(
@@ -131,7 +163,7 @@ public class DeviceVibrationEffectAdapterTest {
    }

    @Test
    public void testStepAndRampSegments_emptyMapping_returnsSameAmplitudesAndFrequencyZero() {
    public void testStepAndRampSegments_withEmptyFreqMapping_returnsSameAmplitudesAndZeroFreq() {
        VibrationEffect.Composed effect = new VibrationEffect.Composed(Arrays.asList(
                new StepSegment(/* amplitude= */ 0, /* frequency= */ 1, /* duration= */ 10),
                new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 0, /* duration= */ 100),
@@ -142,8 +174,11 @@ public class DeviceVibrationEffectAdapterTest {
                /* repeatIndex= */ 2);

        VibrationEffect.Composed expected = new VibrationEffect.Composed(Arrays.asList(
                new StepSegment(/* amplitude= */ 0, /* frequency= */ Float.NaN, /* duration= */ 10),
                new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ Float.NaN,
                new RampSegment(/* startAmplitude= */ 0, /* endAmplitude= */ 0,
                        /* startFrequency= */ Float.NaN, /* endFrequency= */ Float.NaN,
                        /* duration= */ 10),
                new RampSegment(/* startAmplitude= */ 0.5f, /* endAmplitude= */ 0.5f,
                        /* startFrequency= */ Float.NaN, /* endFrequency= */ Float.NaN,
                        /* duration= */ 100),
                new RampSegment(/* startAmplitude= */ 0.8f, /* endAmplitude= */ 1,
                        /* startFrequency= */ Float.NaN, /* endFrequency= */ Float.NaN,
@@ -153,11 +188,13 @@ public class DeviceVibrationEffectAdapterTest {
                        /* duration= */ 20)),
                /* repeatIndex= */ 2);

        assertEquals(expected, mAdapter.apply(effect, createVibratorInfo(EMPTY_FREQUENCY_MAPPING)));
        VibratorInfo info = createVibratorInfo(EMPTY_FREQUENCY_MAPPING,
                IVibrator.CAP_COMPOSE_PWLE_EFFECTS);
        assertEquals(expected, mAdapter.apply(effect, info));
    }

    @Test
    public void testStepAndRampSegments_nonEmptyMapping_returnsClippedValues() {
    public void testStepAndRampSegments_withValidFreqMapping_returnsClippedValues() {
        VibrationEffect.Composed effect = new VibrationEffect.Composed(Arrays.asList(
                new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 0, /* duration= */ 10),
                new StepSegment(/* amplitude= */ 1, /* frequency= */ -1, /* duration= */ 100),
@@ -168,15 +205,21 @@ public class DeviceVibrationEffectAdapterTest {
                /* repeatIndex= */ 2);

        VibrationEffect.Composed expected = new VibrationEffect.Composed(Arrays.asList(
                new StepSegment(/* amplitude= */ 0.5f, /* frequency= */ 150, /* duration= */ 10),
                new StepSegment(/* amplitude= */ 0.8f, /* frequency= */ 125, /* duration= */ 100),
                new RampSegment(/* startAmplitude= */ 0.5f, /* endAmplitude= */ 0.5f,
                        /* startFrequency= */ 150, /* endFrequency= */ 150,
                        /* duration= */ 10),
                new RampSegment(/* startAmplitude= */ 0.8f, /* endAmplitude= */ 0.8f,
                        /* startFrequency= */ 125, /* endFrequency= */ 125,
                        /* duration= */ 100),
                new RampSegment(/* startAmplitude= */ 0.1f, /* endAmplitude= */ 0.8f,
                        /* startFrequency= */ 50, /* endFrequency= */ 200, /* duration= */ 50),
                new RampSegment(/* startAmplitude= */ 0.8f, /* endAmplitude= */ 0.1f,
                        /* startFrequency= */ 200, /* endFrequency= */ 50, /* duration= */ 20)),
                /* repeatIndex= */ 2);

        assertEquals(expected, mAdapter.apply(effect, createVibratorInfo(TEST_FREQUENCY_MAPPING)));
        VibratorInfo info = createVibratorInfo(TEST_FREQUENCY_MAPPING,
                IVibrator.CAP_COMPOSE_PWLE_EFFECTS);
        assertEquals(expected, mAdapter.apply(effect, info));
    }

    private static VibratorInfo createVibratorInfo(VibratorInfo.FrequencyMapping frequencyMapping,