diff options
13 files changed, 841 insertions, 1 deletions
diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java index fa6efa886d89..4366c2811c65 100644 --- a/core/java/android/os/VibrationEffect.java +++ b/core/java/android/os/VibrationEffect.java @@ -28,6 +28,7 @@ import android.content.Context; import android.hardware.vibrator.V1_0.EffectStrength; import android.hardware.vibrator.V1_3.Effect; import android.net.Uri; +import android.os.Vibrator; import android.os.vibrator.PrebakedSegment; import android.os.vibrator.PrimitiveSegment; import android.os.vibrator.RampSegment; @@ -515,6 +516,16 @@ public abstract class VibrationEffect implements Parcelable { public abstract long getDuration(); /** + * Checks if a given {@link Vibrator} can play this effect as intended. + * + * <p>See @link Vibrator#areVibrationFeaturesSupported(VibrationEffect)} for more information + * about what counts as supported by a vibrator, and what counts as not. + * + * @hide + */ + public abstract boolean areVibrationFeaturesSupported(@NonNull Vibrator vibrator); + + /** * Returns true if this effect could represent a touch haptic feedback. * * <p>It is strongly recommended that an instance of {@link VibrationAttributes} is specified @@ -758,6 +769,17 @@ public abstract class VibrationEffect implements Parcelable { /** @hide */ @Override + public boolean areVibrationFeaturesSupported(@NonNull Vibrator vibrator) { + for (VibrationEffectSegment segment : mSegments) { + if (!segment.areVibrationFeaturesSupported(vibrator)) { + return false; + } + } + return true; + } + + /** @hide */ + @Override public boolean isHapticFeedbackCandidate() { long totalDuration = getDuration(); if (totalDuration > MAX_HAPTIC_FEEDBACK_DURATION) { diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java index 7edcdd754f06..4e852e333ec8 100644 --- a/core/java/android/os/Vibrator.java +++ b/core/java/android/os/Vibrator.java @@ -222,6 +222,28 @@ public abstract class Vibrator { } /** + * Checks whether or not the vibrator supports all components of a given {@link VibrationEffect} + * (i.e. the vibrator can play the given effect as intended). + * + * <p>If this method returns {@code true}, then the VibrationEffect should play as expected. + * If {@code false}, playing the VibrationEffect might still make a vibration, but the vibration + * may be significantly degraded from the intention. + * + * <p>This method aggregates the results of feature check methods such as + * {@link #hasAmplitudeControl}, {@link #areAllPrimitivesSupported(int...)}, etc, depending + * on the features that are actually used by the VibrationEffect. + * + * @param effect the {@link VibrationEffect} to check if it is supported + * @return {@code true} if the vibrator can play the given {@code effect} as intended, + * {@code false} otherwise. + * + * @hide + */ + public boolean areVibrationFeaturesSupported(@NonNull VibrationEffect effect) { + return effect.areVibrationFeaturesSupported(this); + } + + /** * Check whether the vibrator can be controlled by an external service with the * {@link IExternalVibratorService}. * diff --git a/core/java/android/os/vibrator/PrebakedSegment.java b/core/java/android/os/vibrator/PrebakedSegment.java index 30f5a5ca86c5..cc76ffa39149 100644 --- a/core/java/android/os/vibrator/PrebakedSegment.java +++ b/core/java/android/os/vibrator/PrebakedSegment.java @@ -22,6 +22,7 @@ import android.annotation.TestApi; import android.os.Parcel; import android.os.Parcelable; import android.os.VibrationEffect; +import android.os.Vibrator; import java.util.Objects; @@ -69,6 +70,31 @@ public final class PrebakedSegment extends VibrationEffectSegment { /** @hide */ @Override + public boolean areVibrationFeaturesSupported(@NonNull Vibrator vibrator) { + if (vibrator.areAllEffectsSupported(mEffectId) == Vibrator.VIBRATION_EFFECT_SUPPORT_YES) { + return true; + } + if (!mFallback) { + // If the Vibrator's support is not `VIBRATION_EFFECT_SUPPORT_YES`, and this effect does + // not support fallbacks, the effect is considered not supported by the vibrator. + return false; + } + // The vibrator does not have hardware support for the effect, but the effect has fallback + // support. Check if a fallback will be available for the effect ID. + switch (mEffectId) { + case VibrationEffect.EFFECT_CLICK: + case VibrationEffect.EFFECT_DOUBLE_CLICK: + case VibrationEffect.EFFECT_HEAVY_CLICK: + case VibrationEffect.EFFECT_TICK: + // Any of these effects are always supported via some form of fallback. + return true; + default: + return false; + } + } + + /** @hide */ + @Override public boolean isHapticFeedbackCandidate() { switch (mEffectId) { case VibrationEffect.EFFECT_CLICK: diff --git a/core/java/android/os/vibrator/PrimitiveSegment.java b/core/java/android/os/vibrator/PrimitiveSegment.java index 2d287e96ef60..cde0ff3aa2f7 100644 --- a/core/java/android/os/vibrator/PrimitiveSegment.java +++ b/core/java/android/os/vibrator/PrimitiveSegment.java @@ -22,6 +22,7 @@ import android.annotation.TestApi; import android.os.Parcel; import android.os.Parcelable; import android.os.VibrationEffect; +import android.os.Vibrator; import com.android.internal.util.Preconditions; @@ -69,6 +70,12 @@ public final class PrimitiveSegment extends VibrationEffectSegment { /** @hide */ @Override + public boolean areVibrationFeaturesSupported(@NonNull Vibrator vibrator) { + return vibrator.areAllPrimitivesSupported(mPrimitiveId); + } + + /** @hide */ + @Override public boolean isHapticFeedbackCandidate() { return true; } diff --git a/core/java/android/os/vibrator/RampSegment.java b/core/java/android/os/vibrator/RampSegment.java index d7d8c49fe28c..034962a5f91b 100644 --- a/core/java/android/os/vibrator/RampSegment.java +++ b/core/java/android/os/vibrator/RampSegment.java @@ -20,6 +20,7 @@ import android.annotation.NonNull; import android.annotation.TestApi; import android.os.Parcel; import android.os.VibrationEffect; +import android.os.Vibrator; import com.android.internal.util.Preconditions; @@ -95,6 +96,29 @@ public final class RampSegment extends VibrationEffectSegment { /** @hide */ @Override + public boolean areVibrationFeaturesSupported(@NonNull Vibrator vibrator) { + boolean areFeaturesSupported = true; + // If the start/end frequencies are not the same, require frequency control since we need to + // ramp up/down the frequency. + if ((mStartFrequencyHz != mEndFrequencyHz) + // If there is no frequency ramping, make sure that the one frequency used does not + // require frequency control. + || frequencyRequiresFrequencyControl(mStartFrequencyHz)) { + areFeaturesSupported &= vibrator.hasFrequencyControl(); + } + // If the start/end amplitudes are not the same, require amplitude control since we need to + // ramp up/down the amplitude. + if ((mStartAmplitude != mEndAmplitude) + // If there is no amplitude ramping, make sure that the amplitude used does not + // require amplitude control. + || amplitudeRequiresAmplitudeControl(mStartAmplitude)) { + areFeaturesSupported &= vibrator.hasAmplitudeControl(); + } + return areFeaturesSupported; + } + + /** @hide */ + @Override public boolean isHapticFeedbackCandidate() { return true; } diff --git a/core/java/android/os/vibrator/StepSegment.java b/core/java/android/os/vibrator/StepSegment.java index 5a0bbf7d9436..115a66c5580a 100644 --- a/core/java/android/os/vibrator/StepSegment.java +++ b/core/java/android/os/vibrator/StepSegment.java @@ -21,6 +21,7 @@ import android.annotation.TestApi; import android.os.Parcel; import android.os.Parcelable; import android.os.VibrationEffect; +import android.os.Vibrator; import com.android.internal.util.Preconditions; @@ -81,6 +82,19 @@ public final class StepSegment extends VibrationEffectSegment { /** @hide */ @Override + public boolean areVibrationFeaturesSupported(@NonNull Vibrator vibrator) { + boolean areFeaturesSupported = true; + if (frequencyRequiresFrequencyControl(mFrequencyHz)) { + areFeaturesSupported &= vibrator.hasFrequencyControl(); + } + if (amplitudeRequiresAmplitudeControl(mAmplitude)) { + areFeaturesSupported &= vibrator.hasAmplitudeControl(); + } + return areFeaturesSupported; + } + + /** @hide */ + @Override public boolean isHapticFeedbackCandidate() { return true; } diff --git a/core/java/android/os/vibrator/VibrationEffectSegment.java b/core/java/android/os/vibrator/VibrationEffectSegment.java index be1055362f1c..75a055fa5273 100644 --- a/core/java/android/os/vibrator/VibrationEffectSegment.java +++ b/core/java/android/os/vibrator/VibrationEffectSegment.java @@ -21,6 +21,7 @@ import android.annotation.TestApi; import android.os.Parcel; import android.os.Parcelable; import android.os.VibrationEffect; +import android.os.Vibrator; /** * Representation of a single segment of a {@link VibrationEffect}. @@ -57,6 +58,15 @@ public abstract class VibrationEffectSegment implements Parcelable { */ public abstract long getDuration(); + /** + * Checks if a given {@link Vibrator} can play this segment as intended. See + * {@link Vibrator#areVibrationFeaturesSupported(VibrationEffect)} for more information about + * what counts as supported by a vibrator, and what counts as not. + * + * @hide + */ + public abstract boolean areVibrationFeaturesSupported(@NonNull Vibrator vibrator); + /** * Returns true if this segment could be a haptic feedback effect candidate. * @@ -149,6 +159,28 @@ public abstract class VibrationEffectSegment implements Parcelable { } } + /** + * Helper method to check if an amplitude requires a vibrator to have amplitude control to play. + * + * @hide + */ + protected static boolean amplitudeRequiresAmplitudeControl(float amplitude) { + return (amplitude != 0) + && (amplitude != 1) + && (amplitude != VibrationEffect.DEFAULT_AMPLITUDE); + } + + /** + * Helper method to check if a frequency requires a vibrator to have frequency control to play. + * + * @hide + */ + protected static boolean frequencyRequiresFrequencyControl(float frequency) { + // Anything other than the default frequency value (represented with "0") requires frequency + // control. + return frequency != 0; + } + @NonNull public static final Creator<VibrationEffectSegment> CREATOR = new Creator<VibrationEffectSegment>() { diff --git a/core/tests/coretests/src/android/os/VibrationEffectTest.java b/core/tests/coretests/src/android/os/VibrationEffectTest.java index 0c7ff4a762b5..627feaba1422 100644 --- a/core/tests/coretests/src/android/os/VibrationEffectTest.java +++ b/core/tests/coretests/src/android/os/VibrationEffectTest.java @@ -35,13 +35,19 @@ import android.content.ContentInterface; import android.content.ContentResolver; import android.content.Context; import android.content.res.Resources; +import android.hardware.vibrator.IVibrator; import android.net.Uri; +import android.os.SystemVibrator; import android.os.VibrationEffect.Composition.UnreachableAfterRepeatingIndefinitelyException; +import android.os.Vibrator; +import android.os.VibratorInfo; import android.os.vibrator.PrebakedSegment; import android.os.vibrator.PrimitiveSegment; import android.os.vibrator.StepSegment; import android.platform.test.annotations.Presubmit; +import androidx.test.InstrumentationRegistry; + import com.android.internal.R; import org.junit.Test; @@ -770,6 +776,45 @@ public class VibrationEffectTest { } @Test + public void testAreVibrationFeaturesSupported_allSegmentsSupported() { + Vibrator vibrator = + createVibratorWithCustomInfo(new VibratorInfo.Builder(/* id= */ 1) + .setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL) + .build()); + + assertTrue(VibrationEffect.createWaveform( + /* timings= */ new long[] {1, 2, 3}, /* repeatIndex= */ -1) + .areVibrationFeaturesSupported(vibrator)); + assertTrue(VibrationEffect.createWaveform( + /* timings= */ new long[] {1, 2, 3}, + /* amplitudes= */ new int[] {10, 20, 40}, + /* repeatIndex= */ 2) + .areVibrationFeaturesSupported(vibrator)); + assertTrue( + VibrationEffect.startComposition() + .addEffect(VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK)) + .repeatEffectIndefinitely(TEST_ONE_SHOT) + .compose() + .areVibrationFeaturesSupported(vibrator)); + } + + @Test + public void testAreVibrationFeaturesSupported_withUnsupportedSegments() { + Vibrator vibrator = + createVibratorWithCustomInfo(new VibratorInfo.Builder(/* id= */ 1).build()); + + assertFalse( + VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK) + .addEffect(VibrationEffect.createWaveform( + /* timings= */ new long[] {1, 2, 3}, + /* amplitudes= */ new int[] {10, 20, 40}, + /* repeatIndex= */ -1)) + .compose() + .areVibrationFeaturesSupported(vibrator)); + } + + @Test public void testIsHapticFeedbackCandidate_repeatingEffects_notCandidates() { assertFalse(VibrationEffect.createWaveform( new long[]{1, 2, 3}, new int[]{1, 2, 3}, 0).isHapticFeedbackCandidate()); @@ -890,4 +935,13 @@ public class VibrationEffectTest { return context; } + + private Vibrator createVibratorWithCustomInfo(VibratorInfo info) { + return new SystemVibrator(InstrumentationRegistry.getContext()) { + @Override + public VibratorInfo getInfo() { + return info; + } + }; + } } diff --git a/core/tests/coretests/src/android/os/VibratorTest.java b/core/tests/coretests/src/android/os/VibratorTest.java index c59a3f518da8..375fdac2e223 100644 --- a/core/tests/coretests/src/android/os/VibratorTest.java +++ b/core/tests/coretests/src/android/os/VibratorTest.java @@ -586,6 +586,189 @@ public class VibratorTest { assertEquals(new VibrationAttributes.Builder().build(), vibrationAttributes); } + @Test + public void areVibrationFeaturesSupported_noAmplitudeControl_fractionalAmplitudes() { + Vibrator vibrator = + createVibratorWithCustomInfo(new VibratorInfo.Builder(/* id= */ 1) + .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS) + .setSupportedEffects(VibrationEffect.EFFECT_THUD) + .build()); + + // Have at least one fractional amplitude (amplitude not min (0) or max (255) or DEFAULT). + assertFalse(vibrator.areVibrationFeaturesSupported(waveformWithAmplitudes(10, 30))); + assertFalse(vibrator.areVibrationFeaturesSupported(waveformWithAmplitudes(10, 255))); + assertFalse(vibrator.areVibrationFeaturesSupported( + VibrationEffect.createOneShot(20, /* amplitude= */ 40))); + } + + @Test + public void areVibrationFeaturesSupported_noAmplitudeControl_nonFractionalAmplitudes() { + Vibrator vibrator = + createVibratorWithCustomInfo(new VibratorInfo.Builder(/* id= */ 1) + .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS) + .setSupportedEffects(VibrationEffect.EFFECT_THUD) + .build()); + + // All amplitudes are min, max, or default. Requires no amplitude control. + assertTrue(vibrator.areVibrationFeaturesSupported( + waveformWithAmplitudes(255, 0, VibrationEffect.DEFAULT_AMPLITUDE, 255))); + assertTrue(vibrator.areVibrationFeaturesSupported( + VibrationEffect.createWaveform( + /* timings= */ new long[] {1, 2, 3}, /* repeatIndex= */ -1))); + assertTrue(vibrator.areVibrationFeaturesSupported( + VibrationEffect.createOneShot(20, VibrationEffect.DEFAULT_AMPLITUDE))); + assertTrue(vibrator.areVibrationFeaturesSupported( + VibrationEffect.createOneShot(20, /* amplitude= */ 255))); + } + + @Test + public void areVibrationFeaturesSupported_withAmplitudeControl() { + Vibrator vibrator = + createVibratorWithCustomInfo(new VibratorInfo.Builder(/* id= */ 1) + .setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL) + .build()); + + // All forms of amplitudes are valid when amplitude control is available. + assertTrue(vibrator.areVibrationFeaturesSupported( + waveformWithAmplitudes(255, 0, VibrationEffect.DEFAULT_AMPLITUDE, 255))); + assertTrue(vibrator.areVibrationFeaturesSupported( + VibrationEffect.createWaveform( + /* timings= */ new long[] {1, 2, 3}, /* repeatIndex= */ -1))); + assertTrue(vibrator.areVibrationFeaturesSupported(waveformWithAmplitudes(10, 30, 50))); + assertTrue(vibrator.areVibrationFeaturesSupported( + waveformWithAmplitudes(7, 255, 0, 0, 60))); + assertTrue(vibrator.areVibrationFeaturesSupported( + VibrationEffect.createOneShot(20, VibrationEffect.DEFAULT_AMPLITUDE))); + assertTrue(vibrator.areVibrationFeaturesSupported( + VibrationEffect.createOneShot(20, /* amplitude= */ 255))); + assertTrue(vibrator.areVibrationFeaturesSupported( + VibrationEffect.createOneShot(20, /* amplitude= */ 40))); + } + + @Test + public void areVibrationFeaturesSupported_primitiveCompositionsWithSupportedPrimitives() { + Vibrator vibrator = createVibratorWithCustomInfo(new VibratorInfo.Builder(/* id= */ 1) + .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS) + .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10) + .build()); + + assertTrue(vibrator.areVibrationFeaturesSupported( + VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK) + .compose())); + assertTrue(vibrator.areVibrationFeaturesSupported( + VibrationEffect.startComposition() + .addPrimitive( + VibrationEffect.Composition.PRIMITIVE_CLICK, + /* scale= */ 0.2f, + /* delay= */ 200) + .compose())); + } + + @Test + public void areVibrationFeaturesSupported_primitiveCompositionsWithUnupportedPrimitives() { + Vibrator vibrator = createVibratorWithCustomInfo(new VibratorInfo.Builder(/* id= */ 1) + .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS) + .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10) + .build()); + + assertFalse(vibrator.areVibrationFeaturesSupported( + VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK) + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_THUD) + .compose())); + assertFalse(vibrator.areVibrationFeaturesSupported( + VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_LOW_TICK) + .compose())); + } + + @Test + public void areVibrationFeaturesSupported_composedEffects_allComponentsSupported() { + Vibrator vibrator = createVibratorWithCustomInfo(new VibratorInfo.Builder(/* id= */ 1) + .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS | IVibrator.CAP_AMPLITUDE_CONTROL) + .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10) + .setSupportedEffects(VibrationEffect.EFFECT_TICK, VibrationEffect.EFFECT_POP) + .build()); + + assertTrue(vibrator.areVibrationFeaturesSupported( + VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK) + .addEffect(VibrationEffect.createWaveform( + /* timings= */ new long[] {1, 2, 3}, + /* amplitudes= */ new int[] {10, 20, 255}, + /* repeatIndex= */ -1)) + .addEffect(VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK)) + .addEffect(VibrationEffect.createPredefined(VibrationEffect.EFFECT_POP)) + .compose())); + + vibrator = createVibratorWithCustomInfo(new VibratorInfo.Builder(/* id= */ 1) + .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS) + .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_THUD, 10) + .setSupportedEffects(VibrationEffect.EFFECT_POP, VibrationEffect.EFFECT_CLICK) + .build()); + + assertTrue(vibrator.areVibrationFeaturesSupported( + VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_THUD) + .addEffect(VibrationEffect.createWaveform( + // These timings are given either 0 or default amplitudes, which + // do not require vibrator's amplitude control. + /* timings= */ new long[] {1, 2, 3}, + /* repeatIndex= */ -1)) + .addEffect(VibrationEffect.createPredefined(VibrationEffect.EFFECT_POP)) + .addEffect(VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK)) + .compose())); + } + + @Test + public void areVibrationFeaturesSupported_composedEffects_someComponentsUnupported() { + Vibrator vibrator = createVibratorWithCustomInfo(new VibratorInfo.Builder(/* id= */ 1) + .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS | IVibrator.CAP_AMPLITUDE_CONTROL) + .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10) + .setSupportedEffects(VibrationEffect.EFFECT_TICK, VibrationEffect.EFFECT_POP) + .build()); + + // Not supported due to the TICK primitive, which the vibrator has no support for. + assertFalse(vibrator.areVibrationFeaturesSupported( + VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK) + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK) + .addEffect(VibrationEffect.createWaveform( + /* timings= */ new long[] {1, 2, 3}, + /* amplitudes= */ new int[] {10, 20, 255}, + /* repeatIndex= */ -1)) + .compose())); + // Not supported due to the THUD effect, which the vibrator has no support for. + assertFalse(vibrator.areVibrationFeaturesSupported( + VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK) + .addEffect(VibrationEffect.createWaveform( + /* timings= */ new long[] {1, 2, 3}, + /* amplitudes= */ new int[] {10, 20, 255}, + /* repeatIndex= */ -1)) + .addEffect(VibrationEffect.createPredefined(VibrationEffect.EFFECT_THUD)) + .compose())); + + vibrator = createVibratorWithCustomInfo(new VibratorInfo.Builder(/* id= */ 1) + .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS) + .setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_THUD, 10) + .setSupportedEffects(VibrationEffect.EFFECT_POP) + .build()); + + // Not supported due to fractional amplitudes (amplitudes not min (0) or max (255) or + // DEFAULT), because the vibrator has no amplitude control. + assertFalse(vibrator.areVibrationFeaturesSupported( + VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_THUD) + .addEffect(VibrationEffect.createWaveform( + /* timings= */ new long[] {1, 2, 3}, + /* amplitudes= */ new int[] {10, 20, 255}, + /* repeatIndex= */ -1)) + .addEffect(VibrationEffect.createPredefined(VibrationEffect.EFFECT_POP)) + .compose())); + } + /** * Asserts that the frequency profile is empty, and therefore frequency control isn't supported. */ @@ -593,4 +776,21 @@ public class VibratorTest { assertTrue(info.getFrequencyProfile().isEmpty()); assertEquals(false, info.hasCapability(IVibrator.CAP_FREQUENCY_CONTROL)); } + + private Vibrator createVibratorWithCustomInfo(VibratorInfo info) { + return new SystemVibrator(mContextSpy) { + @Override + public VibratorInfo getInfo() { + return info; + } + }; + } + + private static VibrationEffect waveformWithAmplitudes(int...amplitudes) { + long[] timings = new long[amplitudes.length]; + for (int i = 0; i < timings.length; i++) { + timings[i] = i * 2; // Arbitrary timings. + } + return VibrationEffect.createWaveform(timings, amplitudes, /* repeatIndex= */ -1); + } } diff --git a/core/tests/coretests/src/android/os/vibrator/PrebakedSegmentTest.java b/core/tests/coretests/src/android/os/vibrator/PrebakedSegmentTest.java index a0e1f437f1da..9099274e2767 100644 --- a/core/tests/coretests/src/android/os/vibrator/PrebakedSegmentTest.java +++ b/core/tests/coretests/src/android/os/vibrator/PrebakedSegmentTest.java @@ -25,9 +25,14 @@ import static org.testng.Assert.assertNotEquals; import static org.testng.Assert.assertThrows; import android.os.Parcel; +import android.os.SystemVibrator; import android.os.VibrationEffect; +import android.os.Vibrator; +import android.os.VibratorInfo; import android.platform.test.annotations.Presubmit; +import androidx.test.InstrumentationRegistry; + import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.junit.MockitoJUnitRunner; @@ -146,9 +151,149 @@ public class PrebakedSegmentTest { } @Test + public void testVibrationFeaturesSupport_idsWithFallback_fallbackEnabled_vibratorSupport() { + Vibrator vibrator = createVibratorWithSupportedEffects( + VibrationEffect.EFFECT_TICK, + VibrationEffect.EFFECT_CLICK, + VibrationEffect.EFFECT_DOUBLE_CLICK, + VibrationEffect.EFFECT_HEAVY_CLICK); + + assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_TICK) + .areVibrationFeaturesSupported(vibrator)); + assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_CLICK) + .areVibrationFeaturesSupported(vibrator)); + assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_DOUBLE_CLICK) + .areVibrationFeaturesSupported(vibrator)); + assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_HEAVY_CLICK) + .areVibrationFeaturesSupported(vibrator)); + + } + + @Test + public void testVibrationFeaturesSupport_idsWithFallback_fallbackEnabled_noVibratorSupport() { + Vibrator vibrator = createVibratorWithSupportedEffects(new int[0]); + + assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_TICK) + .areVibrationFeaturesSupported(vibrator)); + assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_CLICK) + .areVibrationFeaturesSupported(vibrator)); + assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_DOUBLE_CLICK) + .areVibrationFeaturesSupported(vibrator)); + assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_HEAVY_CLICK) + .areVibrationFeaturesSupported(vibrator)); + } + + @Test + public void testVibrationFeaturesSupport_idsWithFallback_fallbackDisabled_vibratorSupport() { + Vibrator vibrator = createVibratorWithSupportedEffects( + VibrationEffect.EFFECT_TICK, + VibrationEffect.EFFECT_CLICK, + VibrationEffect.EFFECT_DOUBLE_CLICK, + VibrationEffect.EFFECT_HEAVY_CLICK); + + assertTrue(createSegmentWithoutFallback(VibrationEffect.EFFECT_TICK) + .areVibrationFeaturesSupported(vibrator)); + assertTrue(createSegmentWithoutFallback(VibrationEffect.EFFECT_CLICK) + .areVibrationFeaturesSupported(vibrator)); + assertTrue(createSegmentWithoutFallback(VibrationEffect.EFFECT_DOUBLE_CLICK) + .areVibrationFeaturesSupported(vibrator)); + assertTrue(createSegmentWithoutFallback(VibrationEffect.EFFECT_HEAVY_CLICK) + .areVibrationFeaturesSupported(vibrator)); + } + + @Test + public void testVibrationFeaturesSupport_idsWithFallback_fallbackDisabled_noVibratorSupport() { + Vibrator vibrator = createVibratorWithSupportedEffects(new int[0]); + + assertFalse(createSegmentWithoutFallback(VibrationEffect.EFFECT_TICK) + .areVibrationFeaturesSupported(vibrator)); + assertFalse(createSegmentWithoutFallback(VibrationEffect.EFFECT_CLICK) + .areVibrationFeaturesSupported(vibrator)); + assertFalse(createSegmentWithoutFallback(VibrationEffect.EFFECT_DOUBLE_CLICK) + .areVibrationFeaturesSupported(vibrator)); + assertFalse(createSegmentWithoutFallback(VibrationEffect.EFFECT_HEAVY_CLICK) + .areVibrationFeaturesSupported(vibrator)); + } + + @Test + public void testVibrationFeaturesSupport_idsWithNoFallback_fallbackEnabled_vibratorSupport() { + Vibrator vibrator = createVibratorWithSupportedEffects( + VibrationEffect.EFFECT_THUD, + VibrationEffect.EFFECT_POP, + VibrationEffect.EFFECT_TEXTURE_TICK); + + assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_THUD) + .areVibrationFeaturesSupported(vibrator)); + assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_POP) + .areVibrationFeaturesSupported(vibrator)); + assertTrue(createSegmentWithFallback(VibrationEffect.EFFECT_TEXTURE_TICK) + .areVibrationFeaturesSupported(vibrator)); + } + + @Test + public void testVibrationFeaturesSupport_idsWithNoFallback_fallbackEnabled_noVibratorSupport() { + Vibrator vibrator = createVibratorWithSupportedEffects(new int[0]); + + assertFalse(createSegmentWithFallback(VibrationEffect.EFFECT_THUD) + .areVibrationFeaturesSupported(vibrator)); + assertFalse(createSegmentWithFallback(VibrationEffect.EFFECT_POP) + .areVibrationFeaturesSupported(vibrator)); + assertFalse(createSegmentWithFallback(VibrationEffect.EFFECT_TEXTURE_TICK) + .areVibrationFeaturesSupported(vibrator)); + } + + @Test + public void testVibrationFeaturesSupport_idsWithNoFallback_fallbackDisabled_vibratorSupport() { + Vibrator vibrator = createVibratorWithSupportedEffects( + VibrationEffect.EFFECT_THUD, + VibrationEffect.EFFECT_POP, + VibrationEffect.EFFECT_TEXTURE_TICK); + + assertTrue(createSegmentWithoutFallback(VibrationEffect.EFFECT_THUD) + .areVibrationFeaturesSupported(vibrator)); + assertTrue(createSegmentWithoutFallback(VibrationEffect.EFFECT_POP) + .areVibrationFeaturesSupported(vibrator)); + assertTrue(createSegmentWithoutFallback(VibrationEffect.EFFECT_TEXTURE_TICK) + .areVibrationFeaturesSupported(vibrator)); + } + + @Test + public void testVibrationFeaturesSupport_idsWithNoFallback_fallbackDisabled_noVibSupport() { + Vibrator vibrator = createVibratorWithSupportedEffects(new int[0]); + + assertFalse(createSegmentWithoutFallback(VibrationEffect.EFFECT_THUD) + .areVibrationFeaturesSupported(vibrator)); + assertFalse(createSegmentWithoutFallback(VibrationEffect.EFFECT_POP) + .areVibrationFeaturesSupported(vibrator)); + assertFalse(createSegmentWithoutFallback(VibrationEffect.EFFECT_TEXTURE_TICK) + .areVibrationFeaturesSupported(vibrator)); + } + + @Test public void testIsHapticFeedbackCandidate_prebakedRingtones_notCandidates() { assertFalse(new PrebakedSegment( VibrationEffect.RINGTONES[1], true, VibrationEffect.EFFECT_STRENGTH_MEDIUM) .isHapticFeedbackCandidate()); } + + private static PrebakedSegment createSegmentWithFallback(int effectId) { + // note: arbitrary effect strength being used. + return new PrebakedSegment(effectId, true, VibrationEffect.EFFECT_STRENGTH_MEDIUM); + } + + private static PrebakedSegment createSegmentWithoutFallback(int effectId) { + // note: arbitrary effect strength being used. + return new PrebakedSegment(effectId, false, VibrationEffect.EFFECT_STRENGTH_MEDIUM); + } + + private static Vibrator createVibratorWithSupportedEffects(int... supportedEffects) { + return new SystemVibrator(InstrumentationRegistry.getContext()) { + @Override + public VibratorInfo getInfo() { + return new VibratorInfo.Builder(/* id= */ 1) + .setSupportedEffects(supportedEffects) + .build(); + } + }; + } } diff --git a/core/tests/coretests/src/android/os/vibrator/PrimitiveSegmentTest.java b/core/tests/coretests/src/android/os/vibrator/PrimitiveSegmentTest.java index a69055335663..298438fdc243 100644 --- a/core/tests/coretests/src/android/os/vibrator/PrimitiveSegmentTest.java +++ b/core/tests/coretests/src/android/os/vibrator/PrimitiveSegmentTest.java @@ -17,15 +17,22 @@ package android.os.vibrator; import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertSame; import static junit.framework.Assert.assertTrue; import static org.testng.Assert.assertThrows; +import android.hardware.vibrator.IVibrator; import android.os.Parcel; +import android.os.SystemVibrator; import android.os.VibrationEffect; +import android.os.Vibrator; +import android.os.VibratorInfo; import android.platform.test.annotations.Presubmit; +import androidx.test.InstrumentationRegistry; + import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.junit.MockitoJUnitRunner; @@ -139,6 +146,38 @@ public class PrimitiveSegmentTest { } @Test + public void testVibrationFeaturesSupport_primitiveSupportedByVibrator() { + assertTrue(createSegment(VibrationEffect.Composition.PRIMITIVE_CLICK) + .areVibrationFeaturesSupported( + createVibratorWithSupportedPrimitive( + VibrationEffect.Composition.PRIMITIVE_CLICK))); + assertTrue(createSegment(VibrationEffect.Composition.PRIMITIVE_THUD) + .areVibrationFeaturesSupported( + createVibratorWithSupportedPrimitive( + VibrationEffect.Composition.PRIMITIVE_THUD))); + assertTrue(createSegment(VibrationEffect.Composition.PRIMITIVE_QUICK_RISE) + .areVibrationFeaturesSupported( + createVibratorWithSupportedPrimitive( + VibrationEffect.Composition.PRIMITIVE_QUICK_RISE))); + } + + @Test + public void testVibrationFeaturesSupport_primitiveNotSupportedByVibrator() { + assertFalse(createSegment(VibrationEffect.Composition.PRIMITIVE_CLICK) + .areVibrationFeaturesSupported( + createVibratorWithSupportedPrimitive( + VibrationEffect.Composition.PRIMITIVE_THUD))); + assertFalse(createSegment(VibrationEffect.Composition.PRIMITIVE_THUD) + .areVibrationFeaturesSupported( + createVibratorWithSupportedPrimitive( + VibrationEffect.Composition.PRIMITIVE_CLICK))); + assertFalse(createSegment(VibrationEffect.Composition.PRIMITIVE_THUD) + .areVibrationFeaturesSupported( + createVibratorWithSupportedPrimitive( + VibrationEffect.Composition.PRIMITIVE_QUICK_RISE))); + } + + @Test public void testIsHapticFeedbackCandidate_returnsTrue() { assertTrue(new PrimitiveSegment( VibrationEffect.Composition.PRIMITIVE_NOOP, 1, 10).isHapticFeedbackCandidate()); @@ -151,4 +190,21 @@ public class PrimitiveSegmentTest { assertTrue(new PrimitiveSegment( VibrationEffect.Composition.PRIMITIVE_SPIN, 1, 10).isHapticFeedbackCandidate()); } + + private static PrimitiveSegment createSegment(int primitiveId) { + // note: arbitrary scale and delay values being used. + return new PrimitiveSegment(primitiveId, 0.2f, 10); + } + + private static Vibrator createVibratorWithSupportedPrimitive(int primitiveId) { + return new SystemVibrator(InstrumentationRegistry.getContext()) { + @Override + public VibratorInfo getInfo() { + return new VibratorInfo.Builder(/* id= */ 1) + .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS) + .setSupportedPrimitive(primitiveId, 10) + .build(); + } + }; + } } diff --git a/core/tests/coretests/src/android/os/vibrator/RampSegmentTest.java b/core/tests/coretests/src/android/os/vibrator/RampSegmentTest.java index 3291b2d8edd9..6f8c20558ddc 100644 --- a/core/tests/coretests/src/android/os/vibrator/RampSegmentTest.java +++ b/core/tests/coretests/src/android/os/vibrator/RampSegmentTest.java @@ -16,26 +16,40 @@ package android.os.vibrator; +import static android.os.VibrationEffect.DEFAULT_AMPLITUDE; + import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertSame; import static junit.framework.Assert.assertTrue; +import static org.mockito.Mockito.when; import static org.testng.Assert.assertThrows; import android.os.Parcel; import android.os.VibrationEffect; +import android.os.Vibrator; import android.platform.test.annotations.Presubmit; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoJUnitRunner; +import org.mockito.junit.MockitoRule; @Presubmit @RunWith(MockitoJUnitRunner.class) public class RampSegmentTest { private static final float TOLERANCE = 1e-2f; + @Rule + public MockitoRule mMockitoRule = MockitoJUnit.rule(); + + @Mock + private Vibrator mVibrator; + @Test public void testCreation() { RampSegment ramp = new RampSegment(/* startAmplitude= */ 1, /* endAmplitude= */ 0, @@ -66,7 +80,7 @@ public class RampSegmentTest { new RampSegment(0, 0, 0, 0, 0).validate(); assertThrows(IllegalArgumentException.class, - () -> new RampSegment(VibrationEffect.DEFAULT_AMPLITUDE, 0, 0, 0, 0).validate()); + () -> new RampSegment(DEFAULT_AMPLITUDE, 0, 0, 0, 0).validate()); assertThrows(IllegalArgumentException.class, () -> new RampSegment(/* startAmplitude= */ -2, 0, 0, 0, 0).validate()); assertThrows(IllegalArgumentException.class, @@ -142,6 +156,129 @@ public class RampSegmentTest { } @Test + public void testVibrationFeaturesSupport_amplitudeAndFrequencyControls_supported() { + when(mVibrator.hasAmplitudeControl()).thenReturn(true); + when(mVibrator.hasFrequencyControl()).thenReturn(true); + + // Increasing amplitude + assertTrue(new RampSegment(0.5f, 1, 0, 0, 10).areVibrationFeaturesSupported(mVibrator)); + // Increasing frequency + assertTrue(new RampSegment(0.5f, 0.5f, 0, 1, 10).areVibrationFeaturesSupported(mVibrator)); + // Decreasing amplitude + assertTrue(new RampSegment(1, 0.5f, 0, 0, 10).areVibrationFeaturesSupported(mVibrator)); + // Decreasing frequency + assertTrue(new RampSegment(0.5f, 0.5f, 1, 0, 10).areVibrationFeaturesSupported(mVibrator)); + // Zero duration + assertTrue(new RampSegment(0.5f, 0.5f, 1, 0, 0).areVibrationFeaturesSupported(mVibrator)); + } + + @Test + public void testVibrationFeaturesSupport_noAmplitudeControl_unsupportedForChangingAmplitude() { + when(mVibrator.hasAmplitudeControl()).thenReturn(false); + when(mVibrator.hasFrequencyControl()).thenReturn(true); + + // Test with increasing/decreasing amplitudes. + assertFalse(new RampSegment(0.5f, 1, 0, 0, 10).areVibrationFeaturesSupported(mVibrator)); + assertFalse(new RampSegment(1, 0.5f, 0, 0, 10).areVibrationFeaturesSupported(mVibrator)); + } + + @Test + public void testVibrationFeaturesSupport_noAmplitudeControl_fractionalAmplitudeUnsupported() { + when(mVibrator.hasAmplitudeControl()).thenReturn(false); + when(mVibrator.hasFrequencyControl()).thenReturn(true); + + assertFalse(new RampSegment(0.2f, 0.2f, 0, 0, 10).areVibrationFeaturesSupported(mVibrator)); + assertFalse(new RampSegment(0, 0.2f, 0, 0, 10).areVibrationFeaturesSupported(mVibrator)); + assertFalse(new RampSegment(0.2f, 0, 0, 0, 10).areVibrationFeaturesSupported(mVibrator)); + } + + @Test + public void testVibrationFeaturesSupport_unchangingZeroAmplitude_supported() { + RampSegment amplitudeZeroWithIncreasingFrequency = new RampSegment(1, 1, 0.5f, 0.8f, 10); + RampSegment amplitudeZeroWithDecreasingFrequency = new RampSegment(1, 1, 0.8f, 0.5f, 10); + when(mVibrator.hasFrequencyControl()).thenReturn(true); + when(mVibrator.hasAmplitudeControl()).thenReturn(false); + + assertTrue(amplitudeZeroWithIncreasingFrequency.areVibrationFeaturesSupported(mVibrator)); + assertTrue(amplitudeZeroWithDecreasingFrequency.areVibrationFeaturesSupported(mVibrator)); + + when(mVibrator.hasAmplitudeControl()).thenReturn(true); + + assertTrue(amplitudeZeroWithIncreasingFrequency.areVibrationFeaturesSupported(mVibrator)); + assertTrue(amplitudeZeroWithDecreasingFrequency.areVibrationFeaturesSupported(mVibrator)); + } + + @Test + public void testVibrationFeaturesSupport_unchangingOneAmplitude_supported() { + RampSegment amplitudeOneWithIncreasingFrequency = new RampSegment(1, 1, 0.5f, 0.8f, 10); + RampSegment amplitudeOneWithDecreasingFrequency = new RampSegment(1, 1, 0.8f, 0.5f, 10); + when(mVibrator.hasFrequencyControl()).thenReturn(true); + when(mVibrator.hasAmplitudeControl()).thenReturn(false); + + assertTrue(amplitudeOneWithIncreasingFrequency.areVibrationFeaturesSupported(mVibrator)); + assertTrue(amplitudeOneWithDecreasingFrequency.areVibrationFeaturesSupported(mVibrator)); + + when(mVibrator.hasAmplitudeControl()).thenReturn(true); + + assertTrue(amplitudeOneWithIncreasingFrequency.areVibrationFeaturesSupported(mVibrator)); + assertTrue(amplitudeOneWithDecreasingFrequency.areVibrationFeaturesSupported(mVibrator)); + } + + @Test + public void testVibrationFeaturesSupport_unchangingDefaultAmplitude_supported() { + RampSegment defaultAmplitudeIncreasingFrequency = + new RampSegment(DEFAULT_AMPLITUDE, DEFAULT_AMPLITUDE, 0.5f, 0.8f, 10); + RampSegment defaultAmplitudeDecreasingFrequency = + new RampSegment(DEFAULT_AMPLITUDE, DEFAULT_AMPLITUDE, 0.8f, 0.5f, 10); + when(mVibrator.hasFrequencyControl()).thenReturn(true); + when(mVibrator.hasAmplitudeControl()).thenReturn(false); + + assertTrue(defaultAmplitudeIncreasingFrequency.areVibrationFeaturesSupported(mVibrator)); + assertTrue(defaultAmplitudeDecreasingFrequency.areVibrationFeaturesSupported(mVibrator)); + + when(mVibrator.hasAmplitudeControl()).thenReturn(true); + + assertTrue(defaultAmplitudeIncreasingFrequency.areVibrationFeaturesSupported(mVibrator)); + assertTrue(defaultAmplitudeDecreasingFrequency.areVibrationFeaturesSupported(mVibrator)); + } + + @Test + public void testVibrationFeaturesSupport_noFrequencyControl_unsupportedForChangingFrequency() { + when(mVibrator.hasAmplitudeControl()).thenReturn(true); + when(mVibrator.hasFrequencyControl()).thenReturn(false); + + // Test with increasing/decreasing frequencies. + assertFalse(new RampSegment(0, 0, 0.2f, 0.4f, 10).areVibrationFeaturesSupported(mVibrator)); + assertFalse(new RampSegment(0, 0, 0.4f, 0.2f, 10).areVibrationFeaturesSupported(mVibrator)); + } + + @Test + public void testVibrationFeaturesSupport_noFrequencyControl_fractionalFrequencyUnsupported() { + when(mVibrator.hasAmplitudeControl()).thenReturn(true); + when(mVibrator.hasFrequencyControl()).thenReturn(false); + + assertFalse(new RampSegment(0, 0, 0.2f, 0.2f, 10).areVibrationFeaturesSupported(mVibrator)); + assertFalse(new RampSegment(0, 0, 0.2f, 0, 10).areVibrationFeaturesSupported(mVibrator)); + assertFalse(new RampSegment(0, 0, 0, 0.2f, 10).areVibrationFeaturesSupported(mVibrator)); + } + + @Test + public void testVibrationFeaturesSupport_unchangingZeroFrequency_supported() { + RampSegment frequencyZeroWithIncreasingAmplitude = new RampSegment(0.1f, 1, 0, 0, 10); + RampSegment frequencyZeroWithDecreasingAmplitude = new RampSegment(1, 0.1f, 0, 0, 10); + when(mVibrator.hasAmplitudeControl()).thenReturn(true); + when(mVibrator.hasFrequencyControl()).thenReturn(false); + + assertTrue(frequencyZeroWithIncreasingAmplitude.areVibrationFeaturesSupported(mVibrator)); + assertTrue(frequencyZeroWithDecreasingAmplitude.areVibrationFeaturesSupported(mVibrator)); + + when(mVibrator.hasFrequencyControl()).thenReturn(true); + + assertTrue(frequencyZeroWithIncreasingAmplitude.areVibrationFeaturesSupported(mVibrator)); + assertTrue(frequencyZeroWithDecreasingAmplitude.areVibrationFeaturesSupported(mVibrator)); + } + + @Test public void testIsHapticFeedbackCandidate_returnsTrue() { // A single ramp segment duration is not checked here, but contributes to the effect known // duration checked in VibrationEffect implementations. diff --git a/core/tests/coretests/src/android/os/vibrator/StepSegmentTest.java b/core/tests/coretests/src/android/os/vibrator/StepSegmentTest.java index 44241273d9e3..ade21613013f 100644 --- a/core/tests/coretests/src/android/os/vibrator/StepSegmentTest.java +++ b/core/tests/coretests/src/android/os/vibrator/StepSegmentTest.java @@ -21,21 +21,33 @@ import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertSame; import static junit.framework.Assert.assertTrue; +import static org.mockito.Mockito.when; import static org.testng.Assert.assertThrows; import android.os.Parcel; import android.os.VibrationEffect; +import android.os.Vibrator; import android.platform.test.annotations.Presubmit; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoJUnitRunner; +import org.mockito.junit.MockitoRule; @Presubmit @RunWith(MockitoJUnitRunner.class) public class StepSegmentTest { private static final float TOLERANCE = 1e-2f; + @Rule + public MockitoRule mMockitoRule = MockitoJUnit.rule(); + + @Mock + private Vibrator mVibrator; + @Test public void testCreation() { StepSegment step = new StepSegment(/* amplitude= */ 1f, /* frequencyHz= */ 1f, @@ -156,6 +168,95 @@ public class StepSegmentTest { } @Test + public void testVibrationFeaturesSupport_zeroAmplitude_supported() { + StepSegment segment = + new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 0, /* duration= */ 0); + when(mVibrator.hasAmplitudeControl()).thenReturn(true); + + assertTrue(segment.areVibrationFeaturesSupported(mVibrator)); + + when(mVibrator.hasAmplitudeControl()).thenReturn(false); + + assertTrue(segment.areVibrationFeaturesSupported(mVibrator)); + } + + @Test + public void testVibrationFeaturesSupport_maxAmplitude_supported() { + StepSegment segment = + new StepSegment(/* amplitude= */ 1, /* frequencyHz= */ 0, /* duration= */ 0); + when(mVibrator.hasAmplitudeControl()).thenReturn(true); + + assertTrue(segment.areVibrationFeaturesSupported(mVibrator)); + + when(mVibrator.hasAmplitudeControl()).thenReturn(false); + + assertTrue(segment.areVibrationFeaturesSupported(mVibrator)); + } + + @Test + public void testVibrationFeaturesSupport_defaultAmplitude_supported() { + StepSegment segment = + new StepSegment( + /* amplitude= */ VibrationEffect.DEFAULT_AMPLITUDE, + /* frequencyHz= */ 0, + /* duration= */ 0); + when(mVibrator.hasAmplitudeControl()).thenReturn(true); + + assertTrue(segment.areVibrationFeaturesSupported(mVibrator)); + + when(mVibrator.hasAmplitudeControl()).thenReturn(false); + + assertTrue(segment.areVibrationFeaturesSupported(mVibrator)); + } + + @Test + public void testVibrationFeaturesSupport_fractionalAmplitude_hasAmplitudeCtrl_supported() { + when(mVibrator.hasAmplitudeControl()).thenReturn(true); + + assertTrue(new StepSegment(/* amplitude= */ 0.2f, /* frequencyHz= */ 0, /* duration= */ 0) + .areVibrationFeaturesSupported(mVibrator)); + } + + @Test + public void testVibrationFeaturesSupport_fractionalAmplitude_hasNoAmplitudeCtrl_notSupported() { + when(mVibrator.hasAmplitudeControl()).thenReturn(false); + + assertFalse(new StepSegment(/* amplitude= */ 0.2f, /* frequencyHz= */ 0, /* duration= */ 0) + .areVibrationFeaturesSupported(mVibrator)); + } + + @Test + public void testVibrationFeaturesSupport_zeroFrequency_supported() { + StepSegment segment = + new StepSegment(/* amplitude= */ 0f, /* frequencyHz= */ 0, /* duration= */ 0); + when(mVibrator.hasFrequencyControl()).thenReturn(false); + + assertTrue(segment.areVibrationFeaturesSupported(mVibrator)); + + when(mVibrator.hasFrequencyControl()).thenReturn(true); + + assertTrue(segment.areVibrationFeaturesSupported(mVibrator)); + } + + @Test + public void testVibrationFeaturesSupport_nonZeroFrequency_hasFrequencyCtrl_supported() { + StepSegment segment = + new StepSegment(/* amplitude= */ 0f, /* frequencyHz= */ 0.2f, /* duration= */ 0); + when(mVibrator.hasFrequencyControl()).thenReturn(true); + + assertTrue(segment.areVibrationFeaturesSupported(mVibrator)); + } + + @Test + public void testVibrationFeaturesSupport_nonZeroFrequency_hasNoFrequencyCtrl_notSupported() { + StepSegment segment = + new StepSegment(/* amplitude= */ 0f, /* frequencyHz= */ 0.2f, /* duration= */ 0); + when(mVibrator.hasFrequencyControl()).thenReturn(false); + + assertFalse(segment.areVibrationFeaturesSupported(mVibrator)); + } + + @Test public void testIsHapticFeedbackCandidate_returnsTrue() { // A single step segment duration is not checked here, but contributes to the effect known // duration checked in VibrationEffect implementations. |