Merge "Use composition size limit on repeating effects" into tm-dev am: 48a15efcc7
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/17474926 Change-Id: I7953ee5f7b5c88d0a1b111740523ed9f69e2a843 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
@@ -141,8 +141,16 @@ abstract class AbstractVibratorStep extends Step {
|
||||
*/
|
||||
protected List<Step> nextSteps(long nextStartTime, long vibratorOffTimeout,
|
||||
int segmentsPlayed) {
|
||||
int nextSegmentIndex = segmentIndex + segmentsPlayed;
|
||||
int effectSize = effect.getSegments().size();
|
||||
int repeatIndex = effect.getRepeatIndex();
|
||||
if (nextSegmentIndex >= effectSize && repeatIndex >= 0) {
|
||||
// Count the loops that were played.
|
||||
int loopSize = effectSize - repeatIndex;
|
||||
nextSegmentIndex = repeatIndex + ((nextSegmentIndex - effectSize) % loopSize);
|
||||
}
|
||||
Step nextStep = conductor.nextVibrateStep(nextStartTime, controller, effect,
|
||||
segmentIndex + segmentsPlayed, vibratorOffTimeout);
|
||||
nextSegmentIndex, vibratorOffTimeout);
|
||||
return nextStep == null ? VibrationStepConductor.EMPTY_STEP_LIST : Arrays.asList(nextStep);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,11 @@ import java.util.List;
|
||||
* {@link PrimitiveSegment} starting at the current index.
|
||||
*/
|
||||
final class ComposePrimitivesVibratorStep extends AbstractVibratorStep {
|
||||
/**
|
||||
* Default limit to the number of primitives in a composition, if none is defined by the HAL,
|
||||
* to prevent repeating effects from generating an infinite list.
|
||||
*/
|
||||
private static final int DEFAULT_COMPOSITION_SIZE_LIMIT = 100;
|
||||
|
||||
ComposePrimitivesVibratorStep(VibrationStepConductor conductor, long startTime,
|
||||
VibratorController controller, VibrationEffect.Composed effect, int index,
|
||||
@@ -49,18 +54,8 @@ final class ComposePrimitivesVibratorStep extends AbstractVibratorStep {
|
||||
// Load the next PrimitiveSegments to create a single compose call to the vibrator,
|
||||
// limited to the vibrator composition maximum size.
|
||||
int limit = controller.getVibratorInfo().getCompositionSizeMax();
|
||||
int segmentCount = limit > 0
|
||||
? Math.min(effect.getSegments().size(), segmentIndex + limit)
|
||||
: effect.getSegments().size();
|
||||
List<PrimitiveSegment> primitives = new ArrayList<>();
|
||||
for (int i = segmentIndex; i < segmentCount; i++) {
|
||||
VibrationEffectSegment segment = effect.getSegments().get(i);
|
||||
if (segment instanceof PrimitiveSegment) {
|
||||
primitives.add((PrimitiveSegment) segment);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
List<PrimitiveSegment> primitives = unrollPrimitiveSegments(effect, segmentIndex,
|
||||
limit > 0 ? limit : DEFAULT_COMPOSITION_SIZE_LIMIT);
|
||||
|
||||
if (primitives.isEmpty()) {
|
||||
Slog.w(VibrationThread.TAG, "Ignoring wrong segment for a ComposePrimitivesStep: "
|
||||
@@ -81,4 +76,44 @@ final class ComposePrimitivesVibratorStep extends AbstractVibratorStep {
|
||||
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the primitive segments to be played by this step as a single composition, starting at
|
||||
* {@code startIndex} until:
|
||||
*
|
||||
* <ol>
|
||||
* <li>There are no more segments in the effect;
|
||||
* <li>The first non-primitive segment is found;
|
||||
* <li>The given limit to the composition size is reached.
|
||||
* </ol>
|
||||
*
|
||||
* <p>If the effect is repeating then this method will generate the largest composition within
|
||||
* given limit.
|
||||
*/
|
||||
private List<PrimitiveSegment> unrollPrimitiveSegments(VibrationEffect.Composed effect,
|
||||
int startIndex, int limit) {
|
||||
List<PrimitiveSegment> segments = new ArrayList<>(limit);
|
||||
int segmentCount = effect.getSegments().size();
|
||||
int repeatIndex = effect.getRepeatIndex();
|
||||
|
||||
for (int i = startIndex; segments.size() < limit; i++) {
|
||||
if (i == segmentCount) {
|
||||
if (repeatIndex >= 0) {
|
||||
i = repeatIndex;
|
||||
} else {
|
||||
// Non-repeating effect, stop collecting primitives.
|
||||
break;
|
||||
}
|
||||
}
|
||||
VibrationEffectSegment segment = effect.getSegments().get(i);
|
||||
if (segment instanceof PrimitiveSegment) {
|
||||
segments.add((PrimitiveSegment) segment);
|
||||
} else {
|
||||
// First non-primitive segment, stop collecting primitives.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return segments;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +33,11 @@ import java.util.List;
|
||||
* {@link StepSegment} or {@link RampSegment} starting at the current index.
|
||||
*/
|
||||
final class ComposePwleVibratorStep extends AbstractVibratorStep {
|
||||
/**
|
||||
* Default limit to the number of PWLE segments, if none is defined by the HAL, to prevent
|
||||
* repeating effects from generating an infinite list.
|
||||
*/
|
||||
private static final int DEFAULT_PWLE_SIZE_LIMIT = 100;
|
||||
|
||||
ComposePwleVibratorStep(VibrationStepConductor conductor, long startTime,
|
||||
VibratorController controller, VibrationEffect.Composed effect, int index,
|
||||
@@ -50,18 +55,8 @@ final class ComposePwleVibratorStep extends AbstractVibratorStep {
|
||||
// Load the next RampSegments to create a single composePwle call to the vibrator,
|
||||
// limited to the vibrator PWLE maximum size.
|
||||
int limit = controller.getVibratorInfo().getPwleSizeMax();
|
||||
int segmentCount = limit > 0
|
||||
? Math.min(effect.getSegments().size(), segmentIndex + limit)
|
||||
: effect.getSegments().size();
|
||||
List<RampSegment> pwles = new ArrayList<>();
|
||||
for (int i = segmentIndex; i < segmentCount; i++) {
|
||||
VibrationEffectSegment segment = effect.getSegments().get(i);
|
||||
if (segment instanceof RampSegment) {
|
||||
pwles.add((RampSegment) segment);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
List<RampSegment> pwles = unrollRampSegments(effect, segmentIndex,
|
||||
limit > 0 ? limit : DEFAULT_PWLE_SIZE_LIMIT);
|
||||
|
||||
if (pwles.isEmpty()) {
|
||||
Slog.w(VibrationThread.TAG, "Ignoring wrong segment for a ComposePwleStep: "
|
||||
@@ -81,4 +76,88 @@ final class ComposePwleVibratorStep extends AbstractVibratorStep {
|
||||
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the ramp segments to be played by this step for a waveform, starting at
|
||||
* {@code startIndex} until:
|
||||
*
|
||||
* <ol>
|
||||
* <li>There are no more segments in the effect;
|
||||
* <li>The first non-ramp segment is found;
|
||||
* <li>The given limit to the PWLE size is reached.
|
||||
* </ol>
|
||||
*
|
||||
* <p>If the effect is repeating then this method will generate the largest PWLE within given
|
||||
* limit. This will also optimize to end the list at a ramp to zero-amplitude, if possible, and
|
||||
* avoid braking down the effect in non-zero amplitude.
|
||||
*/
|
||||
private List<RampSegment> unrollRampSegments(VibrationEffect.Composed effect, int startIndex,
|
||||
int limit) {
|
||||
List<RampSegment> segments = new ArrayList<>(limit);
|
||||
float bestBreakAmplitude = 1;
|
||||
int bestBreakPosition = limit; // Exclusive index.
|
||||
|
||||
int segmentCount = effect.getSegments().size();
|
||||
int repeatIndex = effect.getRepeatIndex();
|
||||
|
||||
// Loop once after reaching the limit to see if breaking it will really be necessary, then
|
||||
// apply the best break position found, otherwise return the full list as it fits the limit.
|
||||
for (int i = startIndex; segments.size() <= limit; i++) {
|
||||
if (i == segmentCount) {
|
||||
if (repeatIndex >= 0) {
|
||||
i = repeatIndex;
|
||||
} else {
|
||||
// Non-repeating effect, stop collecting ramps.
|
||||
break;
|
||||
}
|
||||
}
|
||||
VibrationEffectSegment segment = effect.getSegments().get(i);
|
||||
if (segment instanceof RampSegment) {
|
||||
RampSegment rampSegment = (RampSegment) segment;
|
||||
segments.add(rampSegment);
|
||||
|
||||
if (isBetterBreakPosition(segments, bestBreakAmplitude, limit)) {
|
||||
// Mark this position as the best one so far to break a long waveform.
|
||||
bestBreakAmplitude = rampSegment.getEndAmplitude();
|
||||
bestBreakPosition = segments.size(); // Break after this ramp ends.
|
||||
}
|
||||
} else {
|
||||
// First non-ramp segment, stop collecting ramps.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return segments.size() > limit
|
||||
// Remove excessive segments, using the best breaking position recorded.
|
||||
? segments.subList(0, bestBreakPosition)
|
||||
// Return all collected ramp segments.
|
||||
: segments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the current segment list represents a better break position for a PWLE,
|
||||
* given the current amplitude being used for breaking it at a smaller size and the size limit.
|
||||
*/
|
||||
private boolean isBetterBreakPosition(List<RampSegment> segments,
|
||||
float currentBestBreakAmplitude, int limit) {
|
||||
RampSegment lastSegment = segments.get(segments.size() - 1);
|
||||
float breakAmplitudeCandidate = lastSegment.getEndAmplitude();
|
||||
int breakPositionCandidate = segments.size();
|
||||
|
||||
if (breakPositionCandidate > limit) {
|
||||
// We're beyond limit, last break position found should be used.
|
||||
return false;
|
||||
}
|
||||
if (breakAmplitudeCandidate == 0) {
|
||||
// Breaking at amplitude zero at any position is always preferable.
|
||||
return true;
|
||||
}
|
||||
if (breakPositionCandidate < limit / 2) {
|
||||
// Avoid breaking at the first half of the allowed maximum size, even if amplitudes are
|
||||
// lower, to avoid creating PWLEs that are too small unless it's to break at zero.
|
||||
return false;
|
||||
}
|
||||
// Prefer lower amplitudes at a later position for breaking the PWLE in a more subtle way.
|
||||
return breakAmplitudeCandidate <= currentBestBreakAmplitude;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +33,12 @@ import java.util.List;
|
||||
* and amplitude to simulate waveforms represented by a sequence of {@link StepSegment}.
|
||||
*/
|
||||
final class SetAmplitudeVibratorStep extends AbstractVibratorStep {
|
||||
/**
|
||||
* The repeating waveform keeps the vibrator ON all the time. Use a minimum duration to
|
||||
* prevent short patterns from turning the vibrator ON too frequently.
|
||||
*/
|
||||
private static final int REPEATING_EFFECT_ON_DURATION = 5000; // 5s
|
||||
|
||||
private long mNextOffTime;
|
||||
|
||||
SetAmplitudeVibratorStep(VibrationStepConductor conductor, long startTime,
|
||||
@@ -170,10 +176,7 @@ final class SetAmplitudeVibratorStep extends AbstractVibratorStep {
|
||||
repeatIndex = -1;
|
||||
}
|
||||
if (i == startIndex) {
|
||||
// The repeating waveform keeps the vibrator ON all the time. Use a minimum
|
||||
// of 1s duration to prevent short patterns from turning the vibrator ON too
|
||||
// frequently.
|
||||
return Math.max(timing, 1000);
|
||||
return Math.max(timing, REPEATING_EFFECT_ON_DURATION);
|
||||
}
|
||||
}
|
||||
if (i == segmentCount && effect.getRepeatIndex() < 0) {
|
||||
|
||||
@@ -276,7 +276,7 @@ public class VibrationThreadTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void vibrate_singleVibratorRepeatingShortAlwaysOnWaveform_turnsVibratorOnForASecond()
|
||||
public void vibrate_singleVibratorRepeatingShortAlwaysOnWaveform_turnsVibratorOnForLonger()
|
||||
throws Exception {
|
||||
FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);
|
||||
fakeVibrator.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
|
||||
@@ -293,10 +293,70 @@ public class VibrationThreadTest {
|
||||
|
||||
verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BY_USER);
|
||||
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
|
||||
assertEquals(Arrays.asList(expectedOneShot(1000)),
|
||||
assertEquals(Arrays.asList(expectedOneShot(5000)),
|
||||
fakeVibrator.getEffectSegments(vibrationId));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void vibrate_singleVibratorRepeatingPwle_generatesLargestPwles() throws Exception {
|
||||
FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);
|
||||
fakeVibrator.setCapabilities(IVibrator.CAP_COMPOSE_PWLE_EFFECTS);
|
||||
fakeVibrator.setMinFrequency(100);
|
||||
fakeVibrator.setResonantFrequency(150);
|
||||
fakeVibrator.setFrequencyResolution(50);
|
||||
fakeVibrator.setMaxAmplitudes(1, 1, 1);
|
||||
fakeVibrator.setPwleSizeMax(10);
|
||||
|
||||
long vibrationId = 1;
|
||||
VibrationEffect effect = VibrationEffect.startWaveform(targetAmplitude(1))
|
||||
// Very long segment so thread will be cancelled after first PWLE is triggered.
|
||||
.addTransition(Duration.ofMillis(100), targetFrequency(100))
|
||||
.build();
|
||||
VibrationEffect repeatingEffect = VibrationEffect.startComposition()
|
||||
.repeatEffectIndefinitely(effect)
|
||||
.compose();
|
||||
VibrationStepConductor conductor = startThreadAndDispatcher(vibrationId, repeatingEffect);
|
||||
|
||||
assertTrue(waitUntil(() -> !fakeVibrator.getEffectSegments(vibrationId).isEmpty(),
|
||||
TEST_TIMEOUT_MILLIS));
|
||||
conductor.notifyCancelled(Vibration.Status.CANCELLED_BY_USER, /* immediate= */ false);
|
||||
waitForCompletion();
|
||||
|
||||
// PWLE size max was used to generate a single vibrate call with 10 segments.
|
||||
verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BY_USER);
|
||||
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
|
||||
assertEquals(10, fakeVibrator.getEffectSegments(vibrationId).size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void vibrate_singleVibratorRepeatingPrimitives_generatesLargestComposition()
|
||||
throws Exception {
|
||||
FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);
|
||||
fakeVibrator.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
|
||||
fakeVibrator.setSupportedPrimitives(VibrationEffect.Composition.PRIMITIVE_CLICK);
|
||||
fakeVibrator.setCompositionSizeMax(10);
|
||||
|
||||
long vibrationId = 1;
|
||||
VibrationEffect effect = VibrationEffect.startComposition()
|
||||
// Very long delay so thread will be cancelled after first PWLE is triggered.
|
||||
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f, 100)
|
||||
.compose();
|
||||
VibrationEffect repeatingEffect = VibrationEffect.startComposition()
|
||||
.repeatEffectIndefinitely(effect)
|
||||
.compose();
|
||||
VibrationStepConductor conductor = startThreadAndDispatcher(vibrationId, repeatingEffect);
|
||||
|
||||
assertTrue(waitUntil(() -> !fakeVibrator.getEffectSegments(vibrationId).isEmpty(),
|
||||
TEST_TIMEOUT_MILLIS));
|
||||
conductor.notifyCancelled(Vibration.Status.CANCELLED_SUPERSEDED, /* immediate= */ false);
|
||||
waitForCompletion();
|
||||
|
||||
// Composition size max was used to generate a single vibrate call with 10 primitives.
|
||||
verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_SUPERSEDED);
|
||||
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
|
||||
assertEquals(10, fakeVibrator.getEffectSegments(vibrationId).size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void vibrate_singleVibratorRepeatingLongAlwaysOnWaveform_turnsVibratorOnForACycle()
|
||||
throws Exception {
|
||||
@@ -319,7 +379,7 @@ public class VibrationThreadTest {
|
||||
fakeVibrator.getEffectSegments(vibrationId));
|
||||
}
|
||||
|
||||
|
||||
@LargeTest
|
||||
@Test
|
||||
public void vibrate_singleVibratorRepeatingAlwaysOnWaveform_turnsVibratorBackOn()
|
||||
throws Exception {
|
||||
@@ -329,22 +389,21 @@ public class VibrationThreadTest {
|
||||
long vibrationId = 1;
|
||||
int[] amplitudes = new int[]{1, 2};
|
||||
VibrationEffect effect = VibrationEffect.createWaveform(
|
||||
new long[]{900, 50}, amplitudes, 0);
|
||||
new long[]{4900, 50}, amplitudes, 0);
|
||||
VibrationStepConductor conductor = startThreadAndDispatcher(vibrationId, effect);
|
||||
|
||||
assertTrue(waitUntil(() -> fakeVibrator.getAmplitudes().size() > 2 * amplitudes.length,
|
||||
1000 + TEST_TIMEOUT_MILLIS));
|
||||
assertTrue(waitUntil(() -> fakeVibrator.getEffectSegments(vibrationId).size() > 1,
|
||||
5000 + TEST_TIMEOUT_MILLIS));
|
||||
conductor.notifyCancelled(Vibration.Status.CANCELLED_BY_USER, /* immediate= */ false);
|
||||
waitForCompletion();
|
||||
|
||||
verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BY_USER);
|
||||
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
|
||||
assertEquals(2, fakeVibrator.getEffectSegments(vibrationId).size());
|
||||
// First time turn vibrator ON for minimum of 1s.
|
||||
assertEquals(1000L, fakeVibrator.getEffectSegments(vibrationId).get(0).getDuration());
|
||||
// First time turn vibrator ON for minimum of 5s.
|
||||
assertEquals(5000L, fakeVibrator.getEffectSegments(vibrationId).get(0).getDuration());
|
||||
// Vibrator turns off in the middle of the second execution of first step, turn it back ON
|
||||
// for another 1s + remaining of 850ms.
|
||||
assertEquals(1850,
|
||||
// for another 5s + remaining of 850ms.
|
||||
assertEquals(4900 + 50 + 4900,
|
||||
fakeVibrator.getEffectSegments(vibrationId).get(1).getDuration(), /* delta= */ 20);
|
||||
// Set amplitudes for a cycle {1, 2}, start second loop then turn it back on to same value.
|
||||
assertEquals(expectedAmplitudes(1, 2, 1, 1),
|
||||
@@ -530,12 +589,18 @@ public class VibrationThreadTest {
|
||||
|
||||
@Test
|
||||
public void vibrate_singleVibratorComposedEffects_runsDifferentVibrations() throws Exception {
|
||||
mVibratorProviders.get(VIBRATOR_ID).setSupportedEffects(VibrationEffect.EFFECT_CLICK);
|
||||
mVibratorProviders.get(VIBRATOR_ID).setSupportedPrimitives(
|
||||
FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);
|
||||
fakeVibrator.setSupportedEffects(VibrationEffect.EFFECT_CLICK);
|
||||
fakeVibrator.setSupportedPrimitives(
|
||||
VibrationEffect.Composition.PRIMITIVE_CLICK,
|
||||
VibrationEffect.Composition.PRIMITIVE_TICK);
|
||||
mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS,
|
||||
IVibrator.CAP_AMPLITUDE_CONTROL);
|
||||
fakeVibrator.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS,
|
||||
IVibrator.CAP_COMPOSE_PWLE_EFFECTS, IVibrator.CAP_AMPLITUDE_CONTROL);
|
||||
fakeVibrator.setMinFrequency(100);
|
||||
fakeVibrator.setResonantFrequency(150);
|
||||
fakeVibrator.setFrequencyResolution(50);
|
||||
fakeVibrator.setMaxAmplitudes(
|
||||
0.5f /* 100Hz*/, 1 /* 150Hz */, 0.6f /* 200Hz */);
|
||||
|
||||
long vibrationId = 1;
|
||||
VibrationEffect effect = VibrationEffect.startComposition()
|
||||
@@ -543,7 +608,11 @@ public class VibrationThreadTest {
|
||||
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f)
|
||||
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f)
|
||||
.addEffect(VibrationEffect.get(VibrationEffect.EFFECT_CLICK))
|
||||
.addOffDuration(Duration.ofMillis(100))
|
||||
.addEffect(VibrationEffect.startWaveform()
|
||||
.addTransition(Duration.ofMillis(10),
|
||||
targetAmplitude(1), targetFrequency(100))
|
||||
.addTransition(Duration.ofMillis(20), targetFrequency(120))
|
||||
.build())
|
||||
.addEffect(VibrationEffect.get(VibrationEffect.EFFECT_CLICK))
|
||||
.compose();
|
||||
startThreadAndDispatcher(vibrationId, effect);
|
||||
@@ -552,7 +621,7 @@ public class VibrationThreadTest {
|
||||
// Use first duration the vibrator is turned on since we cannot estimate the clicks.
|
||||
verify(mManagerHooks).noteVibratorOn(eq(UID), eq(10L));
|
||||
verify(mManagerHooks).noteVibratorOff(eq(UID));
|
||||
verify(mControllerCallbacks, times(4)).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
|
||||
verify(mControllerCallbacks, times(5)).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
|
||||
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
|
||||
assertFalse(mControllers.get(VIBRATOR_ID).isVibrating());
|
||||
assertEquals(Arrays.asList(
|
||||
@@ -560,6 +629,10 @@ public class VibrationThreadTest {
|
||||
expectedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1, 0),
|
||||
expectedPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f, 0),
|
||||
expectedPrebaked(VibrationEffect.EFFECT_CLICK),
|
||||
expectedRamp(/* startAmplitude= */ 0, /* endAmplitude= */ 0.5f,
|
||||
/* startFrequencyHz= */ 150, /* endFrequencyHz= */ 100, /* duration= */ 10),
|
||||
expectedRamp(/* startAmplitude= */ 0.5f, /* endAmplitude= */ 0.7f,
|
||||
/* startFrequencyHz= */ 100, /* endFrequencyHz= */ 120, /* duration= */ 20),
|
||||
expectedPrebaked(VibrationEffect.EFFECT_CLICK)),
|
||||
mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibrationId));
|
||||
assertEquals(expectedAmplitudes(100), mVibratorProviders.get(VIBRATOR_ID).getAmplitudes());
|
||||
@@ -605,30 +678,36 @@ public class VibrationThreadTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void vibrate_singleVibratorLargePwle_splitsVibratorComposeCalls() {
|
||||
public void vibrate_singleVibratorLargePwle_splitsComposeCallWhenAmplitudeIsLowest() {
|
||||
FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID);
|
||||
fakeVibrator.setCapabilities(IVibrator.CAP_COMPOSE_PWLE_EFFECTS);
|
||||
fakeVibrator.setMinFrequency(100);
|
||||
fakeVibrator.setResonantFrequency(150);
|
||||
fakeVibrator.setFrequencyResolution(50);
|
||||
fakeVibrator.setMaxAmplitudes(1, 1, 1);
|
||||
fakeVibrator.setPwleSizeMax(2);
|
||||
fakeVibrator.setPwleSizeMax(3);
|
||||
|
||||
long vibrationId = 1;
|
||||
VibrationEffect effect = VibrationEffect.startWaveform(targetAmplitude(1))
|
||||
.addSustain(Duration.ofMillis(10))
|
||||
.addTransition(Duration.ofMillis(20), targetAmplitude(0))
|
||||
// Waveform will be split here, after vibration goes to zero amplitude
|
||||
.addTransition(Duration.ZERO, targetAmplitude(0.8f), targetFrequency(100))
|
||||
.addSustain(Duration.ofMillis(30))
|
||||
.addTransition(Duration.ofMillis(40), targetAmplitude(0.6f), targetFrequency(200))
|
||||
// Waveform will be split here at lowest amplitude.
|
||||
.addTransition(Duration.ofMillis(40), targetAmplitude(0.7f), targetFrequency(200))
|
||||
.addTransition(Duration.ofMillis(40), targetAmplitude(0.6f), targetFrequency(200))
|
||||
.build();
|
||||
startThreadAndDispatcher(vibrationId, effect);
|
||||
waitForCompletion();
|
||||
|
||||
verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED);
|
||||
// Vibrator compose called twice.
|
||||
verify(mControllerCallbacks, times(2)).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
|
||||
assertEquals(4, fakeVibrator.getEffectSegments(vibrationId).size());
|
||||
|
||||
// Vibrator compose called 3 times with 2 segments instead of 2 times with 3 segments.
|
||||
// Using best split points instead of max-packing PWLEs.
|
||||
verify(mControllerCallbacks, times(3)).onComplete(eq(VIBRATOR_ID), eq(vibrationId));
|
||||
assertEquals(6, fakeVibrator.getEffectSegments(vibrationId).size());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user