Merge "Refactor DeviceAdapter to extract split-ramp logic" into main
This commit is contained in:
@@ -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(
|
||||
|
||||
@@ -118,12 +118,6 @@ public final class PrebakedSegment extends VibrationEffectSegment {
|
||||
}
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
@Override
|
||||
public boolean hasNonZeroAmplitude() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
@NonNull
|
||||
@Override
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user