Refactor DeviceAdapter to extract split-ramp logic

This change updates the DeviceAdapter implementation to clear out
the step-to-ramp and ramp-to-step adapters and move the split ramp logic
to a separate adapter class.

This will make sure that long PWLE segments will always be split, even
if they're added by other adapters like the ramp-down one that has duration
configured independently from the HAL capabilities.

Fix: 292081642
Test: atest SplitSegmentsAdapterTest
Change-Id: I091e539a516985fdcf90a4e1f4d8c9dc391eb772
This commit is contained in:
Lais Andrade
2023-06-14 14:15:54 +01:00
parent 19dae7cd51
commit 28b177ce67
18 changed files with 291 additions and 176 deletions

View File

@@ -232,11 +232,11 @@ public abstract class VibrationEffect implements Parcelable {
* Computes a legacy vibration pattern (i.e. a pattern with duration values for "off/on"
* vibration components) that is equivalent to this VibrationEffect.
*
* <p>All non-repeating effects created with {@link #createWaveform(int[], int)} are convertible
* into an equivalent vibration pattern with this method. It is not guaranteed that an effect
* created with other means becomes converted into an equivalent legacy vibration pattern, even
* if it has an equivalent vibration pattern. If this method is unable to create an equivalent
* vibration pattern for such effects, it will return {@code null}.
* <p>All non-repeating effects created with {@link #createWaveform(long[], int)} are
* convertible into an equivalent vibration pattern with this method. It is not guaranteed that
* an effect created with other means becomes converted into an equivalent legacy vibration
* pattern, even if it has an equivalent vibration pattern. If this method is unable to create
* an equivalent vibration pattern for such effects, it will return {@code null}.
*
* <p>Note that a valid equivalent long[] pattern cannot be created for an effect that has any
* form of repeating behavior, regardless of how the effect was created. For repeating effects,
@@ -245,7 +245,7 @@ public abstract class VibrationEffect implements Parcelable {
* @return a long array representing a vibration pattern equivalent to the VibrationEffect, if
* the method successfully derived a vibration pattern equivalent to the effect
* (this will always be the case if the effect was created via
* {@link #createWaveform(int[], int)} and is non-repeating). Otherwise, returns
* {@link #createWaveform(long[], int)} and is non-repeating). Otherwise, returns
* {@code null}.
* @hide
*/
@@ -953,7 +953,7 @@ public abstract class VibrationEffect implements Parcelable {
/**
* Casts a provided {@link VibrationEffectSegment} to a {@link StepSegment} and returns it,
* only if it can possibly be a segment for an effect created via
* {@link #createWaveform(int[], int)}. Otherwise, returns {@code null}.
* {@link #createWaveform(long[], int)}. Otherwise, returns {@code null}.
*/
@Nullable
private static StepSegment castToValidStepSegmentForOffOnTimingsOrNull(

View File

@@ -118,12 +118,6 @@ public final class PrebakedSegment extends VibrationEffectSegment {
}
}
/** @hide */
@Override
public boolean hasNonZeroAmplitude() {
return true;
}
/** @hide */
@NonNull
@Override

View File

@@ -87,13 +87,6 @@ public final class PrimitiveSegment extends VibrationEffectSegment {
return true;
}
/** @hide */
@Override
public boolean hasNonZeroAmplitude() {
// Every primitive plays a vibration with a non-zero amplitude, even at scale == 0.
return true;
}
/** @hide */
@NonNull
@Override

View File

@@ -123,12 +123,6 @@ public final class RampSegment extends VibrationEffectSegment {
return true;
}
/** @hide */
@Override
public boolean hasNonZeroAmplitude() {
return mStartAmplitude > 0 || mEndAmplitude > 0;
}
/** @hide */
@Override
public void validate() {

View File

@@ -99,13 +99,6 @@ public final class StepSegment extends VibrationEffectSegment {
return true;
}
/** @hide */
@Override
public boolean hasNonZeroAmplitude() {
// DEFAULT_AMPLITUDE == -1 is still a non-zero amplitude that will be resolved later.
return Float.compare(mAmplitude, 0) != 0;
}
/** @hide */
@Override
public void validate() {

View File

@@ -75,13 +75,6 @@ public abstract class VibrationEffectSegment implements Parcelable {
*/
public abstract boolean isHapticFeedbackCandidate();
/**
* Returns true if this segment plays at a non-zero amplitude at some point.
*
* @hide
*/
public abstract boolean hasNonZeroAmplitude();
/**
* Validates the segment, throwing exceptions if any parameter is invalid.
*

View File

@@ -45,7 +45,6 @@ public class PrebakedSegmentTest {
VibrationEffect.EFFECT_CLICK, true, VibrationEffect.EFFECT_STRENGTH_MEDIUM);
assertEquals(-1, prebaked.getDuration());
assertTrue(prebaked.hasNonZeroAmplitude());
assertEquals(VibrationEffect.EFFECT_CLICK, prebaked.getEffectId());
assertEquals(VibrationEffect.EFFECT_STRENGTH_MEDIUM, prebaked.getEffectStrength());
assertTrue(prebaked.shouldFallback());

View File

@@ -46,7 +46,6 @@ public class PrimitiveSegmentTest {
VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 10);
assertEquals(-1, primitive.getDuration());
assertTrue(primitive.hasNonZeroAmplitude());
assertEquals(VibrationEffect.Composition.PRIMITIVE_CLICK, primitive.getPrimitiveId());
assertEquals(10, primitive.getDelay());
assertEquals(1f, primitive.getScale(), TOLERANCE);

View File

@@ -54,7 +54,6 @@ public class RampSegmentTest {
/* startFrequencyHz= */ 100, /* endFrequencyHz= */ 200, /* duration= */ 100);
assertEquals(100L, ramp.getDuration());
assertTrue(ramp.hasNonZeroAmplitude());
assertEquals(1f, ramp.getStartAmplitude());
assertEquals(0f, ramp.getEndAmplitude());
assertEquals(100f, ramp.getStartFrequencyHz());
@@ -95,13 +94,6 @@ public class RampSegmentTest {
() -> new RampSegment(0, 0, /* startFrequencyHz= */ Float.NaN, 0, 0).validate());
}
@Test
public void testHasNonZeroAmplitude() {
assertTrue(new RampSegment(0, 1, 0, 0, 0).hasNonZeroAmplitude());
assertTrue(new RampSegment(0.01f, 0, 0, 0, 0).hasNonZeroAmplitude());
assertFalse(new RampSegment(0, 0, 0, 0, 0).hasNonZeroAmplitude());
}
@Test
public void testResolve() {
RampSegment ramp = new RampSegment(0, 1, 0, 0, 0);

View File

@@ -52,7 +52,6 @@ public class StepSegmentTest {
/* duration= */ 100);
assertEquals(100, step.getDuration());
assertTrue(step.hasNonZeroAmplitude());
assertEquals(1f, step.getAmplitude());
assertEquals(1f, step.getFrequencyHz());
}
@@ -86,14 +85,6 @@ public class StepSegmentTest {
() -> new StepSegment(1, /* frequencyHz*/ Float.NaN, 10).validate());
}
@Test
public void testHasNonZeroAmplitude() {
assertTrue(new StepSegment(1f, 0, 0).hasNonZeroAmplitude());
assertTrue(new StepSegment(0.01f, 0, 0).hasNonZeroAmplitude());
assertTrue(new StepSegment(VibrationEffect.DEFAULT_AMPLITUDE, 0, 0).hasNonZeroAmplitude());
assertFalse(new StepSegment(0, 0, 0).hasNonZeroAmplitude());
}
@Test
public void testResolve() {
StepSegment original = new StepSegment(VibrationEffect.DEFAULT_AMPLITUDE, 0, 0);

View File

@@ -30,7 +30,7 @@ import java.util.List;
* each frequency.
*
* <p>The {@link VibratorInfo.FrequencyProfile} is only applicable to PWLE compositions. This
* adapter is only applied to {@link RampSegment} and leaves all other segments unchanged.
* adapter is only applied to {@link RampSegment} and all other segments will remain unchanged.
*/
final class ClippingAmplitudeAndFrequencyAdapter implements VibrationSegmentsAdapter {
@@ -41,13 +41,13 @@ final class ClippingAmplitudeAndFrequencyAdapter implements VibrationSegmentsAda
for (int i = 0; i < segmentCount; i++) {
VibrationEffectSegment segment = segments.get(i);
if (segment instanceof RampSegment) {
segments.set(i, apply((RampSegment) segment, info));
segments.set(i, adaptToVibrator(info, (RampSegment) segment));
}
}
return repeatIndex;
}
private RampSegment apply(RampSegment segment, VibratorInfo info) {
private RampSegment adaptToVibrator(VibratorInfo info, RampSegment segment) {
float clampedStartFrequency = clampFrequency(info, segment.getStartFrequencyHz());
float clampedEndFrequency = clampFrequency(info, segment.getEndFrequencyHz());
return new RampSegment(

View File

@@ -53,9 +53,14 @@ final class DeviceAdapter implements CombinedVibration.VibratorAdapter {
mSegmentAdapters = Arrays.asList(
// TODO(b/167947076): add filter that removes unsupported primitives
// TODO(b/167947076): add filter that replaces unsupported prebaked with fallback
// Convert segments based on device capabilities
new RampToStepAdapter(settings.getRampStepDuration()),
new StepToRampAdapter(),
// Add extra ramp down segments as needed
new RampDownAdapter(settings.getRampDownDuration(), settings.getRampStepDuration()),
// Split segments based on their duration and device supported limits
new SplitSegmentsAdapter(),
// Clip amplitudes and frequencies of final segments based on device bandwidth curve
new ClippingAmplitudeAndFrequencyAdapter()
);
mAvailableVibrators = vibrators;

View File

@@ -28,9 +28,11 @@ import java.util.Arrays;
import java.util.List;
/**
* Adapter that converts ramp segments that to a sequence of fixed step segments.
* Adapter that converts ramp segments to a sequence of fixed step segments.
*
* <p>This leaves the list unchanged if the device has compose PWLE capability.
* <p>This change preserves the frequency parameters by interpolating the ramp values.
*
* <p>The segments will not be changed if the device has {@link IVibrator#CAP_COMPOSE_PWLE_EFFECTS}.
*/
final class RampToStepAdapter implements VibrationSegmentsAdapter {
@@ -53,7 +55,7 @@ final class RampToStepAdapter implements VibrationSegmentsAdapter {
if (!(segment instanceof RampSegment)) {
continue;
}
List<StepSegment> steps = apply(info, (RampSegment) segment);
List<StepSegment> steps = convertRampToSteps(info, (RampSegment) segment);
segments.remove(i);
segments.addAll(i, steps);
int addedSegments = steps.size() - 1;
@@ -66,7 +68,7 @@ final class RampToStepAdapter implements VibrationSegmentsAdapter {
return repeatIndex;
}
private List<StepSegment> apply(VibratorInfo info, RampSegment ramp) {
private List<StepSegment> convertRampToSteps(VibratorInfo info, 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(

View File

@@ -0,0 +1,115 @@
/*
* Copyright (C) 2023 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 com.android.server.vibrator;
import android.hardware.vibrator.IVibrator;
import android.os.VibratorInfo;
import android.os.vibrator.RampSegment;
import android.os.vibrator.VibrationEffectSegment;
import android.util.MathUtils;
import java.util.ArrayList;
import java.util.List;
/**
* Adapter that splits segments with longer duration than the device capabilities.
*
* <p>This transformation replaces large {@link RampSegment} entries by a sequence of smaller
* ramp segments that starts and ends at the same amplitudes/frequencies, interpolating the
* intermediate values.
*
* <p>The segments will not be changed if the device doesn't have
* {@link IVibrator#CAP_COMPOSE_PWLE_EFFECTS}.
*/
final class SplitSegmentsAdapter implements VibrationSegmentsAdapter {
@Override
public int adaptToVibrator(VibratorInfo info, List<VibrationEffectSegment> segments,
int repeatIndex) {
if (!info.hasCapability(IVibrator.CAP_COMPOSE_PWLE_EFFECTS)) {
// The vibrator does not have PWLE capability, so keep the segments unchanged.
return repeatIndex;
}
int maxRampDuration = info.getPwlePrimitiveDurationMax();
if (maxRampDuration <= 0) {
// No limit set to PWLE primitive duration.
return repeatIndex;
}
int segmentCount = segments.size();
for (int i = 0; i < segmentCount; i++) {
if (!(segments.get(i) instanceof RampSegment)) {
continue;
}
RampSegment ramp = (RampSegment) segments.get(i);
int splits = ((int) ramp.getDuration() + maxRampDuration - 1) / maxRampDuration;
if (splits <= 1) {
continue;
}
segments.remove(i);
segments.addAll(i, splitRampSegment(info, ramp, splits));
int addedSegments = splits - 1;
if (repeatIndex > i) {
repeatIndex += addedSegments;
}
i += addedSegments;
segmentCount += addedSegments;
}
return repeatIndex;
}
private static List<RampSegment> splitRampSegment(VibratorInfo info, RampSegment ramp,
int splits) {
List<RampSegment> ramps = new ArrayList<>(splits);
// Fill zero frequency values with the device resonant frequency before interpolating.
float startFrequencyHz = fillEmptyFrequency(info, ramp.getStartFrequencyHz());
float endFrequencyHz = fillEmptyFrequency(info, ramp.getEndFrequencyHz());
long splitDuration = ramp.getDuration() / splits;
float previousAmplitude = ramp.getStartAmplitude();
float previousFrequencyHz = startFrequencyHz;
long accumulatedDuration = 0;
for (int i = 1; i < splits; i++) {
accumulatedDuration += splitDuration;
float durationRatio = (float) accumulatedDuration / ramp.getDuration();
float interpolatedFrequency =
MathUtils.lerp(startFrequencyHz, endFrequencyHz, durationRatio);
float interpolatedAmplitude =
MathUtils.lerp(ramp.getStartAmplitude(), ramp.getEndAmplitude(), durationRatio);
RampSegment rampSplit = new RampSegment(
previousAmplitude, interpolatedAmplitude,
previousFrequencyHz, interpolatedFrequency,
(int) splitDuration);
ramps.add(rampSplit);
previousAmplitude = rampSplit.getEndAmplitude();
previousFrequencyHz = rampSplit.getEndFrequencyHz();
}
ramps.add(new RampSegment(previousAmplitude, ramp.getEndAmplitude(), previousFrequencyHz,
endFrequencyHz, (int) (ramp.getDuration() - accumulatedDuration)));
return ramps;
}
private static float fillEmptyFrequency(VibratorInfo info, float frequencyHz) {
if (Float.isNaN(info.getResonantFrequencyHz())) {
return frequencyHz;
}
return frequencyHz == 0 ? info.getResonantFrequencyHz() : frequencyHz;
}
}

View File

@@ -21,17 +21,17 @@ import android.os.VibratorInfo;
import android.os.vibrator.RampSegment;
import android.os.vibrator.StepSegment;
import android.os.vibrator.VibrationEffectSegment;
import android.util.MathUtils;
import java.util.ArrayList;
import java.util.List;
/**
* Adapter that converts step segments that should be handled as PWLEs to ramp segments.
*
* <p>Each replaced {@link StepSegment} will be represented by a {@link RampSegment} with same
* start and end amplitudes/frequencies, which can then be converted to PWLE compositions. This
* adapter leaves the segments unchanged if the device doesn't have the PWLE composition capability.
* <p>Each replaced step will be represented by a ramp with same start and end
* amplitudes/frequencies, which can then be converted to PWLE compositions.
*
* <p>The segments will not be changed if the device doesn't have
* {@link IVibrator#CAP_COMPOSE_PWLE_EFFECTS}.
*/
final class StepToRampAdapter implements VibrationSegmentsAdapter {
@@ -42,12 +42,6 @@ final class StepToRampAdapter implements VibrationSegmentsAdapter {
// The vibrator does not have PWLE capability, so keep the segments unchanged.
return repeatIndex;
}
convertStepsToRamps(info, segments);
repeatIndex = splitLongRampSegments(info, segments, repeatIndex);
return repeatIndex;
}
private void convertStepsToRamps(VibratorInfo info, List<VibrationEffectSegment> segments) {
int segmentCount = segments.size();
// Convert steps that require frequency control to ramps.
for (int i = 0; i < segmentCount; i++) {
@@ -68,40 +62,6 @@ final class StepToRampAdapter implements VibrationSegmentsAdapter {
}
}
}
}
/**
* Split {@link RampSegment} entries that have duration longer than {@link
* VibratorInfo#getPwlePrimitiveDurationMax()}.
*/
private int splitLongRampSegments(VibratorInfo info, List<VibrationEffectSegment> segments,
int repeatIndex) {
int maxDuration = info.getPwlePrimitiveDurationMax();
if (maxDuration <= 0) {
// No limit set to PWLE primitive duration.
return repeatIndex;
}
int segmentCount = segments.size();
for (int i = 0; i < segmentCount; i++) {
if (!(segments.get(i) instanceof RampSegment)) {
continue;
}
RampSegment ramp = (RampSegment) segments.get(i);
int splits = ((int) ramp.getDuration() + maxDuration - 1) / maxDuration;
if (splits <= 1) {
continue;
}
segments.remove(i);
segments.addAll(i, splitRampSegment(info, ramp, splits));
int addedSegments = splits - 1;
if (repeatIndex > i) {
repeatIndex += addedSegments;
}
i += addedSegments;
segmentCount += addedSegments;
}
return repeatIndex;
}
@@ -111,38 +71,6 @@ final class StepToRampAdapter implements VibrationSegmentsAdapter {
frequencyHz, frequencyHz, (int) segment.getDuration());
}
private static List<RampSegment> splitRampSegment(VibratorInfo info, RampSegment ramp,
int splits) {
List<RampSegment> ramps = new ArrayList<>(splits);
float startFrequencyHz = fillEmptyFrequency(info, ramp.getStartFrequencyHz());
float endFrequencyHz = fillEmptyFrequency(info, ramp.getEndFrequencyHz());
long splitDuration = ramp.getDuration() / splits;
float previousAmplitude = ramp.getStartAmplitude();
float previousFrequency = startFrequencyHz;
long accumulatedDuration = 0;
for (int i = 1; i < splits; i++) {
accumulatedDuration += splitDuration;
float durationRatio = (float) accumulatedDuration / ramp.getDuration();
float interpolatedFrequency =
MathUtils.lerp(startFrequencyHz, endFrequencyHz, durationRatio);
float interpolatedAmplitude =
MathUtils.lerp(ramp.getStartAmplitude(), ramp.getEndAmplitude(), durationRatio);
RampSegment rampSplit = new RampSegment(
previousAmplitude, interpolatedAmplitude,
previousFrequency, interpolatedFrequency,
(int) splitDuration);
ramps.add(rampSplit);
previousAmplitude = rampSplit.getEndAmplitude();
previousFrequency = rampSplit.getEndFrequencyHz();
}
ramps.add(new RampSegment(previousAmplitude, ramp.getEndAmplitude(), previousFrequency,
endFrequencyHz, (int) (ramp.getDuration() - accumulatedDuration)));
return ramps;
}
private static boolean isStep(VibrationEffectSegment segment) {
return segment instanceof StepSegment;
}

View File

@@ -66,6 +66,9 @@ public class RampToStepAdapterTest {
assertEquals(-1, mAdapter.adaptToVibrator(EMPTY_VIBRATOR_INFO, segments, -1));
assertEquals(1, mAdapter.adaptToVibrator(EMPTY_VIBRATOR_INFO, segments, 1));
assertEquals(-1, mAdapter.adaptToVibrator(PWLE_VIBRATOR_INFO, segments, -1));
assertEquals(1, mAdapter.adaptToVibrator(PWLE_VIBRATOR_INFO, segments, 1));
assertEquals(originalSegments, segments);
}

View File

@@ -0,0 +1,144 @@
/*
* Copyright (C) 2023 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 com.android.server.vibrator;
import static com.google.common.truth.Truth.assertThat;
import android.hardware.vibrator.IVibrator;
import android.os.VibrationEffect;
import android.os.VibratorInfo;
import android.os.vibrator.PrebakedSegment;
import android.os.vibrator.PrimitiveSegment;
import android.os.vibrator.RampSegment;
import android.os.vibrator.StepSegment;
import android.os.vibrator.VibrationEffectSegment;
import org.junit.Before;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.IntStream;
public class SplitSegmentsAdapterTest {
private static final int PWLE_COMPOSITION_PRIMITIVE_DURATION_MAX = 10;
private static final float[] TEST_AMPLITUDE_MAP = new float[]{
/* 50Hz= */ 0.1f, 0.2f, 0.4f, 0.8f, /* 150Hz= */ 1f, 0.9f, /* 200Hz= */ 0.8f};
private static final VibratorInfo.FrequencyProfile TEST_FREQUENCY_PROFILE =
new VibratorInfo.FrequencyProfile(
/* resonantFrequencyHz= */ 150f, /* minFrequencyHz= */ 50f,
/* frequencyResolutionHz= */ 25f, TEST_AMPLITUDE_MAP);
private static final VibratorInfo EMPTY_VIBRATOR_INFO = createVibratorInfo();
private static final VibratorInfo PWLE_VIBRATOR_INFO = createVibratorInfo(
IVibrator.CAP_COMPOSE_PWLE_EFFECTS);
private SplitSegmentsAdapter mAdapter;
@Before
public void setUp() throws Exception {
mAdapter = new SplitSegmentsAdapter();
}
@Test
public void testStepAndPrebakedAndPrimitiveSegments_returnsOriginalSegments() {
List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
new StepSegment(/* amplitude= */ 1, /* frequencyHz= */ 40f, /* duration= */ 100),
new PrebakedSegment(
VibrationEffect.EFFECT_CLICK, false, VibrationEffect.EFFECT_STRENGTH_LIGHT),
new PrimitiveSegment(VibrationEffect.Composition.PRIMITIVE_TICK, 1, 10)));
List<VibrationEffectSegment> originalSegments = new ArrayList<>(segments);
assertThat(mAdapter.adaptToVibrator(EMPTY_VIBRATOR_INFO, segments, /*repeatIndex= */ -1))
.isEqualTo(-1);
assertThat(mAdapter.adaptToVibrator(EMPTY_VIBRATOR_INFO, segments, /*repeatIndex= */ 1))
.isEqualTo(1);
assertThat(mAdapter.adaptToVibrator(PWLE_VIBRATOR_INFO, segments, /*repeatIndex= */ -1))
.isEqualTo(-1);
assertThat(mAdapter.adaptToVibrator(PWLE_VIBRATOR_INFO, segments, /*repeatIndex= */ 1))
.isEqualTo(1);
assertThat(segments).isEqualTo(originalSegments);
}
@Test
public void testRampSegments_noPwleCapabilities_returnsOriginalSegments() {
List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
new RampSegment(/* startAmplitude= */ 0.5f, /* endAmplitude*/ 0.5f,
/* startFrequencyHz= */ 10, /* endFrequencyHz= */ 10, /* duration= */ 10),
new RampSegment(/* startAmplitude= */ 0.2f, /* endAmplitude*/ 0.8f,
/* startFrequencyHz= */ 60, /* endFrequencyHz= */ 90, /* duration= */ 10)));
List<VibrationEffectSegment> originalSegments = new ArrayList<>(segments);
assertThat(mAdapter.adaptToVibrator(EMPTY_VIBRATOR_INFO, segments, /*repeatIndex= */ -1))
.isEqualTo(-1);
assertThat(mAdapter.adaptToVibrator(EMPTY_VIBRATOR_INFO, segments, /*repeatIndex= */ 1))
.isEqualTo(1);
assertThat(segments).isEqualTo(originalSegments);
}
@Test
public void testRampSegments_withPwleDurationLimit_splitsLongRampsAndPreserveOtherSegments() {
List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
new StepSegment(/* amplitude= */ 1, /* frequencyHz= */ 40f, /* duration= */ 100),
new RampSegment(/* startAmplitude= */ 0.5f, /* endAmplitude*/ 0.5f,
/* startFrequencyHz= */ 10, /* endFrequencyHz= */ 10, /* duration= */ 10),
new RampSegment(/* startAmplitude= */ 0, /* endAmplitude= */ 1,
/* startFrequencyHz= */ 0, /* endFrequencyHz= */ 50, /* duration= */ 25),
new StepSegment(/* amplitude= */ 1, /* frequencyHz= */ 40f, /* duration= */ 100),
new RampSegment(/* startAmplitude= */ 1, /* endAmplitude*/ 1,
/* startFrequencyHz= */ 10, /* endFrequencyHz= */ 20, /* duration= */ 5)));
List<VibrationEffectSegment> expectedSegments = Arrays.asList(
new StepSegment(/* amplitude= */ 1, /* frequencyHz= */ 40f, /* duration= */ 100),
new RampSegment(/* startAmplitude= */ 0.5f, /* endAmplitude*/ 0.5f,
/* startFrequencyHz= */ 10, /* endFrequencyHz= */ 10, /* duration= */ 10),
new RampSegment(/* startAmplitude= */ 0, /* endAmplitude= */ 0.32f,
/* startFrequencyHz= */ 150, /* endFrequencyHz= */ 118f, /* duration= */ 8),
new RampSegment(/* startAmplitude= */ 0.32f, /* endAmplitude= */ 0.64f,
/* startFrequencyHz= */ 118f, /* endFrequencyHz= */ 86f,
/* duration= */ 8),
new RampSegment(/* startAmplitude= */ 0.64f, /* endAmplitude= */ 1,
/* startFrequencyHz= */ 86f, /* endFrequencyHz= */ 50f, /* duration= */ 9),
new StepSegment(/* amplitude= */ 1, /* frequencyHz= */ 40f, /* duration= */ 100),
new RampSegment(/* startAmplitude= */ 1, /* endAmplitude*/ 1,
/* startFrequencyHz= */ 10, /* endFrequencyHz= */ 20, /* duration= */ 5));
VibratorInfo vibratorInfo = new VibratorInfo.Builder(0)
.setCapabilities(IVibrator.CAP_COMPOSE_PWLE_EFFECTS)
.setPwlePrimitiveDurationMax(10)
.setFrequencyProfile(TEST_FREQUENCY_PROFILE)
.build();
// Update repeat index to skip the ramp splits.
assertThat(mAdapter.adaptToVibrator(vibratorInfo, segments, /*repeatIndex= */ 3))
.isEqualTo(5);
assertThat(segments).isEqualTo(expectedSegments);
}
private static VibratorInfo createVibratorInfo(int... capabilities) {
return new VibratorInfo.Builder(0)
.setCapabilities(IntStream.of(capabilities).reduce((a, b) -> a | b).orElse(0))
.setFrequencyProfile(TEST_FREQUENCY_PROFILE)
.setPwlePrimitiveDurationMax(PWLE_COMPOSITION_PRIMITIVE_DURATION_MAX)
.build();
}
}

View File

@@ -66,42 +66,12 @@ public class StepToRampAdapterTest {
assertEquals(-1, mAdapter.adaptToVibrator(EMPTY_VIBRATOR_INFO, segments, -1));
assertEquals(1, mAdapter.adaptToVibrator(EMPTY_VIBRATOR_INFO, segments, 1));
assertEquals(-1, mAdapter.adaptToVibrator(PWLE_VIBRATOR_INFO, segments, -1));
assertEquals(1, mAdapter.adaptToVibrator(PWLE_VIBRATOR_INFO, segments, 1));
assertEquals(originalSegments, segments);
}
@Test
public void testRampSegments_withPwleDurationLimit_splitsLongRamps() {
List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
new RampSegment(/* startAmplitude= */ 0.5f, /* endAmplitude*/ 0.5f,
/* startFrequencyHz= */ 10, /* endFrequencyHz= */ 10, /* duration= */ 10),
new RampSegment(/* startAmplitude= */ 0, /* endAmplitude= */ 1,
/* startFrequencyHz= */ 0, /* endFrequencyHz= */ 50, /* duration= */ 25),
new RampSegment(/* startAmplitude= */ 1, /* endAmplitude*/ 1,
/* startFrequencyHz= */ 10, /* endFrequencyHz= */ 20, /* duration= */ 5)));
List<VibrationEffectSegment> expectedSegments = Arrays.asList(
new RampSegment(/* startAmplitude= */ 0.5f, /* endAmplitude*/ 0.5f,
/* startFrequencyHz= */ 10, /* endFrequencyHz= */ 10, /* duration= */ 10),
new RampSegment(/* startAmplitude= */ 0, /* endAmplitude= */ 0.32f,
/* startFrequencyHz= */ 150, /* endFrequencyHz= */ 118f, /* duration= */ 8),
new RampSegment(/* startAmplitude= */ 0.32f, /* endAmplitude= */ 0.64f,
/* startFrequencyHz= */ 118f, /* endFrequencyHz= */ 86f,
/* duration= */ 8),
new RampSegment(/* startAmplitude= */ 0.64f, /* endAmplitude= */ 1,
/* startFrequencyHz= */ 86f, /* endFrequencyHz= */ 50f, /* duration= */ 9),
new RampSegment(/* startAmplitude= */ 1, /* endAmplitude*/ 1,
/* startFrequencyHz= */ 10, /* endFrequencyHz= */ 20, /* duration= */ 5));
VibratorInfo vibratorInfo = new VibratorInfo.Builder(0)
.setCapabilities(IVibrator.CAP_COMPOSE_PWLE_EFFECTS)
.setPwlePrimitiveDurationMax(10)
.setFrequencyProfile(TEST_FREQUENCY_PROFILE)
.build();
// Update repeat index to skip the ramp splits.
assertEquals(4, mAdapter.adaptToVibrator(vibratorInfo, segments, 2));
assertEquals(expectedSegments, segments);
}
@Test
public void testStepAndRampSegments_withoutPwleCapability_keepsListUnchanged() {
List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(