Loading core/api/current.txt +2 −2 Original line number Diff line number Diff line Loading @@ -34737,8 +34737,6 @@ package android.os { method public static android.os.VibrationEffect createWaveform(long[], int[], int); method public int describeContents(); method @NonNull public static android.os.VibrationEffect.Composition startComposition(); method @FlaggedApi("android.os.vibrator.normalized_pwle_effects") @NonNull public static android.os.VibrationEffect.WaveformEnvelopeBuilder startWaveformEnvelope(); method @FlaggedApi("android.os.vibrator.normalized_pwle_effects") @NonNull public static android.os.VibrationEffect.WaveformEnvelopeBuilder startWaveformEnvelope(@FloatRange(from=0) float); field @NonNull public static final android.os.Parcelable.Creator<android.os.VibrationEffect> CREATOR; field public static final int DEFAULT_AMPLITUDE = -1; // 0xffffffff field public static final int EFFECT_CLICK = 0; // 0x0 Loading Loading @@ -34766,8 +34764,10 @@ package android.os { } @FlaggedApi("android.os.vibrator.normalized_pwle_effects") public static final class VibrationEffect.WaveformEnvelopeBuilder { ctor public VibrationEffect.WaveformEnvelopeBuilder(); method @FlaggedApi("android.os.vibrator.normalized_pwle_effects") @NonNull public android.os.VibrationEffect.WaveformEnvelopeBuilder addControlPoint(@FloatRange(from=0, to=1) float, @FloatRange(from=0) float, int); method @FlaggedApi("android.os.vibrator.normalized_pwle_effects") @NonNull public android.os.VibrationEffect build(); method @FlaggedApi("android.os.vibrator.normalized_pwle_effects") @NonNull public android.os.VibrationEffect.WaveformEnvelopeBuilder setInitialFrequencyHz(@FloatRange(from=0) float); } public abstract class Vibrator { core/java/android/os/VibrationEffect.java +39 −58 Original line number Diff line number Diff line Loading @@ -1825,52 +1825,6 @@ public abstract class VibrationEffect implements Parcelable { } } /** * Start building a waveform vibration. * * <p>The waveform envelope builder offers more flexibility for creating waveform effects, * allowing control over vibration amplitude and frequency via smooth transitions between * values. The waveform will start the first transition from the vibrator off state, using * the same frequency of the first control point. To provide a different initial vibration * frequency, use {@link #startWaveformEnvelope(float)}. * * <p>Note: To check whether waveform envelope effects are supported, use * {@link Vibrator#areEnvelopeEffectsSupported()}. * * @see VibrationEffect.WaveformEnvelopeBuilder */ @FlaggedApi(Flags.FLAG_NORMALIZED_PWLE_EFFECTS) @NonNull public static VibrationEffect.WaveformEnvelopeBuilder startWaveformEnvelope() { return new WaveformEnvelopeBuilder(); } /** * Start building a waveform vibration with an initial frequency. * * <p>The waveform envelope builder offers more flexibility for creating waveform effects, * allowing control over vibration amplitude and frequency via smooth transitions between * values. * * <p>This is the same as {@link #startWaveformEnvelope()}, but the waveform will start * vibrating at given frequency, in hertz, while it transitions to the new amplitude and * frequency of the first control point. * * <p>Note: To check whether waveform envelope effects are supported, use * {@link Vibrator#areEnvelopeEffectsSupported()}. * * @param initialFrequencyHz The starting frequency of the vibration, in hertz. Must be greater * than zero. * * @see VibrationEffect.WaveformEnvelopeBuilder */ @FlaggedApi(Flags.FLAG_NORMALIZED_PWLE_EFFECTS) @NonNull public static VibrationEffect.WaveformEnvelopeBuilder startWaveformEnvelope( @FloatRange(from = 0) float initialFrequencyHz) { return new WaveformEnvelopeBuilder(initialFrequencyHz); } /** * A builder for waveform effects described by its envelope. * Loading @@ -1882,7 +1836,7 @@ public abstract class VibrationEffect implements Parcelable { * 100ms, holds that state for 200ms, and then ramps back down over 100ms: * * <pre>{@code * VibrationEffect effect = VibrationEffect.startWaveformEnvelope() * VibrationEffect effect = new VibrationEffect.WaveformEnvelopeBuilder() * .addControlPoint(1.0f, 120f, 100) * .addControlPoint(1.0f, 120f, 200) * .addControlPoint(0.0f, 120f, 100) Loading Loading @@ -1916,20 +1870,48 @@ public abstract class VibrationEffect implements Parcelable { * {@link VibratorEnvelopeEffectInfo#getMaxControlPointDurationMillis()} * <li>Maximum total effect duration: {@link VibratorEnvelopeEffectInfo#getMaxDurationMillis()} * </ul> * * @see VibrationEffect#startWaveformEnvelope() */ @FlaggedApi(Flags.FLAG_NORMALIZED_PWLE_EFFECTS) public static final class WaveformEnvelopeBuilder { private ArrayList<PwleSegment> mSegments = new ArrayList<>(); private float mLastAmplitude = 0f; private float mLastFrequencyHz = 0f; private float mLastFrequencyHz = Float.NaN; private WaveformEnvelopeBuilder() {} public WaveformEnvelopeBuilder() {} private WaveformEnvelopeBuilder(float initialFrequency) { mLastFrequencyHz = initialFrequency; /** * Sets the initial frequency for the waveform in Hertz. * * <p>The effect will start vibrating at this frequency when it transitions to the * amplitude and frequency defined by the first control point. * * <p>The frequency must be greater than zero and within the supported range. To determine * the supported range, use {@link Vibrator#getFrequencyProfile()}. Creating * effects using frequencies outside this range will result in the vibration not playing. * * @param initialFrequencyHz The starting frequency of the vibration, in Hz. Must be * greater than zero. */ @FlaggedApi(Flags.FLAG_NORMALIZED_PWLE_EFFECTS) @SuppressWarnings("MissingGetterMatchingBuilder")// No getter to initial frequency once set. @NonNull public WaveformEnvelopeBuilder setInitialFrequencyHz( @FloatRange(from = 0) float initialFrequencyHz) { if (mSegments.isEmpty()) { mLastFrequencyHz = initialFrequencyHz; } else { PwleSegment firstSegment = mSegments.getFirst(); mSegments.set(0, new PwleSegment( firstSegment.getStartAmplitude(), firstSegment.getEndAmplitude(), initialFrequencyHz, // Update start frequency firstSegment.getEndFrequencyHz(), (int) firstSegment.getDuration())); } return this; } /** Loading @@ -1940,15 +1922,13 @@ public abstract class VibrationEffect implements Parcelable { * perceived intensity. It's determined by the actuator response curve. * * <p>Frequency must be greater than zero and within the supported range. To determine * the supported range, use {@link Vibrator#getFrequencyProfile()}. This method returns a * {@link android.os.vibrator.VibratorFrequencyProfile} object, which contains the * minimum and maximum frequencies, among other frequency-related information. Creating * the supported range, use {@link Vibrator#getFrequencyProfile()}. Creating * effects using frequencies outside this range will result in the vibration not playing. * * <p>Time specifies the duration (in milliseconds) for the vibrator to smoothly transition * from the previous control point to this new one. It must be greater than zero. To * transition as quickly as possible, use * {@link Vibrator#getMinEnvelopeEffectControlPointDurationMillis()}. * {@link VibratorEnvelopeEffectInfo#getMinControlPointDurationMillis()}. * * @param amplitude The amplitude value between 0 and 1, inclusive. 0 represents the * vibrator being off, and 1 represents the maximum achievable amplitude Loading @@ -1963,7 +1943,7 @@ public abstract class VibrationEffect implements Parcelable { @FloatRange(from = 0, to = 1) float amplitude, @FloatRange(from = 0) float frequencyHz, int timeMillis) { if (mLastFrequencyHz == 0) { if (Float.isNaN(mLastFrequencyHz)) { mLastFrequencyHz = frequencyHz; } Loading @@ -1984,6 +1964,7 @@ public abstract class VibrationEffect implements Parcelable { * calling this method again. * * @return The {@link VibrationEffect} resulting from the list of control points. * @throws IllegalStateException if no control points were added to the builder. */ @FlaggedApi(Flags.FLAG_NORMALIZED_PWLE_EFFECTS) @NonNull Loading core/java/android/os/vibrator/VibratorEnvelopeEffectInfo.java +2 −2 Original line number Diff line number Diff line Loading @@ -34,11 +34,11 @@ import java.util.Objects; * </ul> * * <p>This information can be used to help construct waveform envelope effects with * {@link VibrationEffect#startWaveformEnvelope()}. When designing these effects, it is also * {@link VibrationEffect.WaveformEnvelopeBuilder}. When designing these effects, it is also * recommended to check the {@link VibratorFrequencyProfile} for information about the supported * frequency range and the vibrator's output response. * * @see VibrationEffect#startWaveformEnvelope() * @see VibrationEffect.WaveformEnvelopeBuilder * @see VibratorFrequencyProfile */ @FlaggedApi(Flags.FLAG_NORMALIZED_PWLE_EFFECTS) Loading core/tests/vibrator/src/android/os/VibrationEffectTest.java +38 −23 Original line number Diff line number Diff line Loading @@ -414,10 +414,10 @@ public class VibrationEffectTest { @Test @EnableFlags(Flags.FLAG_NORMALIZED_PWLE_EFFECTS) public void computeLegacyPattern_effectsViaStartWaveformEnvelope() { // Effects created via startWaveformEnvelope are not expected to be converted to long[] public void computeLegacyPattern_effectsViaWaveformEnvelopeBuilder() { // Effects created via waveformEnvelopeBuilder are not expected to be converted to long[] // patterns, as they are not configured to always play with the default amplitude. VibrationEffect effect = VibrationEffect.startWaveformEnvelope() VibrationEffect effect = new VibrationEffect.WaveformEnvelopeBuilder() .addControlPoint(/*amplitude=*/ 0.0f, /*frequencyHz=*/ 60f, /*timeMillis=*/ 20) .addControlPoint(/*amplitude=*/ 0.3f, /*frequencyHz=*/ 100f, /*timeMillis=*/ 50) .addControlPoint(/*amplitude=*/ 0.4f, /*frequencyHz=*/ 120f, /*timeMillis=*/ 80) Loading @@ -426,7 +426,8 @@ public class VibrationEffectTest { assertNull(effect.computeCreateWaveformOffOnTimingsOrNull()); effect = VibrationEffect.startWaveformEnvelope(/*initialFrequency=*/ 60) effect = new VibrationEffect.WaveformEnvelopeBuilder() .setInitialFrequencyHz(/*initialFrequencyHz=*/ 60) .addControlPoint(/*amplitude=*/ 0.0f, /*frequencyHz=*/ 60f, /*timeMillis=*/ 20) .addControlPoint(/*amplitude=*/ 0.3f, /*frequencyHz=*/ 100f, /*timeMillis=*/ 50) .addControlPoint(/*amplitude=*/ 0.4f, /*frequencyHz=*/ 120f, /*timeMillis=*/ 80) Loading Loading @@ -644,7 +645,7 @@ public class VibrationEffectTest { @Test @EnableFlags(Flags.FLAG_NORMALIZED_PWLE_EFFECTS) public void testValidateWaveformEnvelopeBuilder() { VibrationEffect.startWaveformEnvelope() new VibrationEffect.WaveformEnvelopeBuilder() .addControlPoint(/*amplitude=*/ 0.0f, /*frequencyHz=*/ 60f, /*timeMillis=*/ 20) .addControlPoint(/*amplitude=*/ 0.3f, /*frequencyHz=*/ 100f, /*timeMillis=*/ 50) .addControlPoint(/*amplitude=*/ 0.4f, /*frequencyHz=*/ 120f, /*timeMillis=*/ 80) Loading @@ -652,7 +653,8 @@ public class VibrationEffectTest { .build() .validate(); VibrationEffect.startWaveformEnvelope(/*initialFrequency=*/ 30) new VibrationEffect.WaveformEnvelopeBuilder() .setInitialFrequencyHz(/*initialFrequencyHz=*/ 30) .addControlPoint(/*amplitude=*/ 0.0f, /*frequencyHz=*/ 60f, /*timeMillis=*/ 20) .addControlPoint(/*amplitude=*/ 0.3f, /*frequencyHz=*/ 100f, /*timeMillis=*/ 50) .addControlPoint(/*amplitude=*/ 0.4f, /*frequencyHz=*/ 120f, /*timeMillis=*/ 80) Loading @@ -661,13 +663,13 @@ public class VibrationEffectTest { .validate(); VibrationEffect.createRepeatingEffect( /*preamble=*/ VibrationEffect.startWaveformEnvelope() /*preamble=*/ new VibrationEffect.WaveformEnvelopeBuilder() .addControlPoint(/*amplitude=*/ 0.0f, /*frequencyHz=*/ 60f, /*timeMillis=*/ 20) .addControlPoint(/*amplitude=*/ 0.3f, /*frequencyHz=*/ 100f, /*timeMillis=*/ 50) .build(), /*repeatingEffect=*/ VibrationEffect.startWaveformEnvelope() /*repeatingEffect=*/ new VibrationEffect.WaveformEnvelopeBuilder() .addControlPoint(/*amplitude=*/ 0.0f, /*frequencyHz=*/ 60f, /*timeMillis=*/ 20) .addControlPoint(/*amplitude=*/ 0.5f, /*frequencyHz=*/ 150f, Loading @@ -676,7 +678,7 @@ public class VibrationEffectTest { ).validate(); VibrationEffect.createRepeatingEffect( /*effect=*/ VibrationEffect.startWaveformEnvelope() /*effect=*/ new VibrationEffect.WaveformEnvelopeBuilder() .addControlPoint(/*amplitude=*/ 0.0f, /*frequencyHz=*/ 60f, /*timeMillis=*/ 20) .addControlPoint(/*amplitude=*/ 0.5f, /*frequencyHz=*/ 150f, Loading @@ -685,59 +687,70 @@ public class VibrationEffectTest { ).validate(); assertThrows(IllegalStateException.class, () -> VibrationEffect.startWaveformEnvelope().build().validate()); () -> new VibrationEffect.WaveformEnvelopeBuilder().build().validate()); assertThrows(IllegalArgumentException.class, () -> VibrationEffect.startWaveformEnvelope() () -> new VibrationEffect.WaveformEnvelopeBuilder() .addControlPoint(/*amplitude=*/ -1.0f, /*frequencyHz=*/ 60f, /*timeMillis=*/ 20) .build() .validate()); assertThrows(IllegalArgumentException.class, () -> VibrationEffect.startWaveformEnvelope() () -> new VibrationEffect.WaveformEnvelopeBuilder() .addControlPoint(/*amplitude=*/ 1.1f, /*frequencyHz=*/ 60f, /*timeMillis=*/ 20) .build() .validate()); assertThrows(IllegalArgumentException.class, () -> VibrationEffect.startWaveformEnvelope() () -> new VibrationEffect.WaveformEnvelopeBuilder() .addControlPoint(/*amplitude=*/ 0.8f, /*frequencyHz=*/ 0f, /*timeMillis=*/ 20) .build() .validate()); assertThrows(IllegalArgumentException.class, () -> VibrationEffect.startWaveformEnvelope() () -> new VibrationEffect.WaveformEnvelopeBuilder() .addControlPoint(/*amplitude=*/ 0.8f, /*frequencyHz=*/ 100f, /*timeMillis=*/ 0) .build() .validate()); assertThrows(IllegalStateException.class, () -> VibrationEffect.startWaveformEnvelope(/*initialFrequency=*/30) () -> new VibrationEffect.WaveformEnvelopeBuilder() .setInitialFrequencyHz(/*initialFrequencyHz=*/ 30) .build().validate()); assertThrows(IllegalArgumentException.class, () -> VibrationEffect.startWaveformEnvelope(/*initialFrequency=*/30) () -> new VibrationEffect.WaveformEnvelopeBuilder() .setInitialFrequencyHz(/*initialFrequencyHz=*/ 30) .addControlPoint(/*amplitude=*/ -1.0f, /*frequencyHz=*/ 60f, /*timeMillis=*/ 20) .build() .validate()); assertThrows(IllegalArgumentException.class, () -> VibrationEffect.startWaveformEnvelope(/*initialFrequency=*/30) () -> new VibrationEffect.WaveformEnvelopeBuilder() .setInitialFrequencyHz(/*initialFrequencyHz=*/ 30) .addControlPoint(/*amplitude=*/ 1.1f, /*frequencyHz=*/ 60f, /*timeMillis=*/ 20) .build() .validate()); assertThrows(IllegalArgumentException.class, () -> VibrationEffect.startWaveformEnvelope(/*initialFrequency=*/30) () -> new VibrationEffect.WaveformEnvelopeBuilder() .setInitialFrequencyHz(/*initialFrequencyHz=*/ 30) .addControlPoint(/*amplitude=*/ 0.8f, /*frequencyHz=*/ 0f, /*timeMillis=*/ 20) .build() .validate()); assertThrows(IllegalArgumentException.class, () -> VibrationEffect.startWaveformEnvelope(/*initialFrequency=*/30) () -> new VibrationEffect.WaveformEnvelopeBuilder() .setInitialFrequencyHz(/*initialFrequencyHz=*/ 30) .addControlPoint(/*amplitude=*/ 0.8f, /*frequencyHz=*/ 100f, /*timeMillis=*/ 0) .build() .validate()); assertThrows(IllegalArgumentException.class, () -> new VibrationEffect.WaveformEnvelopeBuilder() .setInitialFrequencyHz(/*initialFrequencyHz=*/ 0) .addControlPoint(/*amplitude=*/ 0.8f, /*frequencyHz=*/ 100f, /*timeMillis=*/ 20) .build().validate()); } @Test Loading Loading @@ -1381,14 +1394,15 @@ public class VibrationEffectTest { @Test @EnableFlags(Flags.FLAG_NORMALIZED_PWLE_EFFECTS) public void testIsHapticFeedbackCandidate_longEnvelopeEffects_notCandidates() { assertFalse(VibrationEffect.startWaveformEnvelope() assertFalse(new VibrationEffect.WaveformEnvelopeBuilder() .addControlPoint(/*amplitude=*/ 0.0f, /*frequencyHz=*/ 60f, /*timeMillis=*/ 200) .addControlPoint(/*amplitude=*/ 0.3f, /*frequencyHz=*/ 100f, /*timeMillis=*/ 500) .addControlPoint(/*amplitude=*/ 0.4f, /*frequencyHz=*/ 120f, /*timeMillis=*/ 800) .addControlPoint(/*amplitude=*/ 0.0f, /*frequencyHz=*/ 120f, /*timeMillis=*/ 400) .build() .isHapticFeedbackCandidate()); assertFalse(VibrationEffect.startWaveformEnvelope(/*initialFrequency=*/ 40) assertFalse(new VibrationEffect.WaveformEnvelopeBuilder() .setInitialFrequencyHz(/*initialFrequencyHz=*/ 40) .addControlPoint(/*amplitude=*/ 0.0f, /*frequencyHz=*/ 60f, /*timeMillis=*/ 200) .addControlPoint(/*amplitude=*/ 0.3f, /*frequencyHz=*/ 100f, /*timeMillis=*/ 500) .addControlPoint(/*amplitude=*/ 0.4f, /*frequencyHz=*/ 120f, /*timeMillis=*/ 800) Loading @@ -1413,13 +1427,14 @@ public class VibrationEffectTest { @Test @EnableFlags(Flags.FLAG_NORMALIZED_PWLE_EFFECTS) public void testIsHapticFeedbackCandidate_shortEnvelopeEffects_areCandidates() { assertTrue(VibrationEffect.startWaveformEnvelope() assertTrue(new VibrationEffect.WaveformEnvelopeBuilder() .addControlPoint(/*amplitude=*/ 0.3f, /*frequencyHz=*/ 100f, /*timeMillis=*/ 500) .addControlPoint(/*amplitude=*/ 0.4f, /*frequencyHz=*/ 120f, /*timeMillis=*/ 400) .addControlPoint(/*amplitude=*/ 0.0f, /*frequencyHz=*/ 120f, /*timeMillis=*/ 100) .build() .isHapticFeedbackCandidate()); assertTrue(VibrationEffect.startWaveformEnvelope(/*initialFrequency=*/ 30) assertTrue(new VibrationEffect.WaveformEnvelopeBuilder() .setInitialFrequencyHz(/*initialFrequencyHz=*/ 30) .addControlPoint(/*amplitude=*/ 0.3f, /*frequencyHz=*/ 100f, /*timeMillis=*/ 500) .addControlPoint(/*amplitude=*/ 0.4f, /*frequencyHz=*/ 120f, /*timeMillis=*/ 400) .addControlPoint(/*amplitude=*/ 0.0f, /*frequencyHz=*/ 120f, /*timeMillis=*/ 100) Loading services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java +4 −2 Original line number Diff line number Diff line Loading @@ -904,7 +904,7 @@ public class VibrationThreadTest { fakeVibrator.setMaxEnvelopeEffectSize(10); fakeVibrator.setMinEnvelopeEffectControlPointDurationMillis(20); VibrationEffect effect = VibrationEffect.startWaveformEnvelope() VibrationEffect effect = new VibrationEffect.WaveformEnvelopeBuilder() .addControlPoint(/*amplitude=*/ 0.1f, /*frequencyHz=*/ 60f, /*timeMillis=*/ 20) .addControlPoint(/*amplitude=*/ 0.3f, /*frequencyHz=*/ 100f, /*timeMillis=*/ 30) .addControlPoint(/*amplitude=*/ 0.4f, /*frequencyHz=*/ 120f, /*timeMillis=*/ 20) Loading Loading @@ -939,12 +939,14 @@ public class VibrationThreadTest { fakeVibrator.setMaxEnvelopeEffectSize(10); fakeVibrator.setMinEnvelopeEffectControlPointDurationMillis(20); VibrationEffect effect = VibrationEffect.startWaveformEnvelope(/*initialFrequency=*/ 30) VibrationEffect effect = new VibrationEffect.WaveformEnvelopeBuilder() .setInitialFrequencyHz(/*initialFrequencyHz=*/ 30) .addControlPoint(/*amplitude=*/ 0.1f, /*frequencyHz=*/ 60f, /*timeMillis=*/ 20) .addControlPoint(/*amplitude=*/ 0.3f, /*frequencyHz=*/ 100f, /*timeMillis=*/ 30) .addControlPoint(/*amplitude=*/ 0.4f, /*frequencyHz=*/ 120f, /*timeMillis=*/ 20) .addControlPoint(/*amplitude=*/ 0.0f, /*frequencyHz=*/ 120f, /*timeMillis=*/ 30) .build(); HalVibration vibration = startThreadAndDispatcher(effect); waitForCompletion(); Loading Loading
core/api/current.txt +2 −2 Original line number Diff line number Diff line Loading @@ -34737,8 +34737,6 @@ package android.os { method public static android.os.VibrationEffect createWaveform(long[], int[], int); method public int describeContents(); method @NonNull public static android.os.VibrationEffect.Composition startComposition(); method @FlaggedApi("android.os.vibrator.normalized_pwle_effects") @NonNull public static android.os.VibrationEffect.WaveformEnvelopeBuilder startWaveformEnvelope(); method @FlaggedApi("android.os.vibrator.normalized_pwle_effects") @NonNull public static android.os.VibrationEffect.WaveformEnvelopeBuilder startWaveformEnvelope(@FloatRange(from=0) float); field @NonNull public static final android.os.Parcelable.Creator<android.os.VibrationEffect> CREATOR; field public static final int DEFAULT_AMPLITUDE = -1; // 0xffffffff field public static final int EFFECT_CLICK = 0; // 0x0 Loading Loading @@ -34766,8 +34764,10 @@ package android.os { } @FlaggedApi("android.os.vibrator.normalized_pwle_effects") public static final class VibrationEffect.WaveformEnvelopeBuilder { ctor public VibrationEffect.WaveformEnvelopeBuilder(); method @FlaggedApi("android.os.vibrator.normalized_pwle_effects") @NonNull public android.os.VibrationEffect.WaveformEnvelopeBuilder addControlPoint(@FloatRange(from=0, to=1) float, @FloatRange(from=0) float, int); method @FlaggedApi("android.os.vibrator.normalized_pwle_effects") @NonNull public android.os.VibrationEffect build(); method @FlaggedApi("android.os.vibrator.normalized_pwle_effects") @NonNull public android.os.VibrationEffect.WaveformEnvelopeBuilder setInitialFrequencyHz(@FloatRange(from=0) float); } public abstract class Vibrator {
core/java/android/os/VibrationEffect.java +39 −58 Original line number Diff line number Diff line Loading @@ -1825,52 +1825,6 @@ public abstract class VibrationEffect implements Parcelable { } } /** * Start building a waveform vibration. * * <p>The waveform envelope builder offers more flexibility for creating waveform effects, * allowing control over vibration amplitude and frequency via smooth transitions between * values. The waveform will start the first transition from the vibrator off state, using * the same frequency of the first control point. To provide a different initial vibration * frequency, use {@link #startWaveformEnvelope(float)}. * * <p>Note: To check whether waveform envelope effects are supported, use * {@link Vibrator#areEnvelopeEffectsSupported()}. * * @see VibrationEffect.WaveformEnvelopeBuilder */ @FlaggedApi(Flags.FLAG_NORMALIZED_PWLE_EFFECTS) @NonNull public static VibrationEffect.WaveformEnvelopeBuilder startWaveformEnvelope() { return new WaveformEnvelopeBuilder(); } /** * Start building a waveform vibration with an initial frequency. * * <p>The waveform envelope builder offers more flexibility for creating waveform effects, * allowing control over vibration amplitude and frequency via smooth transitions between * values. * * <p>This is the same as {@link #startWaveformEnvelope()}, but the waveform will start * vibrating at given frequency, in hertz, while it transitions to the new amplitude and * frequency of the first control point. * * <p>Note: To check whether waveform envelope effects are supported, use * {@link Vibrator#areEnvelopeEffectsSupported()}. * * @param initialFrequencyHz The starting frequency of the vibration, in hertz. Must be greater * than zero. * * @see VibrationEffect.WaveformEnvelopeBuilder */ @FlaggedApi(Flags.FLAG_NORMALIZED_PWLE_EFFECTS) @NonNull public static VibrationEffect.WaveformEnvelopeBuilder startWaveformEnvelope( @FloatRange(from = 0) float initialFrequencyHz) { return new WaveformEnvelopeBuilder(initialFrequencyHz); } /** * A builder for waveform effects described by its envelope. * Loading @@ -1882,7 +1836,7 @@ public abstract class VibrationEffect implements Parcelable { * 100ms, holds that state for 200ms, and then ramps back down over 100ms: * * <pre>{@code * VibrationEffect effect = VibrationEffect.startWaveformEnvelope() * VibrationEffect effect = new VibrationEffect.WaveformEnvelopeBuilder() * .addControlPoint(1.0f, 120f, 100) * .addControlPoint(1.0f, 120f, 200) * .addControlPoint(0.0f, 120f, 100) Loading Loading @@ -1916,20 +1870,48 @@ public abstract class VibrationEffect implements Parcelable { * {@link VibratorEnvelopeEffectInfo#getMaxControlPointDurationMillis()} * <li>Maximum total effect duration: {@link VibratorEnvelopeEffectInfo#getMaxDurationMillis()} * </ul> * * @see VibrationEffect#startWaveformEnvelope() */ @FlaggedApi(Flags.FLAG_NORMALIZED_PWLE_EFFECTS) public static final class WaveformEnvelopeBuilder { private ArrayList<PwleSegment> mSegments = new ArrayList<>(); private float mLastAmplitude = 0f; private float mLastFrequencyHz = 0f; private float mLastFrequencyHz = Float.NaN; private WaveformEnvelopeBuilder() {} public WaveformEnvelopeBuilder() {} private WaveformEnvelopeBuilder(float initialFrequency) { mLastFrequencyHz = initialFrequency; /** * Sets the initial frequency for the waveform in Hertz. * * <p>The effect will start vibrating at this frequency when it transitions to the * amplitude and frequency defined by the first control point. * * <p>The frequency must be greater than zero and within the supported range. To determine * the supported range, use {@link Vibrator#getFrequencyProfile()}. Creating * effects using frequencies outside this range will result in the vibration not playing. * * @param initialFrequencyHz The starting frequency of the vibration, in Hz. Must be * greater than zero. */ @FlaggedApi(Flags.FLAG_NORMALIZED_PWLE_EFFECTS) @SuppressWarnings("MissingGetterMatchingBuilder")// No getter to initial frequency once set. @NonNull public WaveformEnvelopeBuilder setInitialFrequencyHz( @FloatRange(from = 0) float initialFrequencyHz) { if (mSegments.isEmpty()) { mLastFrequencyHz = initialFrequencyHz; } else { PwleSegment firstSegment = mSegments.getFirst(); mSegments.set(0, new PwleSegment( firstSegment.getStartAmplitude(), firstSegment.getEndAmplitude(), initialFrequencyHz, // Update start frequency firstSegment.getEndFrequencyHz(), (int) firstSegment.getDuration())); } return this; } /** Loading @@ -1940,15 +1922,13 @@ public abstract class VibrationEffect implements Parcelable { * perceived intensity. It's determined by the actuator response curve. * * <p>Frequency must be greater than zero and within the supported range. To determine * the supported range, use {@link Vibrator#getFrequencyProfile()}. This method returns a * {@link android.os.vibrator.VibratorFrequencyProfile} object, which contains the * minimum and maximum frequencies, among other frequency-related information. Creating * the supported range, use {@link Vibrator#getFrequencyProfile()}. Creating * effects using frequencies outside this range will result in the vibration not playing. * * <p>Time specifies the duration (in milliseconds) for the vibrator to smoothly transition * from the previous control point to this new one. It must be greater than zero. To * transition as quickly as possible, use * {@link Vibrator#getMinEnvelopeEffectControlPointDurationMillis()}. * {@link VibratorEnvelopeEffectInfo#getMinControlPointDurationMillis()}. * * @param amplitude The amplitude value between 0 and 1, inclusive. 0 represents the * vibrator being off, and 1 represents the maximum achievable amplitude Loading @@ -1963,7 +1943,7 @@ public abstract class VibrationEffect implements Parcelable { @FloatRange(from = 0, to = 1) float amplitude, @FloatRange(from = 0) float frequencyHz, int timeMillis) { if (mLastFrequencyHz == 0) { if (Float.isNaN(mLastFrequencyHz)) { mLastFrequencyHz = frequencyHz; } Loading @@ -1984,6 +1964,7 @@ public abstract class VibrationEffect implements Parcelable { * calling this method again. * * @return The {@link VibrationEffect} resulting from the list of control points. * @throws IllegalStateException if no control points were added to the builder. */ @FlaggedApi(Flags.FLAG_NORMALIZED_PWLE_EFFECTS) @NonNull Loading
core/java/android/os/vibrator/VibratorEnvelopeEffectInfo.java +2 −2 Original line number Diff line number Diff line Loading @@ -34,11 +34,11 @@ import java.util.Objects; * </ul> * * <p>This information can be used to help construct waveform envelope effects with * {@link VibrationEffect#startWaveformEnvelope()}. When designing these effects, it is also * {@link VibrationEffect.WaveformEnvelopeBuilder}. When designing these effects, it is also * recommended to check the {@link VibratorFrequencyProfile} for information about the supported * frequency range and the vibrator's output response. * * @see VibrationEffect#startWaveformEnvelope() * @see VibrationEffect.WaveformEnvelopeBuilder * @see VibratorFrequencyProfile */ @FlaggedApi(Flags.FLAG_NORMALIZED_PWLE_EFFECTS) Loading
core/tests/vibrator/src/android/os/VibrationEffectTest.java +38 −23 Original line number Diff line number Diff line Loading @@ -414,10 +414,10 @@ public class VibrationEffectTest { @Test @EnableFlags(Flags.FLAG_NORMALIZED_PWLE_EFFECTS) public void computeLegacyPattern_effectsViaStartWaveformEnvelope() { // Effects created via startWaveformEnvelope are not expected to be converted to long[] public void computeLegacyPattern_effectsViaWaveformEnvelopeBuilder() { // Effects created via waveformEnvelopeBuilder are not expected to be converted to long[] // patterns, as they are not configured to always play with the default amplitude. VibrationEffect effect = VibrationEffect.startWaveformEnvelope() VibrationEffect effect = new VibrationEffect.WaveformEnvelopeBuilder() .addControlPoint(/*amplitude=*/ 0.0f, /*frequencyHz=*/ 60f, /*timeMillis=*/ 20) .addControlPoint(/*amplitude=*/ 0.3f, /*frequencyHz=*/ 100f, /*timeMillis=*/ 50) .addControlPoint(/*amplitude=*/ 0.4f, /*frequencyHz=*/ 120f, /*timeMillis=*/ 80) Loading @@ -426,7 +426,8 @@ public class VibrationEffectTest { assertNull(effect.computeCreateWaveformOffOnTimingsOrNull()); effect = VibrationEffect.startWaveformEnvelope(/*initialFrequency=*/ 60) effect = new VibrationEffect.WaveformEnvelopeBuilder() .setInitialFrequencyHz(/*initialFrequencyHz=*/ 60) .addControlPoint(/*amplitude=*/ 0.0f, /*frequencyHz=*/ 60f, /*timeMillis=*/ 20) .addControlPoint(/*amplitude=*/ 0.3f, /*frequencyHz=*/ 100f, /*timeMillis=*/ 50) .addControlPoint(/*amplitude=*/ 0.4f, /*frequencyHz=*/ 120f, /*timeMillis=*/ 80) Loading Loading @@ -644,7 +645,7 @@ public class VibrationEffectTest { @Test @EnableFlags(Flags.FLAG_NORMALIZED_PWLE_EFFECTS) public void testValidateWaveformEnvelopeBuilder() { VibrationEffect.startWaveformEnvelope() new VibrationEffect.WaveformEnvelopeBuilder() .addControlPoint(/*amplitude=*/ 0.0f, /*frequencyHz=*/ 60f, /*timeMillis=*/ 20) .addControlPoint(/*amplitude=*/ 0.3f, /*frequencyHz=*/ 100f, /*timeMillis=*/ 50) .addControlPoint(/*amplitude=*/ 0.4f, /*frequencyHz=*/ 120f, /*timeMillis=*/ 80) Loading @@ -652,7 +653,8 @@ public class VibrationEffectTest { .build() .validate(); VibrationEffect.startWaveformEnvelope(/*initialFrequency=*/ 30) new VibrationEffect.WaveformEnvelopeBuilder() .setInitialFrequencyHz(/*initialFrequencyHz=*/ 30) .addControlPoint(/*amplitude=*/ 0.0f, /*frequencyHz=*/ 60f, /*timeMillis=*/ 20) .addControlPoint(/*amplitude=*/ 0.3f, /*frequencyHz=*/ 100f, /*timeMillis=*/ 50) .addControlPoint(/*amplitude=*/ 0.4f, /*frequencyHz=*/ 120f, /*timeMillis=*/ 80) Loading @@ -661,13 +663,13 @@ public class VibrationEffectTest { .validate(); VibrationEffect.createRepeatingEffect( /*preamble=*/ VibrationEffect.startWaveformEnvelope() /*preamble=*/ new VibrationEffect.WaveformEnvelopeBuilder() .addControlPoint(/*amplitude=*/ 0.0f, /*frequencyHz=*/ 60f, /*timeMillis=*/ 20) .addControlPoint(/*amplitude=*/ 0.3f, /*frequencyHz=*/ 100f, /*timeMillis=*/ 50) .build(), /*repeatingEffect=*/ VibrationEffect.startWaveformEnvelope() /*repeatingEffect=*/ new VibrationEffect.WaveformEnvelopeBuilder() .addControlPoint(/*amplitude=*/ 0.0f, /*frequencyHz=*/ 60f, /*timeMillis=*/ 20) .addControlPoint(/*amplitude=*/ 0.5f, /*frequencyHz=*/ 150f, Loading @@ -676,7 +678,7 @@ public class VibrationEffectTest { ).validate(); VibrationEffect.createRepeatingEffect( /*effect=*/ VibrationEffect.startWaveformEnvelope() /*effect=*/ new VibrationEffect.WaveformEnvelopeBuilder() .addControlPoint(/*amplitude=*/ 0.0f, /*frequencyHz=*/ 60f, /*timeMillis=*/ 20) .addControlPoint(/*amplitude=*/ 0.5f, /*frequencyHz=*/ 150f, Loading @@ -685,59 +687,70 @@ public class VibrationEffectTest { ).validate(); assertThrows(IllegalStateException.class, () -> VibrationEffect.startWaveformEnvelope().build().validate()); () -> new VibrationEffect.WaveformEnvelopeBuilder().build().validate()); assertThrows(IllegalArgumentException.class, () -> VibrationEffect.startWaveformEnvelope() () -> new VibrationEffect.WaveformEnvelopeBuilder() .addControlPoint(/*amplitude=*/ -1.0f, /*frequencyHz=*/ 60f, /*timeMillis=*/ 20) .build() .validate()); assertThrows(IllegalArgumentException.class, () -> VibrationEffect.startWaveformEnvelope() () -> new VibrationEffect.WaveformEnvelopeBuilder() .addControlPoint(/*amplitude=*/ 1.1f, /*frequencyHz=*/ 60f, /*timeMillis=*/ 20) .build() .validate()); assertThrows(IllegalArgumentException.class, () -> VibrationEffect.startWaveformEnvelope() () -> new VibrationEffect.WaveformEnvelopeBuilder() .addControlPoint(/*amplitude=*/ 0.8f, /*frequencyHz=*/ 0f, /*timeMillis=*/ 20) .build() .validate()); assertThrows(IllegalArgumentException.class, () -> VibrationEffect.startWaveformEnvelope() () -> new VibrationEffect.WaveformEnvelopeBuilder() .addControlPoint(/*amplitude=*/ 0.8f, /*frequencyHz=*/ 100f, /*timeMillis=*/ 0) .build() .validate()); assertThrows(IllegalStateException.class, () -> VibrationEffect.startWaveformEnvelope(/*initialFrequency=*/30) () -> new VibrationEffect.WaveformEnvelopeBuilder() .setInitialFrequencyHz(/*initialFrequencyHz=*/ 30) .build().validate()); assertThrows(IllegalArgumentException.class, () -> VibrationEffect.startWaveformEnvelope(/*initialFrequency=*/30) () -> new VibrationEffect.WaveformEnvelopeBuilder() .setInitialFrequencyHz(/*initialFrequencyHz=*/ 30) .addControlPoint(/*amplitude=*/ -1.0f, /*frequencyHz=*/ 60f, /*timeMillis=*/ 20) .build() .validate()); assertThrows(IllegalArgumentException.class, () -> VibrationEffect.startWaveformEnvelope(/*initialFrequency=*/30) () -> new VibrationEffect.WaveformEnvelopeBuilder() .setInitialFrequencyHz(/*initialFrequencyHz=*/ 30) .addControlPoint(/*amplitude=*/ 1.1f, /*frequencyHz=*/ 60f, /*timeMillis=*/ 20) .build() .validate()); assertThrows(IllegalArgumentException.class, () -> VibrationEffect.startWaveformEnvelope(/*initialFrequency=*/30) () -> new VibrationEffect.WaveformEnvelopeBuilder() .setInitialFrequencyHz(/*initialFrequencyHz=*/ 30) .addControlPoint(/*amplitude=*/ 0.8f, /*frequencyHz=*/ 0f, /*timeMillis=*/ 20) .build() .validate()); assertThrows(IllegalArgumentException.class, () -> VibrationEffect.startWaveformEnvelope(/*initialFrequency=*/30) () -> new VibrationEffect.WaveformEnvelopeBuilder() .setInitialFrequencyHz(/*initialFrequencyHz=*/ 30) .addControlPoint(/*amplitude=*/ 0.8f, /*frequencyHz=*/ 100f, /*timeMillis=*/ 0) .build() .validate()); assertThrows(IllegalArgumentException.class, () -> new VibrationEffect.WaveformEnvelopeBuilder() .setInitialFrequencyHz(/*initialFrequencyHz=*/ 0) .addControlPoint(/*amplitude=*/ 0.8f, /*frequencyHz=*/ 100f, /*timeMillis=*/ 20) .build().validate()); } @Test Loading Loading @@ -1381,14 +1394,15 @@ public class VibrationEffectTest { @Test @EnableFlags(Flags.FLAG_NORMALIZED_PWLE_EFFECTS) public void testIsHapticFeedbackCandidate_longEnvelopeEffects_notCandidates() { assertFalse(VibrationEffect.startWaveformEnvelope() assertFalse(new VibrationEffect.WaveformEnvelopeBuilder() .addControlPoint(/*amplitude=*/ 0.0f, /*frequencyHz=*/ 60f, /*timeMillis=*/ 200) .addControlPoint(/*amplitude=*/ 0.3f, /*frequencyHz=*/ 100f, /*timeMillis=*/ 500) .addControlPoint(/*amplitude=*/ 0.4f, /*frequencyHz=*/ 120f, /*timeMillis=*/ 800) .addControlPoint(/*amplitude=*/ 0.0f, /*frequencyHz=*/ 120f, /*timeMillis=*/ 400) .build() .isHapticFeedbackCandidate()); assertFalse(VibrationEffect.startWaveformEnvelope(/*initialFrequency=*/ 40) assertFalse(new VibrationEffect.WaveformEnvelopeBuilder() .setInitialFrequencyHz(/*initialFrequencyHz=*/ 40) .addControlPoint(/*amplitude=*/ 0.0f, /*frequencyHz=*/ 60f, /*timeMillis=*/ 200) .addControlPoint(/*amplitude=*/ 0.3f, /*frequencyHz=*/ 100f, /*timeMillis=*/ 500) .addControlPoint(/*amplitude=*/ 0.4f, /*frequencyHz=*/ 120f, /*timeMillis=*/ 800) Loading @@ -1413,13 +1427,14 @@ public class VibrationEffectTest { @Test @EnableFlags(Flags.FLAG_NORMALIZED_PWLE_EFFECTS) public void testIsHapticFeedbackCandidate_shortEnvelopeEffects_areCandidates() { assertTrue(VibrationEffect.startWaveformEnvelope() assertTrue(new VibrationEffect.WaveformEnvelopeBuilder() .addControlPoint(/*amplitude=*/ 0.3f, /*frequencyHz=*/ 100f, /*timeMillis=*/ 500) .addControlPoint(/*amplitude=*/ 0.4f, /*frequencyHz=*/ 120f, /*timeMillis=*/ 400) .addControlPoint(/*amplitude=*/ 0.0f, /*frequencyHz=*/ 120f, /*timeMillis=*/ 100) .build() .isHapticFeedbackCandidate()); assertTrue(VibrationEffect.startWaveformEnvelope(/*initialFrequency=*/ 30) assertTrue(new VibrationEffect.WaveformEnvelopeBuilder() .setInitialFrequencyHz(/*initialFrequencyHz=*/ 30) .addControlPoint(/*amplitude=*/ 0.3f, /*frequencyHz=*/ 100f, /*timeMillis=*/ 500) .addControlPoint(/*amplitude=*/ 0.4f, /*frequencyHz=*/ 120f, /*timeMillis=*/ 400) .addControlPoint(/*amplitude=*/ 0.0f, /*frequencyHz=*/ 120f, /*timeMillis=*/ 100) Loading
services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java +4 −2 Original line number Diff line number Diff line Loading @@ -904,7 +904,7 @@ public class VibrationThreadTest { fakeVibrator.setMaxEnvelopeEffectSize(10); fakeVibrator.setMinEnvelopeEffectControlPointDurationMillis(20); VibrationEffect effect = VibrationEffect.startWaveformEnvelope() VibrationEffect effect = new VibrationEffect.WaveformEnvelopeBuilder() .addControlPoint(/*amplitude=*/ 0.1f, /*frequencyHz=*/ 60f, /*timeMillis=*/ 20) .addControlPoint(/*amplitude=*/ 0.3f, /*frequencyHz=*/ 100f, /*timeMillis=*/ 30) .addControlPoint(/*amplitude=*/ 0.4f, /*frequencyHz=*/ 120f, /*timeMillis=*/ 20) Loading Loading @@ -939,12 +939,14 @@ public class VibrationThreadTest { fakeVibrator.setMaxEnvelopeEffectSize(10); fakeVibrator.setMinEnvelopeEffectControlPointDurationMillis(20); VibrationEffect effect = VibrationEffect.startWaveformEnvelope(/*initialFrequency=*/ 30) VibrationEffect effect = new VibrationEffect.WaveformEnvelopeBuilder() .setInitialFrequencyHz(/*initialFrequencyHz=*/ 30) .addControlPoint(/*amplitude=*/ 0.1f, /*frequencyHz=*/ 60f, /*timeMillis=*/ 20) .addControlPoint(/*amplitude=*/ 0.3f, /*frequencyHz=*/ 100f, /*timeMillis=*/ 30) .addControlPoint(/*amplitude=*/ 0.4f, /*frequencyHz=*/ 120f, /*timeMillis=*/ 20) .addControlPoint(/*amplitude=*/ 0.0f, /*frequencyHz=*/ 120f, /*timeMillis=*/ 30) .build(); HalVibration vibration = startThreadAndDispatcher(effect); waitForCompletion(); Loading