Introduce WaveformBuilder to VibrationEffect
Create a new parcelable RampSegment and introduce VibrationEffect.startWaveform() method that returns a WaveformBuilder that allows creating step/ramp waveforms that changes amplitude and frequency of the effect. Implementations of VibrationThread and InputManagerService for now just ignore the new segments and keep existing behavior. Bug: 167947076 Test: VibrationEffectTest Change-Id: Ibac895b63196e779457efc297f1f5497a7814ae9
This commit is contained in:
@@ -1665,6 +1665,7 @@ 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
|
||||
@@ -1686,6 +1687,20 @@ 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, to=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, to=1.0f) float, @IntRange(from=0) int);
|
||||
method @NonNull public android.os.VibrationEffect build();
|
||||
method @NonNull public android.os.VibrationEffect build(int);
|
||||
}
|
||||
|
||||
public class VintfObject {
|
||||
method public static String[] getHalNamesAndVersions();
|
||||
method public static String getSepolicyVersion();
|
||||
@@ -1832,11 +1847,28 @@ package android.os.vibrator {
|
||||
field @NonNull public static final android.os.Parcelable.Creator<android.os.vibrator.PrimitiveSegment> CREATOR;
|
||||
}
|
||||
|
||||
public final class RampSegment extends android.os.vibrator.VibrationEffectSegment {
|
||||
method @NonNull public android.os.vibrator.RampSegment applyEffectStrength(int);
|
||||
method public int describeContents();
|
||||
method public long getDuration();
|
||||
method public float getEndAmplitude();
|
||||
method public float getEndFrequency();
|
||||
method public float getStartAmplitude();
|
||||
method public float getStartFrequency();
|
||||
method public boolean hasNonZeroAmplitude();
|
||||
method @NonNull public android.os.vibrator.RampSegment resolve(int);
|
||||
method @NonNull public android.os.vibrator.RampSegment scale(float);
|
||||
method public void validate();
|
||||
method public void writeToParcel(@NonNull android.os.Parcel, int);
|
||||
field @NonNull public static final android.os.Parcelable.Creator<android.os.vibrator.RampSegment> CREATOR;
|
||||
}
|
||||
|
||||
public final class StepSegment extends android.os.vibrator.VibrationEffectSegment {
|
||||
method @NonNull public android.os.vibrator.StepSegment applyEffectStrength(int);
|
||||
method public int describeContents();
|
||||
method public float getAmplitude();
|
||||
method public long getDuration();
|
||||
method public float getFrequency();
|
||||
method public boolean hasNonZeroAmplitude();
|
||||
method @NonNull public android.os.vibrator.StepSegment resolve(int);
|
||||
method @NonNull public android.os.vibrator.StepSegment scale(float);
|
||||
|
||||
@@ -21,6 +21,7 @@ import android.annotation.IntDef;
|
||||
import android.annotation.IntRange;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.annotation.TestApi;
|
||||
import android.compat.annotation.UnsupportedAppUsage;
|
||||
import android.content.ContentResolver;
|
||||
@@ -30,6 +31,7 @@ import android.hardware.vibrator.V1_3.Effect;
|
||||
import android.net.Uri;
|
||||
import android.os.vibrator.PrebakedSegment;
|
||||
import android.os.vibrator.PrimitiveSegment;
|
||||
import android.os.vibrator.RampSegment;
|
||||
import android.os.vibrator.StepSegment;
|
||||
import android.os.vibrator.VibrationEffectSegment;
|
||||
import android.util.MathUtils;
|
||||
@@ -250,7 +252,7 @@ public abstract class VibrationEffect implements Parcelable {
|
||||
for (int i = 0; i < timings.length; i++) {
|
||||
float parsedAmplitude = amplitudes[i] == DEFAULT_AMPLITUDE
|
||||
? DEFAULT_AMPLITUDE : (float) amplitudes[i] / MAX_AMPLITUDE;
|
||||
segments.add(new StepSegment(parsedAmplitude, (int) timings[i]));
|
||||
segments.add(new StepSegment(parsedAmplitude, /* frequency= */ 0, (int) timings[i]));
|
||||
}
|
||||
VibrationEffect effect = new Composed(segments, repeat);
|
||||
effect.validate();
|
||||
@@ -389,8 +391,26 @@ public abstract class VibrationEffect implements Parcelable {
|
||||
* @see VibrationEffect.Composition
|
||||
*/
|
||||
@NonNull
|
||||
public static VibrationEffect.Composition startComposition() {
|
||||
return new VibrationEffect.Composition();
|
||||
public static Composition startComposition() {
|
||||
return new Composition();
|
||||
}
|
||||
|
||||
/**
|
||||
* Start building a waveform vibration.
|
||||
*
|
||||
* <p>The waveform builder offers more flexibility for creating waveform vibrations, allowing
|
||||
* control over vibration frequency and ramping up or down the vibration amplitude, frequency or
|
||||
* both.
|
||||
*
|
||||
* <p>For simpler waveform patterns see {@link #createWaveform} methods.
|
||||
*
|
||||
* @hide
|
||||
* @see VibrationEffect.WaveformBuilder
|
||||
*/
|
||||
@TestApi
|
||||
@NonNull
|
||||
public static WaveformBuilder startWaveform() {
|
||||
return new WaveformBuilder();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -770,6 +790,42 @@ public abstract class VibrationEffect implements Parcelable {
|
||||
|
||||
Composition() {}
|
||||
|
||||
/**
|
||||
* Add a haptic effect to the end of the current composition.
|
||||
*
|
||||
* <p>Similar to {@link #addEffect(VibrationEffect, int)} , but with no delay applied.
|
||||
*
|
||||
* @param effect The effect to add to this composition as a primitive
|
||||
* @return The {@link Composition} object to enable adding multiple primitives in one chain.
|
||||
* @hide
|
||||
*/
|
||||
@TestApi
|
||||
@NonNull
|
||||
public Composition addEffect(@NonNull VibrationEffect effect) {
|
||||
return addEffect(effect, /* delay= */ 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a haptic effect to the end of the current composition.
|
||||
*
|
||||
* @param effect The effect to add to this composition as a primitive
|
||||
* @param delay The amount of time in milliseconds to wait before playing this primitive
|
||||
* @return The {@link Composition} object to enable adding multiple primitives in one chain.
|
||||
* @hide
|
||||
*/
|
||||
@TestApi
|
||||
@NonNull
|
||||
public Composition addEffect(@NonNull VibrationEffect effect,
|
||||
@IntRange(from = 0) int delay) {
|
||||
Preconditions.checkArgumentNonnegative(delay);
|
||||
if (delay > 0) {
|
||||
// Created a segment sustaining the zero amplitude to represent the delay.
|
||||
addSegment(new StepSegment(/* amplitude= */ 0, /* frequency= */ 0,
|
||||
/* duration= */ delay));
|
||||
}
|
||||
return addSegments(effect);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a haptic primitive to the end of the current composition.
|
||||
*
|
||||
@@ -829,6 +885,21 @@ public abstract class VibrationEffect implements Parcelable {
|
||||
return this;
|
||||
}
|
||||
|
||||
private Composition addSegments(VibrationEffect effect) {
|
||||
if (mRepeatIndex >= 0) {
|
||||
throw new IllegalStateException(
|
||||
"Composition already have a repeating effect so any new primitive would be"
|
||||
+ " unreachable.");
|
||||
}
|
||||
Composed composed = (Composed) effect;
|
||||
if (composed.getRepeatIndex() >= 0) {
|
||||
// Start repeating from the index relative to the composed waveform.
|
||||
mRepeatIndex = mSegments.size() + composed.getRepeatIndex();
|
||||
}
|
||||
mSegments.addAll(composed.getSegments());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compose all of the added primitives together into a single {@link VibrationEffect}.
|
||||
*
|
||||
@@ -881,6 +952,164 @@ public abstract class VibrationEffect implements Parcelable {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A builder for waveform haptic effects.
|
||||
*
|
||||
* <p>Waveform vibrations constitute of one or more timed segments where the vibration
|
||||
* amplitude, frequency or both can linearly ramp to new values.
|
||||
*
|
||||
* <p>Waveform segments may have zero duration, which represent a jump to new vibration
|
||||
* amplitude and/or frequency values.
|
||||
*
|
||||
* <p>Waveform segments may have the same start and end vibration amplitude and frequency,
|
||||
* which represent a step where the amplitude and frequency are maintained for that duration.
|
||||
*
|
||||
* @hide
|
||||
* @see VibrationEffect#startWaveform()
|
||||
*/
|
||||
@TestApi
|
||||
public static final class WaveformBuilder {
|
||||
private ArrayList<VibrationEffectSegment> mSegments = new ArrayList<>();
|
||||
|
||||
WaveformBuilder() {}
|
||||
|
||||
/**
|
||||
* Vibrate with given amplitude for the given duration, in millis, keeping the previous
|
||||
* frequency the same.
|
||||
*
|
||||
* <p>If the duration is zero the vibrator will jump to new amplitude.
|
||||
*
|
||||
* @param amplitude The amplitude for this step
|
||||
* @param duration The duration of this step in milliseconds
|
||||
* @return The {@link WaveformBuilder} object to enable adding multiple steps in chain.
|
||||
*/
|
||||
@SuppressLint("MissingGetterMatchingBuilder")
|
||||
@NonNull
|
||||
public WaveformBuilder addStep(@FloatRange(from = 0f, to = 1f) float amplitude,
|
||||
@IntRange(from = 0) int duration) {
|
||||
return addStep(amplitude, getPreviousFrequency(), duration);
|
||||
}
|
||||
|
||||
/**
|
||||
* Vibrate with given amplitude for the given duration, in millis, keeping the previous
|
||||
* vibration frequency the same.
|
||||
*
|
||||
* <p>If the duration is zero the vibrator will jump to new amplitude.
|
||||
*
|
||||
* @param amplitude The amplitude for this step
|
||||
* @param frequency The frequency for this step
|
||||
* @param duration The duration of this step in milliseconds
|
||||
* @return The {@link WaveformBuilder} object to enable adding multiple steps in chain.
|
||||
*/
|
||||
@SuppressLint("MissingGetterMatchingBuilder")
|
||||
@NonNull
|
||||
public WaveformBuilder addStep(@FloatRange(from = 0f, to = 1f) float amplitude,
|
||||
@FloatRange(from = -1f, to = 1f) float frequency,
|
||||
@IntRange(from = 0) int duration) {
|
||||
mSegments.add(new StepSegment(amplitude, frequency, duration));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ramp vibration linearly for the given duration, in millis, from previous amplitude value
|
||||
* to the given one, keeping previous frequency.
|
||||
*
|
||||
* <p>If the duration is zero the vibrator will jump to new amplitude.
|
||||
*
|
||||
* @param amplitude The final amplitude this ramp should reach
|
||||
* @param duration The duration of this ramp in milliseconds
|
||||
* @return The {@link WaveformBuilder} object to enable adding multiple steps in chain.
|
||||
*/
|
||||
@SuppressLint("MissingGetterMatchingBuilder")
|
||||
@NonNull
|
||||
public WaveformBuilder addRamp(@FloatRange(from = 0f, to = 1f) float amplitude,
|
||||
@IntRange(from = 0) int duration) {
|
||||
return addRamp(amplitude, getPreviousFrequency(), duration);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ramp vibration linearly for the given duration, in millis, from previous amplitude and
|
||||
* frequency values to the given ones.
|
||||
*
|
||||
* <p>If the duration is zero the vibrator will jump to new amplitude and frequency.
|
||||
*
|
||||
* @param amplitude The final amplitude this ramp should reach
|
||||
* @param frequency The final frequency this ramp should reach
|
||||
* @param duration The duration of this ramp in milliseconds
|
||||
* @return The {@link WaveformBuilder} object to enable adding multiple steps in chain.
|
||||
*/
|
||||
@SuppressLint("MissingGetterMatchingBuilder")
|
||||
@NonNull
|
||||
public WaveformBuilder addRamp(@FloatRange(from = 0f, to = 1f) float amplitude,
|
||||
@FloatRange(from = -1f, to = 1f) float frequency,
|
||||
@IntRange(from = 0) int duration) {
|
||||
mSegments.add(new RampSegment(getPreviousAmplitude(), amplitude, getPreviousFrequency(),
|
||||
frequency, duration));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compose all of the steps together into a single {@link VibrationEffect}.
|
||||
*
|
||||
* The {@link WaveformBuilder} object is still valid after this call, so you can
|
||||
* continue adding more primitives to it and generating more {@link VibrationEffect}s by
|
||||
* calling this method again.
|
||||
*
|
||||
* @return The {@link VibrationEffect} resulting from the composition of the steps.
|
||||
*/
|
||||
@NonNull
|
||||
public VibrationEffect build() {
|
||||
return build(/* repeat= */ -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compose all of the steps together into a single {@link VibrationEffect}.
|
||||
*
|
||||
* <p>To cause the pattern to repeat, pass the index at which to start the repetition
|
||||
* (starting at 0), or -1 to disable repeating.
|
||||
*
|
||||
* <p>The {@link WaveformBuilder} object is still valid after this call, so you can
|
||||
* continue adding more primitives to it and generating more {@link VibrationEffect}s by
|
||||
* calling this method again.
|
||||
*
|
||||
* @return The {@link VibrationEffect} resulting from the composition of the steps.
|
||||
*/
|
||||
@NonNull
|
||||
public VibrationEffect build(int repeat) {
|
||||
if (mSegments.isEmpty()) {
|
||||
throw new IllegalStateException(
|
||||
"WaveformBuilder must have at least one element to build.");
|
||||
}
|
||||
VibrationEffect effect = new Composed(mSegments, repeat);
|
||||
effect.validate();
|
||||
return effect;
|
||||
}
|
||||
|
||||
private float getPreviousFrequency() {
|
||||
if (!mSegments.isEmpty()) {
|
||||
VibrationEffectSegment segment = mSegments.get(mSegments.size() - 1);
|
||||
if (segment instanceof StepSegment) {
|
||||
return ((StepSegment) segment).getFrequency();
|
||||
} else if (segment instanceof RampSegment) {
|
||||
return ((RampSegment) segment).getEndFrequency();
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private float getPreviousAmplitude() {
|
||||
if (!mSegments.isEmpty()) {
|
||||
VibrationEffectSegment segment = mSegments.get(mSegments.size() - 1);
|
||||
if (segment instanceof StepSegment) {
|
||||
return ((StepSegment) segment).getAmplitude();
|
||||
} else if (segment instanceof RampSegment) {
|
||||
return ((RampSegment) segment).getEndAmplitude();
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static final Parcelable.Creator<VibrationEffect> CREATOR =
|
||||
new Parcelable.Creator<VibrationEffect>() {
|
||||
|
||||
176
core/java/android/os/vibrator/RampSegment.java
Normal file
176
core/java/android/os/vibrator/RampSegment.java
Normal file
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.os.vibrator;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.TestApi;
|
||||
import android.os.Parcel;
|
||||
import android.os.VibrationEffect;
|
||||
|
||||
import com.android.internal.util.Preconditions;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Representation of {@link VibrationEffectSegment} that ramps vibration amplitude and/or frequency
|
||||
* for a specified duration.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@TestApi
|
||||
public final class RampSegment extends VibrationEffectSegment {
|
||||
private final float mStartAmplitude;
|
||||
private final float mStartFrequency;
|
||||
private final float mEndAmplitude;
|
||||
private final float mEndFrequency;
|
||||
private final int mDuration;
|
||||
|
||||
RampSegment(@NonNull Parcel in) {
|
||||
this(in.readFloat(), in.readFloat(), in.readFloat(), in.readFloat(), in.readInt());
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public RampSegment(float startAmplitude, float endAmplitude, float startFrequency,
|
||||
float endFrequency, int duration) {
|
||||
mStartAmplitude = startAmplitude;
|
||||
mEndAmplitude = endAmplitude;
|
||||
mStartFrequency = startFrequency;
|
||||
mEndFrequency = endFrequency;
|
||||
mDuration = duration;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (!(o instanceof RampSegment)) {
|
||||
return false;
|
||||
}
|
||||
RampSegment other = (RampSegment) o;
|
||||
return Float.compare(mStartAmplitude, other.mStartAmplitude) == 0
|
||||
&& Float.compare(mEndAmplitude, other.mEndAmplitude) == 0
|
||||
&& Float.compare(mStartFrequency, other.mStartFrequency) == 0
|
||||
&& Float.compare(mEndFrequency, other.mEndFrequency) == 0
|
||||
&& mDuration == other.mDuration;
|
||||
}
|
||||
|
||||
public float getStartAmplitude() {
|
||||
return mStartAmplitude;
|
||||
}
|
||||
|
||||
public float getEndAmplitude() {
|
||||
return mEndAmplitude;
|
||||
}
|
||||
|
||||
public float getStartFrequency() {
|
||||
return mStartFrequency;
|
||||
}
|
||||
|
||||
public float getEndFrequency() {
|
||||
return mEndFrequency;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getDuration() {
|
||||
return mDuration;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNonZeroAmplitude() {
|
||||
return mStartAmplitude > 0 || mEndAmplitude > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validate() {
|
||||
Preconditions.checkArgumentNonnegative(mDuration,
|
||||
"Durations must all be >= 0, got " + mDuration);
|
||||
Preconditions.checkArgumentInRange(mStartAmplitude, 0f, 1f, "startAmplitude");
|
||||
Preconditions.checkArgumentInRange(mEndAmplitude, 0f, 1f, "endAmplitude");
|
||||
}
|
||||
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public RampSegment resolve(int defaultAmplitude) {
|
||||
// Default amplitude is not supported for ramping.
|
||||
return this;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public RampSegment scale(float scaleFactor) {
|
||||
float newStartAmplitude = VibrationEffect.scale(mStartAmplitude, scaleFactor);
|
||||
float newEndAmplitude = VibrationEffect.scale(mEndAmplitude, scaleFactor);
|
||||
if (Float.compare(mStartAmplitude, newStartAmplitude) == 0
|
||||
&& Float.compare(mEndAmplitude, newEndAmplitude) == 0) {
|
||||
return this;
|
||||
}
|
||||
return new RampSegment(newStartAmplitude, newEndAmplitude, mStartFrequency, mEndFrequency,
|
||||
mDuration);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public RampSegment applyEffectStrength(int effectStrength) {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(mStartAmplitude, mEndAmplitude, mStartFrequency, mEndFrequency,
|
||||
mDuration);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Ramp{startAmplitude=" + mStartAmplitude
|
||||
+ ", endAmplitude=" + mEndAmplitude
|
||||
+ ", startFrequency=" + mStartFrequency
|
||||
+ ", endFrequency=" + mEndFrequency
|
||||
+ ", duration=" + mDuration
|
||||
+ "}";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(@NonNull Parcel out, int flags) {
|
||||
out.writeInt(PARCEL_TOKEN_RAMP);
|
||||
out.writeFloat(mStartAmplitude);
|
||||
out.writeFloat(mEndAmplitude);
|
||||
out.writeFloat(mStartFrequency);
|
||||
out.writeFloat(mEndFrequency);
|
||||
out.writeInt(mDuration);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static final Creator<RampSegment> CREATOR =
|
||||
new Creator<RampSegment>() {
|
||||
@Override
|
||||
public RampSegment createFromParcel(Parcel in) {
|
||||
// Skip the type token
|
||||
in.readInt();
|
||||
return new RampSegment(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RampSegment[] newArray(int size) {
|
||||
return new RampSegment[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -27,23 +27,25 @@ import com.android.internal.util.Preconditions;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Representation of {@link VibrationEffectSegment} that holds a fixed vibration amplitude for a
|
||||
* specified duration.
|
||||
* Representation of {@link VibrationEffectSegment} that holds a fixed vibration amplitude and
|
||||
* frequency for a specified duration.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@TestApi
|
||||
public final class StepSegment extends VibrationEffectSegment {
|
||||
private final float mAmplitude;
|
||||
private final float mFrequency;
|
||||
private final int mDuration;
|
||||
|
||||
StepSegment(@NonNull Parcel in) {
|
||||
this(in.readFloat(), in.readInt());
|
||||
this(in.readFloat(), in.readFloat(), in.readInt());
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public StepSegment(float amplitude, int duration) {
|
||||
public StepSegment(float amplitude, float frequency, int duration) {
|
||||
mAmplitude = amplitude;
|
||||
mFrequency = frequency;
|
||||
mDuration = duration;
|
||||
}
|
||||
|
||||
@@ -54,6 +56,7 @@ public final class StepSegment extends VibrationEffectSegment {
|
||||
}
|
||||
StepSegment other = (StepSegment) o;
|
||||
return Float.compare(mAmplitude, other.mAmplitude) == 0
|
||||
&& Float.compare(mFrequency, other.mFrequency) == 0
|
||||
&& mDuration == other.mDuration;
|
||||
}
|
||||
|
||||
@@ -61,6 +64,10 @@ public final class StepSegment extends VibrationEffectSegment {
|
||||
return mAmplitude;
|
||||
}
|
||||
|
||||
public float getFrequency() {
|
||||
return mFrequency;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getDuration() {
|
||||
return mDuration;
|
||||
@@ -92,7 +99,8 @@ public final class StepSegment extends VibrationEffectSegment {
|
||||
if (Float.compare(mAmplitude, VibrationEffect.DEFAULT_AMPLITUDE) != 0) {
|
||||
return this;
|
||||
}
|
||||
return new StepSegment((float) defaultAmplitude / VibrationEffect.MAX_AMPLITUDE, mDuration);
|
||||
return new StepSegment((float) defaultAmplitude / VibrationEffect.MAX_AMPLITUDE, mFrequency,
|
||||
mDuration);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@@ -101,7 +109,8 @@ public final class StepSegment extends VibrationEffectSegment {
|
||||
if (Float.compare(mAmplitude, VibrationEffect.DEFAULT_AMPLITUDE) == 0) {
|
||||
return this;
|
||||
}
|
||||
return new StepSegment(VibrationEffect.scale(mAmplitude, scaleFactor), mDuration);
|
||||
return new StepSegment(VibrationEffect.scale(mAmplitude, scaleFactor), mFrequency,
|
||||
mDuration);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@@ -112,12 +121,13 @@ public final class StepSegment extends VibrationEffectSegment {
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(mAmplitude, mDuration);
|
||||
return Objects.hash(mAmplitude, mFrequency, mDuration);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Step{amplitude=" + mAmplitude
|
||||
+ ", frequency=" + mFrequency
|
||||
+ ", duration=" + mDuration
|
||||
+ "}";
|
||||
}
|
||||
@@ -131,6 +141,7 @@ public final class StepSegment extends VibrationEffectSegment {
|
||||
public void writeToParcel(@NonNull Parcel out, int flags) {
|
||||
out.writeInt(PARCEL_TOKEN_STEP);
|
||||
out.writeFloat(mAmplitude);
|
||||
out.writeFloat(mFrequency);
|
||||
out.writeInt(mDuration);
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,8 @@ import android.os.VibrationEffect;
|
||||
* <ol>
|
||||
* <li>A predefined vibration effect;
|
||||
* <li>A composable effect primitive;
|
||||
* <li>Fixed amplitude value to be held for a specified duration;
|
||||
* <li>Fixed amplitude and frequency values to be held for a specified duration;
|
||||
* <li>Pairs of amplitude and frequency values to be ramped to for a specified duration;
|
||||
* </ol>
|
||||
*
|
||||
* @hide
|
||||
@@ -42,6 +43,7 @@ public abstract class VibrationEffectSegment implements Parcelable {
|
||||
static final int PARCEL_TOKEN_PREBAKED = 1;
|
||||
static final int PARCEL_TOKEN_PRIMITIVE = 2;
|
||||
static final int PARCEL_TOKEN_STEP = 3;
|
||||
static final int PARCEL_TOKEN_RAMP = 4;
|
||||
|
||||
/** Prevent subclassing from outside of this package */
|
||||
VibrationEffectSegment() {
|
||||
@@ -96,6 +98,8 @@ public abstract class VibrationEffectSegment implements Parcelable {
|
||||
switch (in.readInt()) {
|
||||
case PARCEL_TOKEN_STEP:
|
||||
return new StepSegment(in);
|
||||
case PARCEL_TOKEN_RAMP:
|
||||
return new RampSegment(in);
|
||||
case PARCEL_TOKEN_PREBAKED:
|
||||
return new PrebakedSegment(in);
|
||||
case PARCEL_TOKEN_PRIMITIVE:
|
||||
|
||||
@@ -25,6 +25,16 @@ message StepSegmentProto {
|
||||
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
|
||||
optional int32 duration = 1;
|
||||
optional float amplitude = 2;
|
||||
optional float frequency = 3;
|
||||
}
|
||||
|
||||
message RampSegmentProto {
|
||||
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
|
||||
optional int32 duration = 1;
|
||||
optional float startAmplitude = 2;
|
||||
optional float endAmplitude = 3;
|
||||
optional float startFrequency = 4;
|
||||
optional float endFrequency = 5;
|
||||
}
|
||||
|
||||
message PrebakedSegmentProto {
|
||||
@@ -46,6 +56,7 @@ message SegmentProto {
|
||||
optional PrebakedSegmentProto prebaked = 1;
|
||||
optional PrimitiveSegmentProto primitive = 2;
|
||||
optional StepSegmentProto step = 3;
|
||||
optional RampSegmentProto ramp = 4;
|
||||
}
|
||||
|
||||
// A com.android.os.VibrationEffect object.
|
||||
|
||||
@@ -118,7 +118,16 @@ public class VibrationEffectTest {
|
||||
public void testValidateWaveform() {
|
||||
VibrationEffect.createWaveform(TEST_TIMINGS, TEST_AMPLITUDES, -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, /* frequency*/ 1, /* duration= */ 100)
|
||||
.addRamp(/* amplitude= */ 0.5f, /* frequency*/ -1, /* 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,
|
||||
@@ -132,17 +141,31 @@ public class VibrationEffectTest {
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> VibrationEffect.createWaveform(
|
||||
TEST_TIMINGS, TEST_AMPLITUDES, TEST_TIMINGS.length).validate());
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> VibrationEffect.startWaveform()
|
||||
.addStep(/* amplitude= */ -2, 10).build().validate());
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> VibrationEffect.startWaveform()
|
||||
.addStep(1, /* duration= */ -1).build().validate());
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> VibrationEffect.startWaveform()
|
||||
.addStep(1, 0, /* duration= */ -1).build().validate());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateComposed() {
|
||||
VibrationEffect.startComposition()
|
||||
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
|
||||
.addEffect(TEST_ONE_SHOT)
|
||||
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f)
|
||||
.addEffect(TEST_WAVEFORM, 100)
|
||||
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 0.5f, 10)
|
||||
.addEffect(VibrationEffect.get(VibrationEffect.EFFECT_CLICK))
|
||||
.compose()
|
||||
.validate();
|
||||
|
||||
assertThrows(IllegalStateException.class,
|
||||
() -> VibrationEffect.startComposition().compose().validate());
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> VibrationEffect.startComposition().addPrimitive(-1).compose().validate());
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
@@ -150,6 +173,16 @@ public class VibrationEffectTest {
|
||||
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, -1, 10)
|
||||
.compose()
|
||||
.validate());
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> VibrationEffect.startComposition()
|
||||
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1, -10)
|
||||
.compose()
|
||||
.validate());
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> VibrationEffect.startComposition()
|
||||
.addEffect(TEST_ONE_SHOT, /* delay= */ -10)
|
||||
.compose()
|
||||
.validate());
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> VibrationEffect.startComposition()
|
||||
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f, -1)
|
||||
@@ -185,6 +218,12 @@ public class VibrationEffectTest {
|
||||
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f, 1)
|
||||
.compose();
|
||||
assertEquals(effect, effect.resolve(51));
|
||||
|
||||
VibrationEffect.Composed resolved = VibrationEffect.startComposition()
|
||||
.addEffect(DEFAULT_ONE_SHOT)
|
||||
.compose()
|
||||
.resolve(51);
|
||||
assertEquals(0.2f, ((StepSegment) resolved.getSegments().get(0)).getAmplitude());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -215,6 +254,13 @@ public class VibrationEffectTest {
|
||||
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 0.5f, 1)
|
||||
.compose();
|
||||
assertEquals(effect, effect.applyEffectStrength(VibrationEffect.EFFECT_STRENGTH_LIGHT));
|
||||
|
||||
VibrationEffect.Composed applied = VibrationEffect.startComposition()
|
||||
.addEffect(VibrationEffect.get(VibrationEffect.EFFECT_CLICK))
|
||||
.compose()
|
||||
.applyEffectStrength(VibrationEffect.EFFECT_STRENGTH_LIGHT);
|
||||
assertEquals(VibrationEffect.EFFECT_STRENGTH_LIGHT,
|
||||
((PrebakedSegment) applied.getSegments().get(0)).getEffectStrength());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -251,13 +297,16 @@ public class VibrationEffectTest {
|
||||
VibrationEffect.Composed effect =
|
||||
(VibrationEffect.Composed) VibrationEffect.startComposition()
|
||||
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 0.5f, 1)
|
||||
.addEffect(TEST_ONE_SHOT)
|
||||
.compose();
|
||||
|
||||
VibrationEffect.Composed scaledUp = effect.scale(1.5f);
|
||||
assertTrue(0.5f < ((PrimitiveSegment) scaledUp.getSegments().get(0)).getScale());
|
||||
assertTrue(100 / 255f < ((StepSegment) scaledUp.getSegments().get(1)).getAmplitude());
|
||||
|
||||
VibrationEffect.Composed scaledDown = effect.scale(0.5f);
|
||||
assertTrue(0.5f > ((PrimitiveSegment) scaledDown.getSegments().get(0)).getScale());
|
||||
assertTrue(100 / 255f > ((StepSegment) scaledDown.getSegments().get(1)).getAmplitude());
|
||||
}
|
||||
|
||||
private Resources mockRingtoneResources() {
|
||||
@@ -293,4 +342,4 @@ public class VibrationEffectTest {
|
||||
|
||||
return context;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package 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.os.Parcel;
|
||||
import android.os.VibrationEffect;
|
||||
import android.platform.test.annotations.Presubmit;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
@Presubmit
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class RampSegmentTest {
|
||||
private static final float TOLERANCE = 1e-2f;
|
||||
|
||||
@Test
|
||||
public void testCreation() {
|
||||
RampSegment ramp = new RampSegment(/* startAmplitude= */ 1, /* endAmplitude= */ 0,
|
||||
/* StartFrequency= */ -1, /* endFrequency= */ 1, /* duration= */ 100);
|
||||
|
||||
assertEquals(100L, ramp.getDuration());
|
||||
assertTrue(ramp.hasNonZeroAmplitude());
|
||||
assertEquals(1f, ramp.getStartAmplitude());
|
||||
assertEquals(0f, ramp.getEndAmplitude());
|
||||
assertEquals(-1f, ramp.getStartFrequency());
|
||||
assertEquals(1f, ramp.getEndFrequency());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSerialization() {
|
||||
RampSegment original = new RampSegment(0, 1, 0, 0.5f, 10);
|
||||
Parcel parcel = Parcel.obtain();
|
||||
original.writeToParcel(parcel, 0);
|
||||
parcel.setDataPosition(0);
|
||||
assertEquals(original, RampSegment.CREATOR.createFromParcel(parcel));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidate() {
|
||||
new RampSegment(/* startAmplitude= */ 1, /* endAmplitude= */ 0,
|
||||
/* StartFrequency= */ -1, /* endFrequency= */ 1, /* duration= */ 100).validate();
|
||||
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> new RampSegment(VibrationEffect.DEFAULT_AMPLITUDE, 0, 0, 0, 0).validate());
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> new RampSegment(/* startAmplitude= */ -2, 0, 0, 0, 0).validate());
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> new RampSegment(0, /* endAmplitude= */ 2, 0, 0, 0).validate());
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> new RampSegment(0, 0, 0, 0, /* duration= */ -1).validate());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHasNonZeroAmplitude() {
|
||||
assertTrue(new RampSegment(0, 1, 0, 0, 0).hasNonZeroAmplitude());
|
||||
assertTrue(new RampSegment(0.01f, 0, 0, 0, 0).hasNonZeroAmplitude());
|
||||
assertFalse(new RampSegment(0, 0, 0, 0, 0).hasNonZeroAmplitude());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResolve() {
|
||||
RampSegment ramp = new RampSegment(0, 1, 0, 0, 0);
|
||||
assertSame(ramp, ramp.resolve(100));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testApplyEffectStrength_ignoresAndReturnsSameEffect() {
|
||||
RampSegment ramp = new RampSegment(1, 0, 1, 0, 0);
|
||||
assertSame(ramp, ramp.applyEffectStrength(VibrationEffect.EFFECT_STRENGTH_STRONG));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testScale() {
|
||||
RampSegment initial = new RampSegment(0, 1, 0, 0, 0);
|
||||
|
||||
assertEquals(0f, initial.scale(1).getStartAmplitude(), TOLERANCE);
|
||||
assertEquals(0f, initial.scale(0.5f).getStartAmplitude(), TOLERANCE);
|
||||
assertEquals(0f, initial.scale(1.5f).getStartAmplitude(), TOLERANCE);
|
||||
assertEquals(0f, initial.scale(1.5f).scale(2 / 3f).getStartAmplitude(), TOLERANCE);
|
||||
assertEquals(0f, initial.scale(0.8f).scale(1.25f).getStartAmplitude(), TOLERANCE);
|
||||
|
||||
assertEquals(1f, initial.scale(1).getEndAmplitude(), TOLERANCE);
|
||||
assertEquals(0.34f, initial.scale(0.5f).getEndAmplitude(), TOLERANCE);
|
||||
// The original value was not scaled up, so this only scales it down.
|
||||
assertEquals(1f, initial.scale(1.5f).getEndAmplitude(), TOLERANCE);
|
||||
assertEquals(0.53f, initial.scale(1.5f).scale(2 / 3f).getEndAmplitude(), TOLERANCE);
|
||||
// Does not restore to the exact original value because scale up is a bit offset.
|
||||
assertEquals(0.71f, initial.scale(0.8f).getEndAmplitude(), TOLERANCE);
|
||||
assertEquals(0.84f, initial.scale(0.8f).scale(1.25f).getEndAmplitude(), TOLERANCE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testScale_halfPrimitiveScaleValue() {
|
||||
RampSegment initial = new RampSegment(0.5f, 1, 0, 0, 0);
|
||||
|
||||
assertEquals(0.5f, initial.scale(1).getStartAmplitude(), TOLERANCE);
|
||||
assertEquals(0.17f, initial.scale(0.5f).getStartAmplitude(), TOLERANCE);
|
||||
// Does not restore to the exact original value because scale up is a bit offset.
|
||||
assertEquals(0.86f, initial.scale(1.5f).getStartAmplitude(), TOLERANCE);
|
||||
assertEquals(0.47f, initial.scale(1.5f).scale(2 / 3f).getStartAmplitude(), TOLERANCE);
|
||||
// Does not restore to the exact original value because scale up is a bit offset.
|
||||
assertEquals(0.35f, initial.scale(0.8f).getStartAmplitude(), TOLERANCE);
|
||||
assertEquals(0.5f, initial.scale(0.8f).scale(1.25f).getStartAmplitude(), TOLERANCE);
|
||||
}
|
||||
}
|
||||
@@ -38,16 +38,18 @@ public class StepSegmentTest {
|
||||
|
||||
@Test
|
||||
public void testCreation() {
|
||||
StepSegment step = new StepSegment(/* amplitude= */ 1f, /* duration= */ 100);
|
||||
StepSegment step = new StepSegment(/* amplitude= */ 1f, /* frequency= */ -1f,
|
||||
/* duration= */ 100);
|
||||
|
||||
assertEquals(100, step.getDuration());
|
||||
assertTrue(step.hasNonZeroAmplitude());
|
||||
assertEquals(1f, step.getAmplitude());
|
||||
assertEquals(-1f, step.getFrequency());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSerialization() {
|
||||
StepSegment original = new StepSegment(0.5f, 10);
|
||||
StepSegment original = new StepSegment(0.5f, 1f, 10);
|
||||
Parcel parcel = Parcel.obtain();
|
||||
original.writeToParcel(parcel, 0);
|
||||
parcel.setDataPosition(0);
|
||||
@@ -56,31 +58,31 @@ public class StepSegmentTest {
|
||||
|
||||
@Test
|
||||
public void testValidate() {
|
||||
new StepSegment(/* amplitude= */ 0f, /* duration= */ 100).validate();
|
||||
new StepSegment(/* amplitude= */ 0f, /* frequency= */ -1f, /* duration= */ 100).validate();
|
||||
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> new StepSegment(/* amplitude= */ -2, 10).validate());
|
||||
() -> new StepSegment(/* amplitude= */ -2, 1f, 10).validate());
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> new StepSegment(/* amplitude= */ 2, 10).validate());
|
||||
() -> new StepSegment(/* amplitude= */ 2, 1f, 10).validate());
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> new StepSegment(2, /* duration= */ -1).validate());
|
||||
() -> new StepSegment(2, 1f, /* duration= */ -1).validate());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHasNonZeroAmplitude() {
|
||||
assertTrue(new StepSegment(1f, 0).hasNonZeroAmplitude());
|
||||
assertTrue(new StepSegment(0.01f, 0).hasNonZeroAmplitude());
|
||||
assertTrue(new StepSegment(VibrationEffect.DEFAULT_AMPLITUDE, 0).hasNonZeroAmplitude());
|
||||
assertFalse(new StepSegment(0, 0).hasNonZeroAmplitude());
|
||||
assertTrue(new StepSegment(1f, 0, 0).hasNonZeroAmplitude());
|
||||
assertTrue(new StepSegment(0.01f, 0, 0).hasNonZeroAmplitude());
|
||||
assertTrue(new StepSegment(VibrationEffect.DEFAULT_AMPLITUDE, 0, 0).hasNonZeroAmplitude());
|
||||
assertFalse(new StepSegment(0, 0, 0).hasNonZeroAmplitude());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResolve() {
|
||||
StepSegment original = new StepSegment(VibrationEffect.DEFAULT_AMPLITUDE, 0);
|
||||
StepSegment original = new StepSegment(VibrationEffect.DEFAULT_AMPLITUDE, 0, 0);
|
||||
assertEquals(1f, original.resolve(VibrationEffect.MAX_AMPLITUDE).getAmplitude());
|
||||
assertEquals(0.2f, original.resolve(51).getAmplitude(), TOLERANCE);
|
||||
|
||||
StepSegment resolved = new StepSegment(0, 0);
|
||||
StepSegment resolved = new StepSegment(0, 0, 0);
|
||||
assertSame(resolved, resolved.resolve(100));
|
||||
|
||||
assertThrows(IllegalArgumentException.class, () -> resolved.resolve(1000));
|
||||
@@ -88,13 +90,13 @@ public class StepSegmentTest {
|
||||
|
||||
@Test
|
||||
public void testApplyEffectStrength_ignoresAndReturnsSameEffect() {
|
||||
StepSegment step = new StepSegment(VibrationEffect.DEFAULT_AMPLITUDE, 0);
|
||||
StepSegment step = new StepSegment(VibrationEffect.DEFAULT_AMPLITUDE, 0, 0);
|
||||
assertSame(step, step.applyEffectStrength(VibrationEffect.EFFECT_STRENGTH_STRONG));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testScale_fullAmplitude() {
|
||||
StepSegment initial = new StepSegment(1f, 0);
|
||||
StepSegment initial = new StepSegment(1f, 0, 0);
|
||||
|
||||
assertEquals(1f, initial.scale(1).getAmplitude(), TOLERANCE);
|
||||
assertEquals(0.34f, initial.scale(0.5f).getAmplitude(), TOLERANCE);
|
||||
@@ -108,7 +110,7 @@ public class StepSegmentTest {
|
||||
|
||||
@Test
|
||||
public void testScale_halfAmplitude() {
|
||||
StepSegment initial = new StepSegment(0.5f, 0);
|
||||
StepSegment initial = new StepSegment(0.5f, 0, 0);
|
||||
|
||||
assertEquals(0.5f, initial.scale(1).getAmplitude(), TOLERANCE);
|
||||
assertEquals(0.17f, initial.scale(0.5f).getAmplitude(), TOLERANCE);
|
||||
@@ -122,7 +124,7 @@ public class StepSegmentTest {
|
||||
|
||||
@Test
|
||||
public void testScale_zeroAmplitude() {
|
||||
StepSegment initial = new StepSegment(0, 0);
|
||||
StepSegment initial = new StepSegment(0, 0, 0);
|
||||
|
||||
assertEquals(0f, initial.scale(1).getAmplitude(), TOLERANCE);
|
||||
assertEquals(0f, initial.scale(0.5f).getAmplitude(), TOLERANCE);
|
||||
@@ -131,7 +133,7 @@ public class StepSegmentTest {
|
||||
|
||||
@Test
|
||||
public void testScale_defaultAmplitude() {
|
||||
StepSegment initial = new StepSegment(VibrationEffect.DEFAULT_AMPLITUDE, 0);
|
||||
StepSegment initial = new StepSegment(VibrationEffect.DEFAULT_AMPLITUDE, 0, 0);
|
||||
|
||||
assertEquals(VibrationEffect.DEFAULT_AMPLITUDE, initial.scale(1).getAmplitude(), TOLERANCE);
|
||||
assertEquals(VibrationEffect.DEFAULT_AMPLITUDE, initial.scale(0.5f).getAmplitude(),
|
||||
|
||||
@@ -24,6 +24,7 @@ import android.os.VibrationAttributes;
|
||||
import android.os.VibrationEffect;
|
||||
import android.os.vibrator.PrebakedSegment;
|
||||
import android.os.vibrator.PrimitiveSegment;
|
||||
import android.os.vibrator.RampSegment;
|
||||
import android.os.vibrator.StepSegment;
|
||||
import android.os.vibrator.VibrationEffectSegment;
|
||||
import android.util.SparseArray;
|
||||
@@ -345,6 +346,8 @@ final class Vibration {
|
||||
final long token = proto.start(fieldId);
|
||||
if (segment instanceof StepSegment) {
|
||||
dumpEffect(proto, SegmentProto.STEP, (StepSegment) segment);
|
||||
} else if (segment instanceof RampSegment) {
|
||||
dumpEffect(proto, SegmentProto.RAMP, (RampSegment) segment);
|
||||
} else if (segment instanceof PrebakedSegment) {
|
||||
dumpEffect(proto, SegmentProto.PREBAKED, (PrebakedSegment) segment);
|
||||
} else if (segment instanceof PrimitiveSegment) {
|
||||
@@ -357,6 +360,17 @@ final class Vibration {
|
||||
final long token = proto.start(fieldId);
|
||||
proto.write(StepSegmentProto.DURATION, segment.getDuration());
|
||||
proto.write(StepSegmentProto.AMPLITUDE, segment.getAmplitude());
|
||||
proto.write(StepSegmentProto.FREQUENCY, segment.getFrequency());
|
||||
proto.end(token);
|
||||
}
|
||||
|
||||
private void dumpEffect(ProtoOutputStream proto, long fieldId, RampSegment segment) {
|
||||
final long token = proto.start(fieldId);
|
||||
proto.write(RampSegmentProto.DURATION, segment.getDuration());
|
||||
proto.write(RampSegmentProto.START_AMPLITUDE, segment.getStartAmplitude());
|
||||
proto.write(RampSegmentProto.END_AMPLITUDE, segment.getEndAmplitude());
|
||||
proto.write(RampSegmentProto.START_FREQUENCY, segment.getStartFrequency());
|
||||
proto.write(RampSegmentProto.END_FREQUENCY, segment.getEndFrequency());
|
||||
proto.end(token);
|
||||
}
|
||||
|
||||
|
||||
@@ -75,8 +75,8 @@ final class FakeVibratorControllerProvider {
|
||||
|
||||
@Override
|
||||
public void on(long milliseconds, long vibrationId) {
|
||||
mEffectSegments.add(
|
||||
new StepSegment(VibrationEffect.DEFAULT_AMPLITUDE, (int) milliseconds));
|
||||
mEffectSegments.add(new StepSegment(VibrationEffect.DEFAULT_AMPLITUDE,
|
||||
/* frequency= */ 0, (int) milliseconds));
|
||||
applyLatency();
|
||||
scheduleListener(milliseconds, vibrationId);
|
||||
}
|
||||
|
||||
@@ -921,7 +921,7 @@ public class VibrationThreadTest {
|
||||
}
|
||||
|
||||
private VibrationEffectSegment expectedOneShot(long millis) {
|
||||
return new StepSegment(VibrationEffect.DEFAULT_AMPLITUDE, (int) millis);
|
||||
return new StepSegment(VibrationEffect.DEFAULT_AMPLITUDE, /* frequency= */ 0, (int) millis);
|
||||
}
|
||||
|
||||
private VibrationEffectSegment expectedPrebaked(int effectId) {
|
||||
|
||||
Reference in New Issue
Block a user