Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3c3dcac4 authored by Lais Andrade's avatar Lais Andrade
Browse files

Make VibrationEffect.WaveformBuilder public

Update WaveformBuilder to have a single method that adds transitions to
new sets of vibration attributes for a fixed duration. The parameters
are represented by a new type VibrationEffect.VibrationParameter, and
have two concrete types now: amplitude and frequency.

Update VibrationEffect.Composition API to add effects and repeating
effects, removing the repeat index parameter from WaveformBuilder.

Remove delay parameter from Composition in favor of a new method
addOffPeriod to make it less ambiguous when the delay is played in the
composition.

Bug: 203785430
Test: VibrationEffectTest
Change-Id: I40cbfaff2de3231378da7db6a2bc5c9252e6d8f8
parent 136ca7e3
Loading
Loading
Loading
Loading
+21 −0
Original line number Diff line number Diff line
@@ -32926,6 +32926,9 @@ 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 @NonNull public static android.os.VibrationEffect.WaveformBuilder startWaveform();
    method @NonNull public static android.os.VibrationEffect.WaveformBuilder startWaveform(@NonNull android.os.VibrationEffect.VibrationParameter);
    method @NonNull public static android.os.VibrationEffect.WaveformBuilder startWaveform(@NonNull android.os.VibrationEffect.VibrationParameter, @NonNull android.os.VibrationEffect.VibrationParameter);
    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
@@ -32935,10 +32938,13 @@ package android.os {
  }
  public static final class VibrationEffect.Composition {
    method @NonNull public android.os.VibrationEffect.Composition addEffect(@NonNull android.os.VibrationEffect);
    method @NonNull public android.os.VibrationEffect.Composition addOffDuration(@NonNull java.time.Duration);
    method @NonNull public android.os.VibrationEffect.Composition addPrimitive(int);
    method @NonNull public android.os.VibrationEffect.Composition addPrimitive(int, @FloatRange(from=0.0f, to=1.0f) float);
    method @NonNull public android.os.VibrationEffect.Composition addPrimitive(int, @FloatRange(from=0.0f, to=1.0f) float, @IntRange(from=0) int);
    method @NonNull public android.os.VibrationEffect compose();
    method @NonNull public android.os.VibrationEffect.Composition repeatEffectIndefinitely(@NonNull android.os.VibrationEffect);
    field public static final int PRIMITIVE_CLICK = 1; // 0x1
    field public static final int PRIMITIVE_LOW_TICK = 8; // 0x8
    field public static final int PRIMITIVE_QUICK_FALL = 6; // 0x6
@@ -32949,6 +32955,21 @@ package android.os {
    field public static final int PRIMITIVE_TICK = 7; // 0x7
  }
  public static final class VibrationEffect.Composition.UnreachableAfterRepeatingIndefinitelyException extends java.lang.IllegalStateException {
  }
  public static class VibrationEffect.VibrationParameter {
    method @NonNull public static android.os.VibrationEffect.VibrationParameter targetAmplitude(@FloatRange(from=0, to=1) float);
    method @NonNull public static android.os.VibrationEffect.VibrationParameter targetFrequency(@FloatRange(from=1) float);
  }
  public static final class VibrationEffect.WaveformBuilder {
    method @NonNull public android.os.VibrationEffect.WaveformBuilder addSustain(@NonNull java.time.Duration);
    method @NonNull public android.os.VibrationEffect.WaveformBuilder addTransition(@NonNull java.time.Duration, @NonNull android.os.VibrationEffect.VibrationParameter);
    method @NonNull public android.os.VibrationEffect.WaveformBuilder addTransition(@NonNull java.time.Duration, @NonNull android.os.VibrationEffect.VibrationParameter, @NonNull android.os.VibrationEffect.VibrationParameter);
    method @NonNull public android.os.VibrationEffect build();
  }
  public abstract class Vibrator {
    method public final int areAllEffectsSupported(@NonNull int...);
    method public final boolean areAllPrimitivesSupported(@NonNull int...);
+0 −15
Original line number Diff line number Diff line
@@ -1786,7 +1786,6 @@ package android.os {
    method public static android.os.VibrationEffect get(int, boolean);
    method @Nullable public static android.os.VibrationEffect get(android.net.Uri, android.content.Context);
    method public abstract long getDuration();
    method @NonNull public static android.os.VibrationEffect.WaveformBuilder startWaveform();
    field public static final int EFFECT_POP = 4; // 0x4
    field public static final int EFFECT_STRENGTH_LIGHT = 0; // 0x0
    field public static final int EFFECT_STRENGTH_MEDIUM = 1; // 0x1
@@ -1804,20 +1803,6 @@ package android.os {
    field @NonNull public static final android.os.Parcelable.Creator<android.os.VibrationEffect.Composed> CREATOR;
  }

  public static final class VibrationEffect.Composition {
    method @NonNull public android.os.VibrationEffect.Composition addEffect(@NonNull android.os.VibrationEffect);
    method @NonNull public android.os.VibrationEffect.Composition addEffect(@NonNull android.os.VibrationEffect, @IntRange(from=0) int);
  }

  public static final class VibrationEffect.WaveformBuilder {
    method @NonNull public android.os.VibrationEffect.WaveformBuilder addRamp(@FloatRange(from=0.0f, to=1.0f) float, @IntRange(from=0) int);
    method @NonNull public android.os.VibrationEffect.WaveformBuilder addRamp(@FloatRange(from=0.0f, to=1.0f) float, @FloatRange(from=1.0f) float, @IntRange(from=0) int);
    method @NonNull public android.os.VibrationEffect.WaveformBuilder addStep(@FloatRange(from=0.0f, to=1.0f) float, @IntRange(from=0) int);
    method @NonNull public android.os.VibrationEffect.WaveformBuilder addStep(@FloatRange(from=0.0f, to=1.0f) float, @FloatRange(from=1.0f) float, @IntRange(from=0) int);
    method @NonNull public android.os.VibrationEffect build();
    method @NonNull public android.os.VibrationEffect build(int);
  }

  public abstract class Vibrator {
    method public int getDefaultVibrationIntensity(int);
    field public static final int VIBRATION_INTENSITY_HIGH = 3; // 0x3
+330 −151

File changed.

Preview size limit exceeded, changes collapsed.

+63 −37
Original line number Diff line number Diff line
@@ -16,6 +16,9 @@

package android.os;

import static android.os.VibrationEffect.VibrationParameter.targetAmplitude;
import static android.os.VibrationEffect.VibrationParameter.targetFrequency;

import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNotNull;
@@ -32,6 +35,7 @@ import android.content.ContentResolver;
import android.content.Context;
import android.content.res.Resources;
import android.net.Uri;
import android.os.VibrationEffect.Composition.UnreachableAfterRepeatingIndefinitelyException;
import android.os.vibrator.PrebakedSegment;
import android.os.vibrator.PrimitiveSegment;
import android.os.vibrator.StepSegment;
@@ -43,6 +47,8 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.junit.MockitoJUnitRunner;

import java.time.Duration;

@Presubmit
@RunWith(MockitoJUnitRunner.class)
public class VibrationEffectTest {
@@ -122,16 +128,7 @@ public class VibrationEffectTest {
        VibrationEffect.createWaveform(TEST_TIMINGS, TEST_AMPLITUDES, -1).validate();
        VibrationEffect.createWaveform(new long[]{10, 10}, new int[] {0, 0}, -1).validate();
        VibrationEffect.createWaveform(TEST_TIMINGS, TEST_AMPLITUDES, 0).validate();
        VibrationEffect.startWaveform()
                .addStep(/* amplitude= */ 1, /* duration= */ 10)
                .addRamp(/* amplitude= */ 0, /* duration= */ 20)
                .addStep(/* amplitude= */ 1, /* frequencyHz= */ 1, /* duration= */ 100)
                .addRamp(/* amplitude= */ 0.5f, /* frequencyHz= */ 100, /* duration= */ 50)
                .build()
                .validate();

        assertThrows(IllegalStateException.class,
                () -> VibrationEffect.startWaveform().build().validate());
        assertThrows(IllegalArgumentException.class,
                () -> VibrationEffect.createWaveform(new long[0], new int[0], -1).validate());
        assertThrows(IllegalArgumentException.class,
@@ -145,27 +142,31 @@ public class VibrationEffectTest {
        assertThrows(IllegalArgumentException.class,
                () -> VibrationEffect.createWaveform(
                        TEST_TIMINGS, TEST_AMPLITUDES, TEST_TIMINGS.length).validate());
    }

    @Test
    public void testValidateWaveformBuilder() {
        VibrationEffect.startWaveform(targetAmplitude(1))
                .addTransition(Duration.ofSeconds(1), targetAmplitude(0.5f), targetFrequency(100))
                .addTransition(Duration.ZERO, targetAmplitude(0f), targetFrequency(200))
                .addSustain(Duration.ofMinutes(2))
                .addTransition(Duration.ofMillis(10), targetAmplitude(1f), targetFrequency(50))
                .addSustain(Duration.ofMillis(1))
                .addTransition(Duration.ZERO, targetFrequency(150))
                .addSustain(Duration.ofMillis(2))
                .addTransition(Duration.ofSeconds(15), targetAmplitude(1))
                .build()
                .validate();

        assertThrows(IllegalStateException.class,
                () -> VibrationEffect.startWaveform().build().validate());
        assertThrows(IllegalArgumentException.class, () -> targetAmplitude(-2));
        assertThrows(IllegalArgumentException.class, () -> targetFrequency(0));
        assertThrows(IllegalArgumentException.class,
                () -> VibrationEffect.startWaveform()
                        .addStep(/* amplitude= */ -2, 10).build().validate());
        assertThrows(IllegalArgumentException.class,
                () -> VibrationEffect.startWaveform()
                        .addStep(1, /* frequencyHz= */ -1f, 10).build().validate());
        assertThrows(IllegalArgumentException.class,
                () -> VibrationEffect.startWaveform()
                        .addStep(1, /* duration= */ -1).build().validate());
        assertThrows(IllegalArgumentException.class,
                () -> VibrationEffect.startWaveform()
                        .addStep(1, 100f, /* duration= */ -1).build().validate());
        assertThrows(IllegalArgumentException.class,
                () -> VibrationEffect.startWaveform()
                        .addRamp(/* amplitude= */ -3, 10).build().validate());
        assertThrows(IllegalArgumentException.class,
                () -> VibrationEffect.startWaveform()
                        .addRamp(1, /* frequencyHz= */ 0, 10).build().validate());
                () -> VibrationEffect.startWaveform().addTransition(
                        Duration.ofMillis(-10), targetAmplitude(1)).build().validate());
        assertThrows(IllegalArgumentException.class,
                () -> VibrationEffect.startWaveform()
                        .addRamp(1, 10f, /* duration= */ -3).build().validate());
                () -> VibrationEffect.startWaveform().addSustain(Duration.ZERO).build().validate());
    }

    @Test
@@ -174,14 +175,24 @@ public class VibrationEffectTest {
                .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
                .addEffect(TEST_ONE_SHOT)
                .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f)
                .addEffect(TEST_WAVEFORM, 100)
                .addOffDuration(Duration.ofMillis(100))
                .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 0.5f, 10)
                .addEffect(VibrationEffect.get(VibrationEffect.EFFECT_CLICK))
                .compose()
                .validate();

        VibrationEffect.startComposition()
                .repeatEffectIndefinitely(TEST_ONE_SHOT)
                .compose()
                .validate();

        assertThrows(IllegalStateException.class,
                () -> VibrationEffect.startComposition().compose().validate());
        assertThrows(IllegalStateException.class,
                () -> VibrationEffect.startComposition()
                        .addOffDuration(Duration.ofSeconds(0))
                        .compose()
                        .validate());
        assertThrows(IllegalArgumentException.class,
                () -> VibrationEffect.startComposition().addPrimitive(-1).compose().validate());
        assertThrows(IllegalArgumentException.class,
@@ -196,12 +207,27 @@ public class VibrationEffectTest {
                        .validate());
        assertThrows(IllegalArgumentException.class,
                () -> VibrationEffect.startComposition()
                        .addEffect(TEST_ONE_SHOT, /* delay= */ -10)
                        .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f, -1)
                        .compose()
                        .validate());
        assertThrows(IllegalArgumentException.class,
                () -> VibrationEffect.startComposition()
                        .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f, -1)
                        .repeatEffectIndefinitely(
                                // Repeating waveform.
                                VibrationEffect.createWaveform(
                                        new long[] { 10 }, new int[] { 100}, 0))
                        .compose()
                        .validate());
        assertThrows(UnreachableAfterRepeatingIndefinitelyException.class,
                () -> VibrationEffect.startComposition()
                        .repeatEffectIndefinitely(TEST_WAVEFORM)
                        .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
                        .compose()
                        .validate());
        assertThrows(UnreachableAfterRepeatingIndefinitelyException.class,
                () -> VibrationEffect.startComposition()
                        .repeatEffectIndefinitely(TEST_WAVEFORM)
                        .addEffect(TEST_ONE_SHOT)
                        .compose()
                        .validate());
    }
@@ -354,9 +380,9 @@ public class VibrationEffectTest {
        assertFalse(VibrationEffect.createWaveform(
                new long[]{200, 200, 700}, new int[]{1, 2, 3}, -1).isHapticFeedbackCandidate());
        assertFalse(VibrationEffect.startWaveform()
                .addRamp(1, 500)
                .addStep(1, 200)
                .addRamp(0, 500)
                .addTransition(Duration.ofMillis(500), targetAmplitude(1))
                .addTransition(Duration.ofMillis(200), targetAmplitude(0.5f))
                .addTransition(Duration.ofMillis(500), targetAmplitude(0))
                .build()
                .isHapticFeedbackCandidate());
    }
@@ -367,9 +393,9 @@ public class VibrationEffectTest {
        assertTrue(VibrationEffect.createWaveform(
                new long[]{100, 200, 300}, new int[]{1, 2, 3}, -1).isHapticFeedbackCandidate());
        assertTrue(VibrationEffect.startWaveform()
                .addRamp(1, 300)
                .addStep(1, 200)
                .addRamp(0, 300)
                .addTransition(Duration.ofMillis(300), targetAmplitude(1))
                .addTransition(Duration.ofMillis(200), targetAmplitude(0.5f))
                .addTransition(Duration.ofMillis(300), targetAmplitude(0))
                .build()
                .isHapticFeedbackCandidate());
    }
+12 −8
Original line number Diff line number Diff line
@@ -16,6 +16,9 @@

package com.android.server.notification;

import static android.os.VibrationEffect.VibrationParameter.targetAmplitude;
import static android.os.VibrationEffect.VibrationParameter.targetFrequency;

import android.annotation.Nullable;
import android.content.Context;
import android.content.res.Resources;
@@ -30,6 +33,7 @@ import android.util.Slog;
import com.android.internal.R;
import com.android.server.pm.PackageManagerService;

import java.time.Duration;
import java.util.Arrays;

/**
@@ -89,8 +93,7 @@ public final class VibratorHelper {
     * Safely create a {@link VibrationEffect} from given waveform description.
     *
     * <p>The waveform is described by a sequence of values for target amplitude, frequency and
     * duration, that are forwarded to
     * {@link VibrationEffect.WaveformBuilder#addRamp(float, float, int)}.
     * duration, that are forwarded to {@link VibrationEffect.WaveformBuilder#addTransition}.
     *
     * <p>This method returns {@code null} if the pattern is also {@code null} or invalid.
     *
@@ -114,16 +117,17 @@ public final class VibratorHelper {

            VibrationEffect.WaveformBuilder waveformBuilder = VibrationEffect.startWaveform();
            for (int i = 0; i < length; i += 3) {
                waveformBuilder.addRamp(
                        /* amplitude= */ values[i],
                        /* frequencyHz= */ values[i + 1],
                        /* duration= */ (int) values[i + 2]);
                waveformBuilder.addTransition(Duration.ofMillis((int) values[i + 2]),
                        targetAmplitude(values[i]), targetFrequency(values[i + 1]));
            }

            VibrationEffect effect = waveformBuilder.build();
            if (insistent) {
                return waveformBuilder.build(/* repeat= */ 0);
                return VibrationEffect.startComposition()
                        .repeatEffectIndefinitely(effect)
                        .compose();
            }
            return waveformBuilder.build();
            return effect;
        } catch (IllegalArgumentException e) {
            Slog.e(TAG, "Error creating vibration PWLE waveform with pattern: "
                    + Arrays.toString(values));
Loading