Merge "Create VibratorFrequencyProfile in Vibrator API."
This commit is contained in:
@@ -33045,9 +33045,13 @@ package android.os {
|
||||
method @NonNull public int[] areEffectsSupported(@NonNull int...);
|
||||
method @NonNull public boolean[] arePrimitivesSupported(@NonNull int...);
|
||||
method @RequiresPermission(android.Manifest.permission.VIBRATE) public abstract void cancel();
|
||||
method @Nullable public android.os.vibrator.VibratorFrequencyProfile getFrequencyProfile();
|
||||
method public int getId();
|
||||
method @NonNull public int[] getPrimitiveDurations(@NonNull int...);
|
||||
method public float getQFactor();
|
||||
method public float getResonantFrequency();
|
||||
method public abstract boolean hasAmplitudeControl();
|
||||
method public boolean hasFrequencyControl();
|
||||
method public abstract boolean hasVibrator();
|
||||
method @Deprecated @RequiresPermission(android.Manifest.permission.VIBRATE) public void vibrate(long);
|
||||
method @Deprecated @RequiresPermission(android.Manifest.permission.VIBRATE) public void vibrate(long, android.media.AudioAttributes);
|
||||
@@ -33373,6 +33377,17 @@ package android.os.strictmode {
|
||||
|
||||
}
|
||||
|
||||
package android.os.vibrator {
|
||||
|
||||
public final class VibratorFrequencyProfile {
|
||||
method public float getMaxAmplitudeMeasurementInterval();
|
||||
method @FloatRange(from=0, to=1) @NonNull public float[] getMaxAmplitudeMeasurements();
|
||||
method public float getMaxFrequency();
|
||||
method public float getMinFrequency();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
package android.preference {
|
||||
|
||||
@Deprecated public class CheckBoxPreference extends android.preference.TwoStatePreference {
|
||||
|
||||
@@ -18,18 +18,25 @@ package android.os;
|
||||
|
||||
import android.annotation.CallbackExecutor;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.compat.annotation.UnsupportedAppUsage;
|
||||
import android.content.Context;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.Log;
|
||||
import android.util.Range;
|
||||
import android.util.Slog;
|
||||
import android.util.SparseArray;
|
||||
import android.util.SparseBooleanArray;
|
||||
import android.util.SparseIntArray;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* Vibrator implementation that controls the main system vibrator.
|
||||
@@ -51,7 +58,7 @@ public class SystemVibrator extends Vibrator {
|
||||
|
||||
private final Object mLock = new Object();
|
||||
@GuardedBy("mLock")
|
||||
private AllVibratorsInfo mVibratorInfo;
|
||||
private VibratorInfo mVibratorInfo;
|
||||
|
||||
@UnsupportedAppUsage
|
||||
public SystemVibrator(Context context) {
|
||||
@@ -71,6 +78,11 @@ public class SystemVibrator extends Vibrator {
|
||||
return VibratorInfo.EMPTY_VIBRATOR_INFO;
|
||||
}
|
||||
int[] vibratorIds = mVibratorManager.getVibratorIds();
|
||||
if (vibratorIds.length == 0) {
|
||||
// It is known that the device has no vibrator, so cache and return info that
|
||||
// reflects the lack of support for effects/primitives.
|
||||
return mVibratorInfo = new NoVibratorInfo();
|
||||
}
|
||||
VibratorInfo[] vibratorInfos = new VibratorInfo[vibratorIds.length];
|
||||
for (int i = 0; i < vibratorIds.length; i++) {
|
||||
Vibrator vibrator = mVibratorManager.getVibrator(vibratorIds[i]);
|
||||
@@ -83,7 +95,12 @@ public class SystemVibrator extends Vibrator {
|
||||
}
|
||||
vibratorInfos[i] = vibrator.getInfo();
|
||||
}
|
||||
return mVibratorInfo = new AllVibratorsInfo(vibratorInfos);
|
||||
if (vibratorInfos.length == 1) {
|
||||
// Device has a single vibrator info, cache and return successfully loaded info.
|
||||
return mVibratorInfo = new VibratorInfo(/* id= */ -1, vibratorInfos[0]);
|
||||
}
|
||||
// Device has multiple vibrators, generate a single info representing all of them.
|
||||
return mVibratorInfo = new MultiVibratorInfo(vibratorInfos);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -257,77 +274,282 @@ public class SystemVibrator extends Vibrator {
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents all the vibrators information as a single {@link VibratorInfo}.
|
||||
* Represents a device with no vibrator as a single {@link VibratorInfo}.
|
||||
*
|
||||
* <p>This uses the first vibrator on the list as the default one for all hardware spec, but
|
||||
* uses an intersection of all vibrators to decide the capabilities and effect/primitive
|
||||
* @hide
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public static class NoVibratorInfo extends VibratorInfo {
|
||||
public NoVibratorInfo() {
|
||||
// Use empty arrays to indicate no support, while null would indicate support unknown.
|
||||
super(/* id= */ -1,
|
||||
/* capabilities= */ 0,
|
||||
/* supportedEffects= */ new SparseBooleanArray(),
|
||||
/* supportedBraking= */ new SparseBooleanArray(),
|
||||
/* supportedPrimitives= */ new SparseIntArray(),
|
||||
/* primitiveDelayMax= */ 0,
|
||||
/* compositionSizeMax= */ 0,
|
||||
/* pwlePrimitiveDurationMax= */ 0,
|
||||
/* pwleSizeMax= */ 0,
|
||||
/* qFactor= */ Float.NaN,
|
||||
new FrequencyProfile(/* resonantFrequencyHz= */ Float.NaN,
|
||||
/* minFrequencyHz= */ Float.NaN,
|
||||
/* frequencyResolutionHz= */ Float.NaN,
|
||||
/* maxAmplitudes= */ null));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents multiple vibrator information as a single {@link VibratorInfo}.
|
||||
*
|
||||
* <p>This uses an intersection of all vibrators to decide the capabilities and effect/primitive
|
||||
* support.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public static class AllVibratorsInfo extends VibratorInfo {
|
||||
private final VibratorInfo[] mVibratorInfos;
|
||||
public static class MultiVibratorInfo extends VibratorInfo {
|
||||
// Epsilon used for float comparison applied in calculations for the merged info.
|
||||
private static final float EPSILON = 1e-5f;
|
||||
|
||||
public AllVibratorsInfo(VibratorInfo[] vibrators) {
|
||||
super(/* id= */ -1, capabilitiesIntersection(vibrators),
|
||||
vibrators.length > 0 ? vibrators[0] : VibratorInfo.EMPTY_VIBRATOR_INFO);
|
||||
mVibratorInfos = vibrators;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int isEffectSupported(int effectId) {
|
||||
if (mVibratorInfos.length == 0) {
|
||||
return Vibrator.VIBRATION_EFFECT_SUPPORT_NO;
|
||||
}
|
||||
int supported = Vibrator.VIBRATION_EFFECT_SUPPORT_YES;
|
||||
for (VibratorInfo info : mVibratorInfos) {
|
||||
int effectSupported = info.isEffectSupported(effectId);
|
||||
if (effectSupported == Vibrator.VIBRATION_EFFECT_SUPPORT_NO) {
|
||||
return effectSupported;
|
||||
} else if (effectSupported == Vibrator.VIBRATION_EFFECT_SUPPORT_UNKNOWN) {
|
||||
supported = effectSupported;
|
||||
}
|
||||
}
|
||||
return supported;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPrimitiveSupported(int primitiveId) {
|
||||
if (mVibratorInfos.length == 0) {
|
||||
return false;
|
||||
}
|
||||
for (VibratorInfo info : mVibratorInfos) {
|
||||
if (!info.isPrimitiveSupported(primitiveId)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPrimitiveDuration(int primitiveId) {
|
||||
int maxDuration = 0;
|
||||
for (VibratorInfo info : mVibratorInfos) {
|
||||
int duration = info.getPrimitiveDuration(primitiveId);
|
||||
if (duration == 0) {
|
||||
return 0;
|
||||
}
|
||||
maxDuration = Math.max(maxDuration, duration);
|
||||
}
|
||||
return maxDuration;
|
||||
public MultiVibratorInfo(VibratorInfo[] vibrators) {
|
||||
super(/* id= */ -1,
|
||||
capabilitiesIntersection(vibrators),
|
||||
supportedEffectsIntersection(vibrators),
|
||||
supportedBrakingIntersection(vibrators),
|
||||
supportedPrimitivesAndDurationsIntersection(vibrators),
|
||||
integerLimitIntersection(vibrators, VibratorInfo::getPrimitiveDelayMax),
|
||||
integerLimitIntersection(vibrators, VibratorInfo::getCompositionSizeMax),
|
||||
integerLimitIntersection(vibrators, VibratorInfo::getPwlePrimitiveDurationMax),
|
||||
integerLimitIntersection(vibrators, VibratorInfo::getPwleSizeMax),
|
||||
floatPropertyIntersection(vibrators, VibratorInfo::getQFactor),
|
||||
frequencyProfileIntersection(vibrators));
|
||||
}
|
||||
|
||||
private static int capabilitiesIntersection(VibratorInfo[] infos) {
|
||||
if (infos.length == 0) {
|
||||
return 0;
|
||||
}
|
||||
int intersection = ~0;
|
||||
for (VibratorInfo info : infos) {
|
||||
intersection &= info.getCapabilities();
|
||||
}
|
||||
return intersection;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static SparseBooleanArray supportedBrakingIntersection(VibratorInfo[] infos) {
|
||||
for (VibratorInfo info : infos) {
|
||||
if (!info.isBrakingSupportKnown()) {
|
||||
// If one vibrator support is unknown, then the intersection is also unknown.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
SparseBooleanArray intersection = new SparseBooleanArray();
|
||||
SparseBooleanArray firstVibratorBraking = infos[0].getSupportedBraking();
|
||||
|
||||
brakingIdLoop:
|
||||
for (int i = 0; i < firstVibratorBraking.size(); i++) {
|
||||
int brakingId = firstVibratorBraking.keyAt(i);
|
||||
if (!firstVibratorBraking.valueAt(i)) {
|
||||
// The first vibrator already doesn't support this braking, so skip it.
|
||||
continue brakingIdLoop;
|
||||
}
|
||||
|
||||
for (int j = 1; j < infos.length; j++) {
|
||||
if (!infos[j].hasBrakingSupport(brakingId)) {
|
||||
// One vibrator doesn't support this braking, so the intersection doesn't.
|
||||
continue brakingIdLoop;
|
||||
}
|
||||
}
|
||||
|
||||
intersection.put(brakingId, true);
|
||||
}
|
||||
|
||||
return intersection;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static SparseBooleanArray supportedEffectsIntersection(VibratorInfo[] infos) {
|
||||
for (VibratorInfo info : infos) {
|
||||
if (!info.isEffectSupportKnown()) {
|
||||
// If one vibrator support is unknown, then the intersection is also unknown.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
SparseBooleanArray intersection = new SparseBooleanArray();
|
||||
SparseBooleanArray firstVibratorEffects = infos[0].getSupportedEffects();
|
||||
|
||||
effectIdLoop:
|
||||
for (int i = 0; i < firstVibratorEffects.size(); i++) {
|
||||
int effectId = firstVibratorEffects.keyAt(i);
|
||||
if (!firstVibratorEffects.valueAt(i)) {
|
||||
// The first vibrator already doesn't support this effect, so skip it.
|
||||
continue effectIdLoop;
|
||||
}
|
||||
|
||||
for (int j = 1; j < infos.length; j++) {
|
||||
if (infos[j].isEffectSupported(effectId) != VIBRATION_EFFECT_SUPPORT_YES) {
|
||||
// One vibrator doesn't support this effect, so the intersection doesn't.
|
||||
continue effectIdLoop;
|
||||
}
|
||||
}
|
||||
|
||||
intersection.put(effectId, true);
|
||||
}
|
||||
|
||||
return intersection;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private static SparseIntArray supportedPrimitivesAndDurationsIntersection(
|
||||
VibratorInfo[] infos) {
|
||||
SparseIntArray intersection = new SparseIntArray();
|
||||
SparseIntArray firstVibratorPrimitives = infos[0].getSupportedPrimitives();
|
||||
|
||||
primitiveIdLoop:
|
||||
for (int i = 0; i < firstVibratorPrimitives.size(); i++) {
|
||||
int primitiveId = firstVibratorPrimitives.keyAt(i);
|
||||
int primitiveDuration = firstVibratorPrimitives.valueAt(i);
|
||||
if (primitiveDuration == 0) {
|
||||
// The first vibrator already doesn't support this primitive, so skip it.
|
||||
continue primitiveIdLoop;
|
||||
}
|
||||
|
||||
for (int j = 1; j < infos.length; j++) {
|
||||
int vibratorPrimitiveDuration = infos[j].getPrimitiveDuration(primitiveId);
|
||||
if (vibratorPrimitiveDuration == 0) {
|
||||
// One vibrator doesn't support this primitive, so the intersection doesn't.
|
||||
continue primitiveIdLoop;
|
||||
} else {
|
||||
// The primitive vibration duration is the maximum among all vibrators.
|
||||
primitiveDuration = Math.max(primitiveDuration, vibratorPrimitiveDuration);
|
||||
}
|
||||
}
|
||||
|
||||
intersection.put(primitiveId, primitiveDuration);
|
||||
}
|
||||
return intersection;
|
||||
}
|
||||
|
||||
private static int integerLimitIntersection(VibratorInfo[] infos,
|
||||
Function<VibratorInfo, Integer> propertyGetter) {
|
||||
int limit = 0; // Limit 0 means unlimited
|
||||
for (VibratorInfo info : infos) {
|
||||
int vibratorLimit = propertyGetter.apply(info);
|
||||
if ((limit == 0) || (vibratorLimit > 0 && vibratorLimit < limit)) {
|
||||
// This vibrator is limited and intersection is unlimited or has a larger limit:
|
||||
// use smaller limit here for the intersection.
|
||||
limit = vibratorLimit;
|
||||
}
|
||||
}
|
||||
return limit;
|
||||
}
|
||||
|
||||
private static float floatPropertyIntersection(VibratorInfo[] infos,
|
||||
Function<VibratorInfo, Float> propertyGetter) {
|
||||
float property = propertyGetter.apply(infos[0]);
|
||||
if (Float.isNaN(property)) {
|
||||
// If one vibrator is undefined then the intersection is undefined.
|
||||
return Float.NaN;
|
||||
}
|
||||
for (int i = 1; i < infos.length; i++) {
|
||||
if (Float.compare(property, propertyGetter.apply(infos[i])) != 0) {
|
||||
// If one vibrator has a different value then the intersection is undefined.
|
||||
return Float.NaN;
|
||||
}
|
||||
}
|
||||
return property;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private static FrequencyProfile frequencyProfileIntersection(VibratorInfo[] infos) {
|
||||
float freqResolution = floatPropertyIntersection(infos,
|
||||
info -> info.getFrequencyProfile().getFrequencyResolutionHz());
|
||||
float resonantFreq = floatPropertyIntersection(infos,
|
||||
VibratorInfo::getResonantFrequencyHz);
|
||||
Range<Float> freqRange = frequencyRangeIntersection(infos, freqResolution);
|
||||
|
||||
if ((freqRange == null) || Float.isNaN(freqResolution)) {
|
||||
return new FrequencyProfile(resonantFreq, Float.NaN, freqResolution, null);
|
||||
}
|
||||
|
||||
int amplitudeCount =
|
||||
Math.round(1 + (freqRange.getUpper() - freqRange.getLower()) / freqResolution);
|
||||
float[] maxAmplitudes = new float[amplitudeCount];
|
||||
|
||||
// Use MAX_VALUE here to ensure that the FrequencyProfile constructor called with this
|
||||
// will fail if the loop below is broken and do not replace filled values with actual
|
||||
// vibrator measurements.
|
||||
Arrays.fill(maxAmplitudes, Float.MAX_VALUE);
|
||||
|
||||
for (VibratorInfo info : infos) {
|
||||
Range<Float> vibratorFreqRange = info.getFrequencyProfile().getFrequencyRangeHz();
|
||||
float[] vibratorMaxAmplitudes = info.getFrequencyProfile().getMaxAmplitudes();
|
||||
int vibratorStartIdx = Math.round(
|
||||
(freqRange.getLower() - vibratorFreqRange.getLower()) / freqResolution);
|
||||
int vibratorEndIdx = vibratorStartIdx + maxAmplitudes.length - 1;
|
||||
|
||||
if ((vibratorStartIdx < 0) || (vibratorEndIdx >= vibratorMaxAmplitudes.length)) {
|
||||
Slog.w(TAG, "Error calculating the intersection of vibrator frequency"
|
||||
+ " profiles: attempted to fetch from vibrator "
|
||||
+ info.getId() + " max amplitude with bad index " + vibratorStartIdx);
|
||||
return new FrequencyProfile(resonantFreq, Float.NaN, Float.NaN, null);
|
||||
}
|
||||
|
||||
for (int i = 0; i < maxAmplitudes.length; i++) {
|
||||
maxAmplitudes[i] = Math.min(maxAmplitudes[i],
|
||||
vibratorMaxAmplitudes[vibratorStartIdx + i]);
|
||||
}
|
||||
}
|
||||
|
||||
return new FrequencyProfile(resonantFreq, freqRange.getLower(),
|
||||
freqResolution, maxAmplitudes);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static Range<Float> frequencyRangeIntersection(VibratorInfo[] infos,
|
||||
float frequencyResolution) {
|
||||
Range<Float> firstRange = infos[0].getFrequencyProfile().getFrequencyRangeHz();
|
||||
if (firstRange == null) {
|
||||
// If one vibrator is undefined then the intersection is undefined.
|
||||
return null;
|
||||
}
|
||||
float intersectionLower = firstRange.getLower();
|
||||
float intersectionUpper = firstRange.getUpper();
|
||||
|
||||
// Generate the intersection of all vibrator supported ranges, making sure that both
|
||||
// min supported frequencies are aligned w.r.t. the frequency resolution.
|
||||
|
||||
for (int i = 1; i < infos.length; i++) {
|
||||
Range<Float> vibratorRange = infos[i].getFrequencyProfile().getFrequencyRangeHz();
|
||||
if (vibratorRange == null) {
|
||||
// If one vibrator is undefined then the intersection is undefined.
|
||||
return null;
|
||||
}
|
||||
|
||||
if ((vibratorRange.getLower() >= intersectionUpper)
|
||||
|| (vibratorRange.getUpper() <= intersectionLower)) {
|
||||
// If the range and intersection are disjoint then the intersection is undefined
|
||||
return null;
|
||||
}
|
||||
|
||||
float frequencyDelta = Math.abs(intersectionLower - vibratorRange.getLower());
|
||||
if ((frequencyDelta % frequencyResolution) > EPSILON) {
|
||||
// If the intersection is not aligned with one vibrator then it's undefined
|
||||
return null;
|
||||
}
|
||||
|
||||
intersectionLower = Math.max(intersectionLower, vibratorRange.getLower());
|
||||
intersectionUpper = Math.min(intersectionUpper, vibratorRange.getUpper());
|
||||
}
|
||||
|
||||
if ((intersectionUpper - intersectionLower) < frequencyResolution) {
|
||||
// If the intersection is empty then it's undefined.
|
||||
return null;
|
||||
}
|
||||
|
||||
return Range.create(intersectionLower, intersectionUpper);
|
||||
}
|
||||
}
|
||||
|
||||
/** Listener for all vibrators state change. */
|
||||
|
||||
@@ -31,6 +31,7 @@ import android.content.res.Resources;
|
||||
import android.hardware.vibrator.IVibrator;
|
||||
import android.media.AudioAttributes;
|
||||
import android.os.vibrator.VibrationConfig;
|
||||
import android.os.vibrator.VibratorFrequencyProfile;
|
||||
import android.util.Log;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
@@ -208,8 +209,8 @@ public abstract class Vibrator {
|
||||
/**
|
||||
* Check whether the vibrator has independent frequency control.
|
||||
*
|
||||
* @return True if the hardware can control the frequency of the vibrations, otherwise false.
|
||||
* @hide
|
||||
* @return True if the hardware can control the frequency of the vibrations independently of
|
||||
* the vibration amplitude, false otherwise.
|
||||
*/
|
||||
public boolean hasFrequencyControl() {
|
||||
// We currently can only control frequency of the vibration using the compose PWLE method.
|
||||
@@ -229,27 +230,47 @@ public abstract class Vibrator {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the resonant frequency of the vibrator.
|
||||
* Gets the resonant frequency of the vibrator, if applicable.
|
||||
*
|
||||
* @return the resonant frequency of the vibrator, or {@link Float#NaN NaN} if it's unknown or
|
||||
* this vibrator is a composite of multiple physical devices.
|
||||
* @hide
|
||||
* @return the resonant frequency of the vibrator, or {@link Float#NaN NaN} if it's unknown, not
|
||||
* applicable, or if this vibrator is a composite of multiple physical devices with different
|
||||
* frequencies.
|
||||
*/
|
||||
public float getResonantFrequency() {
|
||||
return getInfo().getResonantFrequency();
|
||||
return getInfo().getResonantFrequencyHz();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the <a href="https://en.wikipedia.org/wiki/Q_factor">Q factor</a> of the vibrator.
|
||||
*
|
||||
* @return the Q factor of the vibrator, or {@link Float#NaN NaN} if it's unknown or
|
||||
* this vibrator is a composite of multiple physical devices.
|
||||
* @hide
|
||||
* @return the Q factor of the vibrator, or {@link Float#NaN NaN} if it's unknown, not
|
||||
* applicable, or if this vibrator is a composite of multiple physical devices with different
|
||||
* Q factors.
|
||||
*/
|
||||
public float getQFactor() {
|
||||
return getInfo().getQFactor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the profile that describes the vibrator output across the supported frequency range.
|
||||
*
|
||||
* <p>The profile describes the relative output acceleration that the device can reach when it
|
||||
* vibrates at different frequencies.
|
||||
*
|
||||
* @return The frequency profile for this vibrator, or null if the vibrator does not have
|
||||
* frequency control. If this vibrator is a composite of multiple physical devices then this
|
||||
* will return a profile supported in all devices, or null if the intersection is empty or not
|
||||
* available.
|
||||
*/
|
||||
@Nullable
|
||||
public VibratorFrequencyProfile getFrequencyProfile() {
|
||||
VibratorInfo.FrequencyProfile frequencyProfile = getInfo().getFrequencyProfile();
|
||||
if (frequencyProfile.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return new VibratorFrequencyProfile(frequencyProfile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the maximum amplitude the vibrator can play using the audio haptic channels.
|
||||
*
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
package android.os;
|
||||
|
||||
import android.annotation.FloatRange;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.hardware.vibrator.Braking;
|
||||
@@ -26,6 +25,8 @@ import android.util.Range;
|
||||
import android.util.SparseBooleanArray;
|
||||
import android.util.SparseIntArray;
|
||||
|
||||
import com.android.internal.util.Preconditions;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
@@ -56,7 +57,7 @@ public class VibratorInfo implements Parcelable {
|
||||
private final int mPwlePrimitiveDurationMax;
|
||||
private final int mPwleSizeMax;
|
||||
private final float mQFactor;
|
||||
private final FrequencyMapping mFrequencyMapping;
|
||||
private final FrequencyProfile mFrequencyProfile;
|
||||
|
||||
VibratorInfo(Parcel in) {
|
||||
mId = in.readInt();
|
||||
@@ -69,7 +70,15 @@ public class VibratorInfo implements Parcelable {
|
||||
mPwlePrimitiveDurationMax = in.readInt();
|
||||
mPwleSizeMax = in.readInt();
|
||||
mQFactor = in.readFloat();
|
||||
mFrequencyMapping = in.readParcelable(VibratorInfo.class.getClassLoader(), android.os.VibratorInfo.FrequencyMapping.class);
|
||||
mFrequencyProfile = FrequencyProfile.CREATOR.createFromParcel(in);
|
||||
}
|
||||
|
||||
public VibratorInfo(int id, @NonNull VibratorInfo baseVibratorInfo) {
|
||||
this(id, baseVibratorInfo.mCapabilities, baseVibratorInfo.mSupportedEffects,
|
||||
baseVibratorInfo.mSupportedBraking, baseVibratorInfo.mSupportedPrimitives,
|
||||
baseVibratorInfo.mPrimitiveDelayMax, baseVibratorInfo.mCompositionSizeMax,
|
||||
baseVibratorInfo.mPwlePrimitiveDurationMax, baseVibratorInfo.mPwleSizeMax,
|
||||
baseVibratorInfo.mQFactor, baseVibratorInfo.mFrequencyProfile);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -92,7 +101,7 @@ public class VibratorInfo implements Parcelable {
|
||||
* @param pwleSizeMax The maximum number of primitives supported by a PWLE
|
||||
* composition.
|
||||
* @param qFactor The vibrator quality factor.
|
||||
* @param frequencyMapping The description of the vibrator supported frequencies and max
|
||||
* @param frequencyProfile The description of the vibrator supported frequencies and max
|
||||
* amplitude mappings.
|
||||
* @hide
|
||||
*/
|
||||
@@ -100,7 +109,9 @@ public class VibratorInfo implements Parcelable {
|
||||
@Nullable SparseBooleanArray supportedBraking,
|
||||
@NonNull SparseIntArray supportedPrimitives, int primitiveDelayMax,
|
||||
int compositionSizeMax, int pwlePrimitiveDurationMax, int pwleSizeMax,
|
||||
float qFactor, @NonNull FrequencyMapping frequencyMapping) {
|
||||
float qFactor, @NonNull FrequencyProfile frequencyProfile) {
|
||||
Preconditions.checkNotNull(supportedPrimitives);
|
||||
Preconditions.checkNotNull(frequencyProfile);
|
||||
mId = id;
|
||||
mCapabilities = capabilities;
|
||||
mSupportedEffects = supportedEffects == null ? null : supportedEffects.clone();
|
||||
@@ -111,14 +122,7 @@ public class VibratorInfo implements Parcelable {
|
||||
mPwlePrimitiveDurationMax = pwlePrimitiveDurationMax;
|
||||
mPwleSizeMax = pwleSizeMax;
|
||||
mQFactor = qFactor;
|
||||
mFrequencyMapping = frequencyMapping;
|
||||
}
|
||||
|
||||
protected VibratorInfo(int id, int capabilities, VibratorInfo baseVibrator) {
|
||||
this(id, capabilities, baseVibrator.mSupportedEffects, baseVibrator.mSupportedBraking,
|
||||
baseVibrator.mSupportedPrimitives, baseVibrator.mPrimitiveDelayMax,
|
||||
baseVibrator.mCompositionSizeMax, baseVibrator.mPwlePrimitiveDurationMax,
|
||||
baseVibrator.mPwleSizeMax, baseVibrator.mQFactor, baseVibrator.mFrequencyMapping);
|
||||
mFrequencyProfile = frequencyProfile;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -133,7 +137,7 @@ public class VibratorInfo implements Parcelable {
|
||||
dest.writeInt(mPwlePrimitiveDurationMax);
|
||||
dest.writeInt(mPwleSizeMax);
|
||||
dest.writeFloat(mQFactor);
|
||||
dest.writeParcelable(mFrequencyMapping, flags);
|
||||
mFrequencyProfile.writeToParcel(dest, flags);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -170,13 +174,13 @@ public class VibratorInfo implements Parcelable {
|
||||
&& Objects.equals(mSupportedEffects, that.mSupportedEffects)
|
||||
&& Objects.equals(mSupportedBraking, that.mSupportedBraking)
|
||||
&& Objects.equals(mQFactor, that.mQFactor)
|
||||
&& Objects.equals(mFrequencyMapping, that.mFrequencyMapping);
|
||||
&& Objects.equals(mFrequencyProfile, that.mFrequencyProfile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hashCode = Objects.hash(mId, mCapabilities, mSupportedEffects, mSupportedBraking,
|
||||
mQFactor, mFrequencyMapping);
|
||||
mQFactor, mFrequencyProfile);
|
||||
for (int i = 0; i < mSupportedPrimitives.size(); i++) {
|
||||
hashCode = 31 * hashCode + mSupportedPrimitives.keyAt(i);
|
||||
hashCode = 31 * hashCode + mSupportedPrimitives.valueAt(i);
|
||||
@@ -198,7 +202,7 @@ public class VibratorInfo implements Parcelable {
|
||||
+ ", mPwlePrimitiveDurationMax=" + mPwlePrimitiveDurationMax
|
||||
+ ", mPwleSizeMax=" + mPwleSizeMax
|
||||
+ ", mQFactor=" + mQFactor
|
||||
+ ", mFrequencyMapping=" + mFrequencyMapping
|
||||
+ ", mFrequencyProfile=" + mFrequencyProfile
|
||||
+ '}';
|
||||
}
|
||||
|
||||
@@ -234,6 +238,30 @@ public class VibratorInfo implements Parcelable {
|
||||
return Braking.NONE;
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
@Nullable
|
||||
public SparseBooleanArray getSupportedBraking() {
|
||||
if (mSupportedBraking == null) {
|
||||
return null;
|
||||
}
|
||||
return mSupportedBraking.clone();
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public boolean isBrakingSupportKnown() {
|
||||
return mSupportedBraking != null;
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public boolean hasBrakingSupport(@Braking int braking) {
|
||||
return (mSupportedBraking != null) && mSupportedBraking.get(braking);
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public boolean isEffectSupportKnown() {
|
||||
return mSupportedEffects != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Query whether the vibrator supports the given effect.
|
||||
*
|
||||
@@ -252,6 +280,15 @@ public class VibratorInfo implements Parcelable {
|
||||
: Vibrator.VIBRATION_EFFECT_SUPPORT_NO;
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
@Nullable
|
||||
public SparseBooleanArray getSupportedEffects() {
|
||||
if (mSupportedEffects == null) {
|
||||
return null;
|
||||
}
|
||||
return mSupportedEffects.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* Query whether the vibrator supports the given primitive.
|
||||
*
|
||||
@@ -276,6 +313,11 @@ public class VibratorInfo implements Parcelable {
|
||||
return mSupportedPrimitives.get(primitiveId);
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public SparseIntArray getSupportedPrimitives() {
|
||||
return mSupportedPrimitives.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* Query the maximum delay supported for a primitive in a composed effect.
|
||||
*
|
||||
@@ -329,8 +371,8 @@ public class VibratorInfo implements Parcelable {
|
||||
* @return the resonant frequency of the vibrator, or {@link Float#NaN NaN} if it's unknown or
|
||||
* this vibrator is a composite of multiple physical devices.
|
||||
*/
|
||||
public float getResonantFrequency() {
|
||||
return mFrequencyMapping.mResonantFrequencyHz;
|
||||
public float getResonantFrequencyHz() {
|
||||
return mFrequencyProfile.mResonantFrequencyHz;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -344,31 +386,14 @@ public class VibratorInfo implements Parcelable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a range of frequency values supported by the vibrator.
|
||||
* Gets the profile of supported frequencies, including the measurements of maximum relative
|
||||
* output acceleration for supported vibration frequencies.
|
||||
*
|
||||
* @return A range of frequency values supported, in hertz. The range will always contain the
|
||||
* device resonant frequency. Devices without frequency control will return null.
|
||||
* @hide
|
||||
* <p>If the devices does not have frequency control then the profile should be empty.
|
||||
*/
|
||||
@Nullable
|
||||
public Range<Float> getFrequencyRangeHz() {
|
||||
return mFrequencyMapping.mFrequencyRangeHz;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the maximum amplitude the vibrator can play at given frequency.
|
||||
*
|
||||
* @param frequencyHz The frequency, in hertz, for query.
|
||||
|
||||
* @return a value in [0,1] representing the maximum amplitude the device can play at given
|
||||
* frequency. Devices without frequency control will return 0 to any input. Devices with
|
||||
* frequency control will return the supported value, for input in
|
||||
* {@link #getFrequencyRangeHz()}, and 0 for any other input.
|
||||
* @hide
|
||||
*/
|
||||
@FloatRange(from = 0, to = 1)
|
||||
public float getMaxAmplitude(float frequencyHz) {
|
||||
return mFrequencyMapping.getMaxAmplitude(frequencyHz);
|
||||
@NonNull
|
||||
public FrequencyProfile getFrequencyProfile() {
|
||||
return mFrequencyProfile;
|
||||
}
|
||||
|
||||
protected long getCapabilities() {
|
||||
@@ -452,7 +477,7 @@ public class VibratorInfo implements Parcelable {
|
||||
* Describes the maximum relative output acceleration that can be achieved for each supported
|
||||
* frequency in a specific vibrator.
|
||||
*
|
||||
* <p>This mapping is defined by the following parameters:
|
||||
* <p>This profile is defined by the following parameters:
|
||||
*
|
||||
* <ol>
|
||||
* <li>{@code minFrequencyHz}, {@code resonantFrequencyHz} and {@code frequencyResolutionHz}
|
||||
@@ -466,7 +491,7 @@ public class VibratorInfo implements Parcelable {
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public static final class FrequencyMapping implements Parcelable {
|
||||
public static final class FrequencyProfile implements Parcelable {
|
||||
@Nullable
|
||||
private final Range<Float> mFrequencyRangeHz;
|
||||
private final float mMinFrequencyHz;
|
||||
@@ -474,7 +499,7 @@ public class VibratorInfo implements Parcelable {
|
||||
private final float mFrequencyResolutionHz;
|
||||
private final float[] mMaxAmplitudes;
|
||||
|
||||
FrequencyMapping(Parcel in) {
|
||||
FrequencyProfile(Parcel in) {
|
||||
this(in.readFloat(), in.readFloat(), in.readFloat(), in.createFloatArray());
|
||||
}
|
||||
|
||||
@@ -484,13 +509,13 @@ public class VibratorInfo implements Parcelable {
|
||||
* @param resonantFrequencyHz The vibrator resonant frequency, in hertz.
|
||||
* @param minFrequencyHz Minimum supported frequency, in hertz.
|
||||
* @param frequencyResolutionHz The frequency resolution, in hertz, used by the max
|
||||
* amplitudes mapping.
|
||||
* amplitude measurements.
|
||||
* @param maxAmplitudes The max amplitude supported by each supported frequency,
|
||||
* starting at minimum frequency with jumps of frequency
|
||||
* resolution.
|
||||
* @hide
|
||||
*/
|
||||
public FrequencyMapping(float resonantFrequencyHz, float minFrequencyHz,
|
||||
public FrequencyProfile(float resonantFrequencyHz, float minFrequencyHz,
|
||||
float frequencyResolutionHz, float[] maxAmplitudes) {
|
||||
mMinFrequencyHz = minFrequencyHz;
|
||||
mResonantFrequencyHz = resonantFrequencyHz;
|
||||
@@ -500,18 +525,25 @@ public class VibratorInfo implements Parcelable {
|
||||
System.arraycopy(maxAmplitudes, 0, mMaxAmplitudes, 0, maxAmplitudes.length);
|
||||
}
|
||||
|
||||
// If any required field is undefined then leave this mapping empty.
|
||||
// If any required field is undefined or has a bad value then this profile is invalid.
|
||||
boolean isValid = !Float.isNaN(resonantFrequencyHz)
|
||||
&& (resonantFrequencyHz > 0)
|
||||
&& !Float.isNaN(minFrequencyHz)
|
||||
&& (minFrequencyHz > 0)
|
||||
&& !Float.isNaN(frequencyResolutionHz)
|
||||
&& (frequencyResolutionHz > 0)
|
||||
&& (mMaxAmplitudes.length > 0);
|
||||
|
||||
// If any max amplitude is outside the allowed range then this profile is invalid.
|
||||
for (int i = 0; i < mMaxAmplitudes.length; i++) {
|
||||
isValid &= (mMaxAmplitudes[i] >= 0) && (mMaxAmplitudes[i] <= 1);
|
||||
}
|
||||
|
||||
float maxFrequencyHz = isValid
|
||||
? minFrequencyHz + frequencyResolutionHz * (mMaxAmplitudes.length - 1)
|
||||
: Float.NaN;
|
||||
|
||||
// If the non-empty mapping does not have min < resonant < max frequency respected
|
||||
// then leave this mapping empty.
|
||||
// If the constraint min < resonant < max is not met then it is invalid.
|
||||
isValid &= !Float.isNaN(maxFrequencyHz)
|
||||
&& (resonantFrequencyHz >= minFrequencyHz)
|
||||
&& (resonantFrequencyHz <= maxFrequencyHz)
|
||||
@@ -520,14 +552,17 @@ public class VibratorInfo implements Parcelable {
|
||||
mFrequencyRangeHz = isValid ? Range.create(minFrequencyHz, maxFrequencyHz) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this frequency mapping is empty, i.e. the only supported is the resonant
|
||||
* frequency.
|
||||
*/
|
||||
/** Returns true if the supported frequency range is empty. */
|
||||
public boolean isEmpty() {
|
||||
return mFrequencyRangeHz == null;
|
||||
}
|
||||
|
||||
/** Returns the supported frequency range, in hertz. */
|
||||
@Nullable
|
||||
public Range<Float> getFrequencyRangeHz() {
|
||||
return mFrequencyRangeHz;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the maximum relative amplitude the vibrator can reach while playing at the
|
||||
* given frequency.
|
||||
@@ -535,7 +570,7 @@ public class VibratorInfo implements Parcelable {
|
||||
* @param frequencyHz frequency, in hertz, for query.
|
||||
* @return A value in [0,1] representing the max relative amplitude supported at the given
|
||||
* frequency. This will return 0 if the frequency is outside the supported range, or if the
|
||||
* mapping is empty.
|
||||
* supported frequency range is empty.
|
||||
*/
|
||||
public float getMaxAmplitude(float frequencyHz) {
|
||||
if (isEmpty() || Float.isNaN(frequencyHz)) {
|
||||
@@ -555,6 +590,17 @@ public class VibratorInfo implements Parcelable {
|
||||
return mMaxAmplitudes[floorIndex];
|
||||
}
|
||||
|
||||
/** Returns the raw list of maximum relative output accelerations from the vibrator. */
|
||||
@NonNull
|
||||
public float[] getMaxAmplitudes() {
|
||||
return Arrays.copyOf(mMaxAmplitudes, mMaxAmplitudes.length);
|
||||
}
|
||||
|
||||
/** Returns the raw frequency resolution used for max amplitude measurements, in hertz. */
|
||||
public float getFrequencyResolutionHz() {
|
||||
return mFrequencyResolutionHz;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeFloat(mResonantFrequencyHz);
|
||||
@@ -573,10 +619,10 @@ public class VibratorInfo implements Parcelable {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (!(o instanceof FrequencyMapping)) {
|
||||
if (!(o instanceof FrequencyProfile)) {
|
||||
return false;
|
||||
}
|
||||
FrequencyMapping that = (FrequencyMapping) o;
|
||||
FrequencyProfile that = (FrequencyProfile) o;
|
||||
return Float.compare(mMinFrequencyHz, that.mMinFrequencyHz) == 0
|
||||
&& Float.compare(mResonantFrequencyHz, that.mResonantFrequencyHz) == 0
|
||||
&& Float.compare(mFrequencyResolutionHz, that.mFrequencyResolutionHz) == 0
|
||||
@@ -593,7 +639,7 @@ public class VibratorInfo implements Parcelable {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "FrequencyMapping{"
|
||||
return "FrequencyProfile{"
|
||||
+ "mFrequencyRange=" + mFrequencyRangeHz
|
||||
+ ", mMinFrequency=" + mMinFrequencyHz
|
||||
+ ", mResonantFrequency=" + mResonantFrequencyHz
|
||||
@@ -603,16 +649,16 @@ public class VibratorInfo implements Parcelable {
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static final Creator<FrequencyMapping> CREATOR =
|
||||
new Creator<FrequencyMapping>() {
|
||||
public static final Creator<FrequencyProfile> CREATOR =
|
||||
new Creator<FrequencyProfile>() {
|
||||
@Override
|
||||
public FrequencyMapping createFromParcel(Parcel in) {
|
||||
return new FrequencyMapping(in);
|
||||
public FrequencyProfile createFromParcel(Parcel in) {
|
||||
return new FrequencyProfile(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FrequencyMapping[] newArray(int size) {
|
||||
return new FrequencyMapping[size];
|
||||
public FrequencyProfile[] newArray(int size) {
|
||||
return new FrequencyProfile[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -629,8 +675,8 @@ public class VibratorInfo implements Parcelable {
|
||||
private int mPwlePrimitiveDurationMax;
|
||||
private int mPwleSizeMax;
|
||||
private float mQFactor = Float.NaN;
|
||||
private FrequencyMapping mFrequencyMapping =
|
||||
new FrequencyMapping(Float.NaN, Float.NaN, Float.NaN, null);
|
||||
private FrequencyProfile mFrequencyProfile =
|
||||
new FrequencyProfile(Float.NaN, Float.NaN, Float.NaN, null);
|
||||
|
||||
/** A builder class for a {@link VibratorInfo}. */
|
||||
public Builder(int id) {
|
||||
@@ -702,8 +748,8 @@ public class VibratorInfo implements Parcelable {
|
||||
|
||||
/** Configure the vibrator frequency information like resonant frequency and bandwidth. */
|
||||
@NonNull
|
||||
public Builder setFrequencyMapping(FrequencyMapping frequencyMapping) {
|
||||
mFrequencyMapping = frequencyMapping;
|
||||
public Builder setFrequencyProfile(@NonNull FrequencyProfile frequencyProfile) {
|
||||
mFrequencyProfile = frequencyProfile;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -712,7 +758,7 @@ public class VibratorInfo implements Parcelable {
|
||||
public VibratorInfo build() {
|
||||
return new VibratorInfo(mId, mCapabilities, mSupportedEffects, mSupportedBraking,
|
||||
mSupportedPrimitives, mPrimitiveDelayMax, mCompositionSizeMax,
|
||||
mPwlePrimitiveDurationMax, mPwleSizeMax, mQFactor, mFrequencyMapping);
|
||||
mPwlePrimitiveDurationMax, mPwleSizeMax, mQFactor, mFrequencyProfile);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
100
core/java/android/os/vibrator/VibratorFrequencyProfile.java
Normal file
100
core/java/android/os/vibrator/VibratorFrequencyProfile.java
Normal file
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.FloatRange;
|
||||
import android.annotation.NonNull;
|
||||
import android.os.VibratorInfo;
|
||||
|
||||
import com.android.internal.util.Preconditions;
|
||||
|
||||
/**
|
||||
* Describes the output of a {@link android.os.Vibrator} for different vibration frequencies.
|
||||
*
|
||||
* <p>The profile contains the minimum and maximum supported vibration frequencies, if the device
|
||||
* supports independent frequency control.
|
||||
*
|
||||
* <p>It also describes the relative output acceleration of a vibration at different supported
|
||||
* frequencies. The acceleration is defined by a relative amplitude value between 0 and 1,
|
||||
* inclusive, where 0 represents the vibrator off state and 1 represents the maximum output
|
||||
* acceleration that the vibrator can reach across all supported frequencies.
|
||||
*
|
||||
* <p>The measurements are returned as an array of uniformly distributed amplitude values for
|
||||
* frequencies between the minimum and maximum supported ones. The measurement interval is the
|
||||
* frequency increment between each pair of amplitude values.
|
||||
*
|
||||
* <p>Vibrators without independent frequency control do not have a frequency profile.
|
||||
*/
|
||||
public final class VibratorFrequencyProfile {
|
||||
|
||||
private final VibratorInfo.FrequencyProfile mFrequencyProfile;
|
||||
|
||||
/** @hide */
|
||||
public VibratorFrequencyProfile(@NonNull VibratorInfo.FrequencyProfile frequencyProfile) {
|
||||
Preconditions.checkArgument(!frequencyProfile.isEmpty(),
|
||||
"Frequency profile must have a non-empty frequency range");
|
||||
mFrequencyProfile = frequencyProfile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Measurements of the maximum relative amplitude the vibrator can achieve for each supported
|
||||
* frequency.
|
||||
*
|
||||
* <p>The frequency of a measurement is determined as:
|
||||
*
|
||||
* {@code getMinFrequency() + measurementIndex * getMaxAmplitudeMeasurementInterval()}
|
||||
*
|
||||
* <p>The returned list will not be empty, and will have entries representing frequencies from
|
||||
* {@link #getMinFrequency()} to {@link #getMaxFrequency()}, inclusive.
|
||||
*
|
||||
* @return Array of maximum relative amplitude measurements, each value is between 0 and 1,
|
||||
* inclusive.
|
||||
*/
|
||||
@NonNull
|
||||
@FloatRange(from = 0, to = 1)
|
||||
public float[] getMaxAmplitudeMeasurements() {
|
||||
// VibratorInfo getters always return a copy or clone of the data objects.
|
||||
return mFrequencyProfile.getMaxAmplitudes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the frequency interval used to measure the maximum relative amplitudes.
|
||||
*
|
||||
* @return the frequency interval used for the measurement, in hertz.
|
||||
*/
|
||||
public float getMaxAmplitudeMeasurementInterval() {
|
||||
return mFrequencyProfile.getFrequencyResolutionHz();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the minimum frequency supported by the vibrator.
|
||||
*
|
||||
* @return the minimum frequency supported by the vibrator, in hertz.
|
||||
*/
|
||||
public float getMinFrequency() {
|
||||
return mFrequencyProfile.getFrequencyRangeHz().getLower();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the maximum frequency supported by the vibrator.
|
||||
*
|
||||
* @return the maximum frequency supported by the vibrator, in hertz.
|
||||
*/
|
||||
public float getMaxFrequency() {
|
||||
return mFrequencyProfile.getFrequencyRangeHz().getUpper();
|
||||
}
|
||||
}
|
||||
@@ -19,13 +19,11 @@ package android.os;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import android.hardware.vibrator.Braking;
|
||||
import android.hardware.vibrator.IVibrator;
|
||||
import android.platform.test.annotations.Presubmit;
|
||||
import android.util.Range;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
@@ -43,10 +41,10 @@ public class VibratorInfoTest {
|
||||
private static final float[] TEST_AMPLITUDE_MAP = new float[]{
|
||||
/* 50Hz= */ 0.1f, 0.2f, 0.4f, 0.8f, /* 150Hz= */ 1f, 0.9f, /* 200Hz= */ 0.8f};
|
||||
|
||||
private static final VibratorInfo.FrequencyMapping EMPTY_FREQUENCY_MAPPING =
|
||||
new VibratorInfo.FrequencyMapping(Float.NaN, Float.NaN, Float.NaN, null);
|
||||
private static final VibratorInfo.FrequencyMapping TEST_FREQUENCY_MAPPING =
|
||||
new VibratorInfo.FrequencyMapping(TEST_RESONANT_FREQUENCY, TEST_MIN_FREQUENCY,
|
||||
private static final VibratorInfo.FrequencyProfile EMPTY_FREQUENCY_PROFILE =
|
||||
new VibratorInfo.FrequencyProfile(Float.NaN, Float.NaN, Float.NaN, null);
|
||||
private static final VibratorInfo.FrequencyProfile TEST_FREQUENCY_PROFILE =
|
||||
new VibratorInfo.FrequencyProfile(TEST_RESONANT_FREQUENCY, TEST_MIN_FREQUENCY,
|
||||
TEST_FREQUENCY_RESOLUTION, TEST_AMPLITUDE_MAP);
|
||||
|
||||
@Test
|
||||
@@ -142,95 +140,76 @@ public class VibratorInfoTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetFrequencyRangeHz_invalidFrequencyMappingReturnsNull() {
|
||||
public void testGetFrequencyProfile_unsetProfileIsEmpty() {
|
||||
assertTrue(
|
||||
new VibratorInfo.Builder(TEST_VIBRATOR_ID).build().getFrequencyProfile().isEmpty());
|
||||
}
|
||||
@Test
|
||||
public void testFrequencyProfile_invalidValuesCreatesEmptyProfile() {
|
||||
// Invalid, contains NaN values or empty array.
|
||||
assertNull(new VibratorInfo.Builder(TEST_VIBRATOR_ID).build().getFrequencyRangeHz());
|
||||
assertNull(new VibratorInfo.Builder(TEST_VIBRATOR_ID)
|
||||
.setFrequencyMapping(new VibratorInfo.FrequencyMapping(
|
||||
Float.NaN, 50, 25, TEST_AMPLITUDE_MAP))
|
||||
.build().getFrequencyRangeHz());
|
||||
assertNull(new VibratorInfo.Builder(TEST_VIBRATOR_ID)
|
||||
.setFrequencyMapping(new VibratorInfo.FrequencyMapping(
|
||||
150, Float.NaN, 25, TEST_AMPLITUDE_MAP))
|
||||
.build().getFrequencyRangeHz());
|
||||
assertNull(new VibratorInfo.Builder(TEST_VIBRATOR_ID)
|
||||
.setFrequencyMapping(new VibratorInfo.FrequencyMapping(
|
||||
150, 50, Float.NaN, TEST_AMPLITUDE_MAP))
|
||||
.build().getFrequencyRangeHz());
|
||||
assertNull(new VibratorInfo.Builder(TEST_VIBRATOR_ID)
|
||||
.setFrequencyMapping(new VibratorInfo.FrequencyMapping(150, 50, 25, null))
|
||||
.build().getFrequencyRangeHz());
|
||||
assertTrue(new VibratorInfo.FrequencyProfile(
|
||||
Float.NaN, 50, 25, TEST_AMPLITUDE_MAP).isEmpty());
|
||||
assertTrue(new VibratorInfo.FrequencyProfile(
|
||||
150, Float.NaN, 25, TEST_AMPLITUDE_MAP).isEmpty());
|
||||
assertTrue(new VibratorInfo.FrequencyProfile(
|
||||
150, 50, Float.NaN, TEST_AMPLITUDE_MAP).isEmpty());
|
||||
assertTrue(new VibratorInfo.FrequencyProfile(150, 50, 25, null).isEmpty());
|
||||
// Invalid, contains zero or negative frequency values.
|
||||
assertTrue(new VibratorInfo.FrequencyProfile(-1, 50, 25, TEST_AMPLITUDE_MAP).isEmpty());
|
||||
assertTrue(new VibratorInfo.FrequencyProfile(150, 0, 25, TEST_AMPLITUDE_MAP).isEmpty());
|
||||
assertTrue(new VibratorInfo.FrequencyProfile(150, 50, -2, TEST_AMPLITUDE_MAP).isEmpty());
|
||||
// Invalid max amplitude entries.
|
||||
assertTrue(new VibratorInfo.FrequencyProfile(
|
||||
150, 50, 50, new float[] { -1, 0, 1, 1, 0 }).isEmpty());
|
||||
assertTrue(new VibratorInfo.FrequencyProfile(
|
||||
150, 50, 50, new float[] { 0, 1, 2, 1, 0 }).isEmpty());
|
||||
// Invalid, minFrequency > resonantFrequency
|
||||
assertNull(new VibratorInfo.Builder(TEST_VIBRATOR_ID)
|
||||
.setFrequencyMapping(new VibratorInfo.FrequencyMapping(
|
||||
/* resonantFrequencyHz= */ 150, /* minFrequencyHz= */ 250, 25, null))
|
||||
.build().getFrequencyRangeHz());
|
||||
assertTrue(new VibratorInfo.FrequencyProfile(
|
||||
/* resonantFrequencyHz= */ 150, /* minFrequencyHz= */ 250, 25, TEST_AMPLITUDE_MAP)
|
||||
.isEmpty());
|
||||
// Invalid, maxFrequency < resonantFrequency by changing resolution.
|
||||
assertNull(new VibratorInfo.Builder(TEST_VIBRATOR_ID)
|
||||
.setFrequencyMapping(new VibratorInfo.FrequencyMapping(
|
||||
150, 50, /* frequencyResolutionHz= */ 10, null))
|
||||
.build().getFrequencyRangeHz());
|
||||
assertTrue(new VibratorInfo.FrequencyProfile(
|
||||
150, 50, /* frequencyResolutionHz= */ 10, TEST_AMPLITUDE_MAP).isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetFrequencyRangeHz_resultRangeDerivedFromHalMapping() {
|
||||
VibratorInfo info = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
|
||||
.setFrequencyMapping(new VibratorInfo.FrequencyMapping(
|
||||
/* resonantFrequencyHz= */ 150,
|
||||
/* minFrequencyHz= */ 50,
|
||||
/* frequencyResolutionHz= */ 25,
|
||||
new float[]{
|
||||
/* 50Hz= */ 0.1f, 0.2f, 0.4f, 0.8f, /* 150Hz= */ 1f, 0.9f,
|
||||
/* 200Hz= */ 0.8f}))
|
||||
.build();
|
||||
public void testGetMaxAmplitude_emptyProfileReturnsAlwaysZero() {
|
||||
VibratorInfo.FrequencyProfile profile = EMPTY_FREQUENCY_PROFILE;
|
||||
assertEquals(0f, profile.getMaxAmplitude(Float.NaN), TEST_TOLERANCE);
|
||||
assertEquals(0f, profile.getMaxAmplitude(100f), TEST_TOLERANCE);
|
||||
assertEquals(0f, profile.getMaxAmplitude(200f), TEST_TOLERANCE);
|
||||
|
||||
assertEquals(Range.create(50f, 200f), info.getFrequencyRangeHz());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetMaxAmplitude_emptyMappingReturnsAlwaysZero() {
|
||||
VibratorInfo info = new VibratorInfo.Builder(TEST_VIBRATOR_ID).build();
|
||||
assertEquals(0f, info.getMaxAmplitude(Float.NaN), TEST_TOLERANCE);
|
||||
assertEquals(0f, info.getMaxAmplitude(100f), TEST_TOLERANCE);
|
||||
assertEquals(0f, info.getMaxAmplitude(200f), TEST_TOLERANCE);
|
||||
|
||||
info = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
|
||||
.setFrequencyMapping(new VibratorInfo.FrequencyMapping(
|
||||
profile = new VibratorInfo.FrequencyProfile(
|
||||
/* resonantFrequencyHz= */ 150,
|
||||
/* minFrequencyHz= */ Float.NaN,
|
||||
/* frequencyResolutionHz= */ Float.NaN,
|
||||
null))
|
||||
.build();
|
||||
/* maxAmplitudes= */ null);
|
||||
|
||||
assertEquals(0f, info.getMaxAmplitude(Float.NaN), TEST_TOLERANCE);
|
||||
assertEquals(0f, info.getMaxAmplitude(100f), TEST_TOLERANCE);
|
||||
assertEquals(0f, info.getMaxAmplitude(150f), TEST_TOLERANCE);
|
||||
assertEquals(0f, profile.getMaxAmplitude(Float.NaN), TEST_TOLERANCE);
|
||||
assertEquals(0f, profile.getMaxAmplitude(100f), TEST_TOLERANCE);
|
||||
assertEquals(0f, profile.getMaxAmplitude(150f), TEST_TOLERANCE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetMaxAmplitude_validMappingReturnsMappedValues() {
|
||||
VibratorInfo info = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
|
||||
.setFrequencyMapping(new VibratorInfo.FrequencyMapping(
|
||||
public void testGetMaxAmplitude_validprofileReturnsMappedValues() {
|
||||
VibratorInfo.FrequencyProfile profile = new VibratorInfo.FrequencyProfile(
|
||||
/* resonantFrequencyHz= */ 150,
|
||||
/* minFrequencyHz= */ 50,
|
||||
/* frequencyResolutionHz= */ 25,
|
||||
new float[]{
|
||||
/* maxAmplitudes= */ new float[]{
|
||||
/* 50Hz= */ 0.1f, 0.2f, 0.4f, 0.8f, /* 150Hz= */ 1f, 0.9f,
|
||||
/* 200Hz= */ 0.8f}))
|
||||
.build();
|
||||
/* 200Hz= */ 0.8f});
|
||||
|
||||
assertEquals(1f, info.getMaxAmplitude(150f), TEST_TOLERANCE);
|
||||
assertEquals(0.9f, info.getMaxAmplitude(175f), TEST_TOLERANCE);
|
||||
assertEquals(0.8f, info.getMaxAmplitude(125f), TEST_TOLERANCE);
|
||||
assertEquals(0.8f, info.getMaxAmplitude(info.getFrequencyRangeHz().getUpper()),
|
||||
TEST_TOLERANCE); // 200Hz
|
||||
assertEquals(0.1f, info.getMaxAmplitude(info.getFrequencyRangeHz().getLower()),
|
||||
TEST_TOLERANCE); // 50Hz
|
||||
assertEquals(1f, profile.getMaxAmplitude(150f), TEST_TOLERANCE);
|
||||
assertEquals(0.9f, profile.getMaxAmplitude(175f), TEST_TOLERANCE);
|
||||
assertEquals(0.8f, profile.getMaxAmplitude(125f), TEST_TOLERANCE);
|
||||
assertEquals(0.8f, profile.getMaxAmplitude(200f), TEST_TOLERANCE);
|
||||
assertEquals(0.1f, profile.getMaxAmplitude(50f), TEST_TOLERANCE);
|
||||
|
||||
// 145Hz maps to the max amplitude for 125Hz, which is lower.
|
||||
assertEquals(0.8f, info.getMaxAmplitude(145f), TEST_TOLERANCE); // 145Hz
|
||||
assertEquals(0.8f, profile.getMaxAmplitude(145f), TEST_TOLERANCE); // 145Hz
|
||||
// 185Hz maps to the max amplitude for 200Hz, which is lower.
|
||||
assertEquals(0.8f, info.getMaxAmplitude(185f), TEST_TOLERANCE); // 185Hz
|
||||
assertEquals(0.8f, profile.getMaxAmplitude(185f), TEST_TOLERANCE); // 185Hz
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -245,7 +224,7 @@ public class VibratorInfoTest {
|
||||
.setPwlePrimitiveDurationMax(50)
|
||||
.setPwleSizeMax(20)
|
||||
.setQFactor(2f)
|
||||
.setFrequencyMapping(TEST_FREQUENCY_MAPPING);
|
||||
.setFrequencyProfile(TEST_FREQUENCY_PROFILE);
|
||||
VibratorInfo complete = completeBuilder.build();
|
||||
|
||||
assertEquals(complete, complete);
|
||||
@@ -272,23 +251,21 @@ public class VibratorInfoTest {
|
||||
.build();
|
||||
assertNotEquals(complete, completeWithDifferentPrimitiveDuration);
|
||||
|
||||
VibratorInfo completeWithDifferentFrequencyMapping = completeBuilder
|
||||
.setFrequencyMapping(new VibratorInfo.FrequencyMapping(
|
||||
VibratorInfo completeWithDifferentFrequencyProfile = completeBuilder
|
||||
.setFrequencyProfile(new VibratorInfo.FrequencyProfile(
|
||||
TEST_RESONANT_FREQUENCY + 20,
|
||||
TEST_MIN_FREQUENCY + 10,
|
||||
TEST_FREQUENCY_RESOLUTION + 5,
|
||||
TEST_AMPLITUDE_MAP))
|
||||
.build();
|
||||
assertNotEquals(complete, completeWithDifferentFrequencyMapping);
|
||||
assertNotEquals(complete, completeWithDifferentFrequencyProfile);
|
||||
|
||||
VibratorInfo completeWithEmptyFrequencyMapping = completeBuilder
|
||||
.setFrequencyMapping(EMPTY_FREQUENCY_MAPPING)
|
||||
VibratorInfo completeWithEmptyFrequencyProfile = completeBuilder
|
||||
.setFrequencyProfile(EMPTY_FREQUENCY_PROFILE)
|
||||
.build();
|
||||
assertNotEquals(complete, completeWithEmptyFrequencyMapping);
|
||||
assertNotEquals(complete, completeWithEmptyFrequencyProfile);
|
||||
|
||||
VibratorInfo completeWithUnknownQFactor = completeBuilder
|
||||
.setQFactor(Float.NaN)
|
||||
.build();
|
||||
VibratorInfo completeWithUnknownQFactor = completeBuilder.setQFactor(Float.NaN).build();
|
||||
assertNotEquals(complete, completeWithUnknownQFactor);
|
||||
|
||||
VibratorInfo completeWithDifferentQFactor = completeBuilder
|
||||
@@ -316,7 +293,7 @@ public class VibratorInfoTest {
|
||||
.setSupportedEffects(VibrationEffect.EFFECT_CLICK)
|
||||
.setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 20)
|
||||
.setQFactor(Float.NaN)
|
||||
.setFrequencyMapping(TEST_FREQUENCY_MAPPING)
|
||||
.setFrequencyProfile(TEST_FREQUENCY_PROFILE)
|
||||
.build();
|
||||
|
||||
Parcel parcel = Parcel.obtain();
|
||||
|
||||
@@ -61,6 +61,8 @@ public class VibratorTest {
|
||||
@Rule
|
||||
public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
|
||||
|
||||
private static final float TEST_TOLERANCE = 1e-5f;
|
||||
|
||||
private Context mContextSpy;
|
||||
private Vibrator mVibratorSpy;
|
||||
|
||||
@@ -76,6 +78,9 @@ public class VibratorTest {
|
||||
@Test
|
||||
public void getId_returnsDefaultId() {
|
||||
assertEquals(-1, mVibratorSpy.getId());
|
||||
assertEquals(-1, new SystemVibrator.NoVibratorInfo().getId());
|
||||
assertEquals(-1, new SystemVibrator.MultiVibratorInfo(new VibratorInfo[] {
|
||||
VibratorInfo.EMPTY_VIBRATOR_INFO, VibratorInfo.EMPTY_VIBRATOR_INFO }).getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -90,8 +95,7 @@ public class VibratorTest {
|
||||
|
||||
@Test
|
||||
public void areEffectsSupported_noVibrator_returnsAlwaysNo() {
|
||||
SystemVibrator.AllVibratorsInfo info = new SystemVibrator.AllVibratorsInfo(
|
||||
new VibratorInfo[0]);
|
||||
VibratorInfo info = new SystemVibrator.NoVibratorInfo();
|
||||
assertEquals(Vibrator.VIBRATION_EFFECT_SUPPORT_NO,
|
||||
info.isEffectSupported(VibrationEffect.EFFECT_CLICK));
|
||||
}
|
||||
@@ -104,7 +108,7 @@ public class VibratorTest {
|
||||
VibratorInfo unsupportedVibrator = new VibratorInfo.Builder(/* id= */ 2)
|
||||
.setSupportedEffects(new int[0])
|
||||
.build();
|
||||
SystemVibrator.AllVibratorsInfo info = new SystemVibrator.AllVibratorsInfo(
|
||||
VibratorInfo info = new SystemVibrator.MultiVibratorInfo(
|
||||
new VibratorInfo[]{supportedVibrator, unsupportedVibrator});
|
||||
assertEquals(Vibrator.VIBRATION_EFFECT_SUPPORT_NO,
|
||||
info.isEffectSupported(VibrationEffect.EFFECT_CLICK));
|
||||
@@ -116,7 +120,7 @@ public class VibratorTest {
|
||||
.setSupportedEffects(VibrationEffect.EFFECT_CLICK)
|
||||
.build();
|
||||
VibratorInfo unknownSupportVibrator = VibratorInfo.EMPTY_VIBRATOR_INFO;
|
||||
SystemVibrator.AllVibratorsInfo info = new SystemVibrator.AllVibratorsInfo(
|
||||
VibratorInfo info = new SystemVibrator.MultiVibratorInfo(
|
||||
new VibratorInfo[]{supportedVibrator, unknownSupportVibrator});
|
||||
assertEquals(Vibrator.VIBRATION_EFFECT_SUPPORT_UNKNOWN,
|
||||
info.isEffectSupported(VibrationEffect.EFFECT_CLICK));
|
||||
@@ -130,7 +134,7 @@ public class VibratorTest {
|
||||
VibratorInfo secondVibrator = new VibratorInfo.Builder(/* id= */ 2)
|
||||
.setSupportedEffects(VibrationEffect.EFFECT_CLICK)
|
||||
.build();
|
||||
SystemVibrator.AllVibratorsInfo info = new SystemVibrator.AllVibratorsInfo(
|
||||
VibratorInfo info = new SystemVibrator.MultiVibratorInfo(
|
||||
new VibratorInfo[]{firstVibrator, secondVibrator});
|
||||
assertEquals(Vibrator.VIBRATION_EFFECT_SUPPORT_YES,
|
||||
info.isEffectSupported(VibrationEffect.EFFECT_CLICK));
|
||||
@@ -148,8 +152,7 @@ public class VibratorTest {
|
||||
|
||||
@Test
|
||||
public void arePrimitivesSupported_noVibrator_returnsAlwaysFalse() {
|
||||
SystemVibrator.AllVibratorsInfo info = new SystemVibrator.AllVibratorsInfo(
|
||||
new VibratorInfo[0]);
|
||||
VibratorInfo info = new SystemVibrator.NoVibratorInfo();
|
||||
assertFalse(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_CLICK));
|
||||
}
|
||||
|
||||
@@ -160,7 +163,7 @@ public class VibratorTest {
|
||||
.setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
|
||||
.build();
|
||||
VibratorInfo unsupportedVibrator = VibratorInfo.EMPTY_VIBRATOR_INFO;
|
||||
SystemVibrator.AllVibratorsInfo info = new SystemVibrator.AllVibratorsInfo(
|
||||
VibratorInfo info = new SystemVibrator.MultiVibratorInfo(
|
||||
new VibratorInfo[]{supportedVibrator, unsupportedVibrator});
|
||||
assertFalse(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_CLICK));
|
||||
}
|
||||
@@ -175,7 +178,7 @@ public class VibratorTest {
|
||||
.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
|
||||
.setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 15)
|
||||
.build();
|
||||
SystemVibrator.AllVibratorsInfo info = new SystemVibrator.AllVibratorsInfo(
|
||||
VibratorInfo info = new SystemVibrator.MultiVibratorInfo(
|
||||
new VibratorInfo[]{firstVibrator, secondVibrator});
|
||||
assertTrue(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_CLICK));
|
||||
}
|
||||
@@ -192,8 +195,7 @@ public class VibratorTest {
|
||||
|
||||
@Test
|
||||
public void getPrimitivesDurations_noVibrator_returnsAlwaysZero() {
|
||||
SystemVibrator.AllVibratorsInfo info = new SystemVibrator.AllVibratorsInfo(
|
||||
new VibratorInfo[0]);
|
||||
VibratorInfo info = new SystemVibrator.NoVibratorInfo();
|
||||
assertEquals(0, info.getPrimitiveDuration(VibrationEffect.Composition.PRIMITIVE_CLICK));
|
||||
}
|
||||
|
||||
@@ -204,7 +206,7 @@ public class VibratorTest {
|
||||
.setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 10)
|
||||
.build();
|
||||
VibratorInfo unsupportedVibrator = VibratorInfo.EMPTY_VIBRATOR_INFO;
|
||||
SystemVibrator.AllVibratorsInfo info = new SystemVibrator.AllVibratorsInfo(
|
||||
VibratorInfo info = new SystemVibrator.MultiVibratorInfo(
|
||||
new VibratorInfo[]{supportedVibrator, unsupportedVibrator});
|
||||
assertEquals(0, info.getPrimitiveDuration(VibrationEffect.Composition.PRIMITIVE_CLICK));
|
||||
}
|
||||
@@ -219,11 +221,179 @@ public class VibratorTest {
|
||||
.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
|
||||
.setSupportedPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 20)
|
||||
.build();
|
||||
SystemVibrator.AllVibratorsInfo info = new SystemVibrator.AllVibratorsInfo(
|
||||
VibratorInfo info = new SystemVibrator.MultiVibratorInfo(
|
||||
new VibratorInfo[]{firstVibrator, secondVibrator});
|
||||
assertEquals(20, info.getPrimitiveDuration(VibrationEffect.Composition.PRIMITIVE_CLICK));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getQFactorAndResonantFrequency_noVibrator_returnsNaN() {
|
||||
VibratorInfo info = new SystemVibrator.NoVibratorInfo();
|
||||
|
||||
assertTrue(Float.isNaN(info.getQFactor()));
|
||||
assertTrue(Float.isNaN(info.getResonantFrequencyHz()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getQFactorAndResonantFrequency_differentValues_returnsNaN() {
|
||||
VibratorInfo firstVibrator = new VibratorInfo.Builder(/* id= */ 1)
|
||||
.setQFactor(1f)
|
||||
.setFrequencyProfile(new VibratorInfo.FrequencyProfile(1, 1, 1, null))
|
||||
.build();
|
||||
VibratorInfo secondVibrator = new VibratorInfo.Builder(/* id= */ 2)
|
||||
.setQFactor(2f)
|
||||
.setFrequencyProfile(new VibratorInfo.FrequencyProfile(2, 2, 2, null))
|
||||
.build();
|
||||
VibratorInfo info = new SystemVibrator.MultiVibratorInfo(
|
||||
new VibratorInfo[]{firstVibrator, secondVibrator});
|
||||
|
||||
assertTrue(Float.isNaN(info.getQFactor()));
|
||||
assertTrue(Float.isNaN(info.getResonantFrequencyHz()));
|
||||
|
||||
// One vibrator with values undefined.
|
||||
VibratorInfo thirdVibrator = new VibratorInfo.Builder(/* id= */ 3).build();
|
||||
info = new SystemVibrator.MultiVibratorInfo(
|
||||
new VibratorInfo[]{firstVibrator, thirdVibrator});
|
||||
|
||||
assertTrue(Float.isNaN(info.getQFactor()));
|
||||
assertTrue(Float.isNaN(info.getResonantFrequencyHz()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getQFactorAndResonantFrequency_sameValues_returnsValue() {
|
||||
VibratorInfo firstVibrator = new VibratorInfo.Builder(/* id= */ 1)
|
||||
.setQFactor(10f)
|
||||
.setFrequencyProfile(new VibratorInfo.FrequencyProfile(
|
||||
/* resonantFrequencyHz= */ 11, 10, 0.5f, null))
|
||||
.build();
|
||||
VibratorInfo secondVibrator = new VibratorInfo.Builder(/* id= */ 2)
|
||||
.setQFactor(10f)
|
||||
.setFrequencyProfile(new VibratorInfo.FrequencyProfile(
|
||||
/* resonantFrequencyHz= */ 11, 5, 1, null))
|
||||
.build();
|
||||
VibratorInfo info = new SystemVibrator.MultiVibratorInfo(
|
||||
new VibratorInfo[]{firstVibrator, secondVibrator});
|
||||
|
||||
assertEquals(10f, info.getQFactor(), TEST_TOLERANCE);
|
||||
assertEquals(11f, info.getResonantFrequencyHz(), TEST_TOLERANCE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getFrequencyProfile_noVibrator_returnsEmpty() {
|
||||
VibratorInfo info = new SystemVibrator.NoVibratorInfo();
|
||||
|
||||
assertTrue(info.getFrequencyProfile().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getFrequencyProfile_differentResonantFrequencyOrResolutionValues_returnsEmpty() {
|
||||
VibratorInfo firstVibrator = new VibratorInfo.Builder(/* id= */ 1)
|
||||
.setFrequencyProfile(new VibratorInfo.FrequencyProfile(1, 1, 1,
|
||||
new float[] { 0, 1 }))
|
||||
.build();
|
||||
VibratorInfo differentResonantFrequency = new VibratorInfo.Builder(/* id= */ 2)
|
||||
.setFrequencyProfile(new VibratorInfo.FrequencyProfile(2, 1, 1,
|
||||
new float[] { 0, 1 }))
|
||||
.build();
|
||||
VibratorInfo info = new SystemVibrator.MultiVibratorInfo(
|
||||
new VibratorInfo[]{firstVibrator, differentResonantFrequency});
|
||||
|
||||
assertTrue(info.getFrequencyProfile().isEmpty());
|
||||
|
||||
VibratorInfo differentFrequencyResolution = new VibratorInfo.Builder(/* id= */ 2)
|
||||
.setFrequencyProfile(new VibratorInfo.FrequencyProfile(1, 1, 2,
|
||||
new float[] { 0, 1 }))
|
||||
.build();
|
||||
info = new SystemVibrator.MultiVibratorInfo(
|
||||
new VibratorInfo[]{firstVibrator, differentFrequencyResolution});
|
||||
|
||||
assertTrue(info.getFrequencyProfile().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getFrequencyProfile_missingValues_returnsEmpty() {
|
||||
VibratorInfo firstVibrator = new VibratorInfo.Builder(/* id= */ 1)
|
||||
.setFrequencyProfile(new VibratorInfo.FrequencyProfile(1, 1, 1,
|
||||
new float[] { 0, 1 }))
|
||||
.build();
|
||||
VibratorInfo missingResonantFrequency = new VibratorInfo.Builder(/* id= */ 2)
|
||||
.setFrequencyProfile(new VibratorInfo.FrequencyProfile(Float.NaN, 1, 1,
|
||||
new float[] { 0, 1 }))
|
||||
.build();
|
||||
VibratorInfo info = new SystemVibrator.MultiVibratorInfo(
|
||||
new VibratorInfo[]{firstVibrator, missingResonantFrequency});
|
||||
|
||||
assertTrue(info.getFrequencyProfile().isEmpty());
|
||||
|
||||
VibratorInfo missingMinFrequency = new VibratorInfo.Builder(/* id= */ 2)
|
||||
.setFrequencyProfile(new VibratorInfo.FrequencyProfile(1, Float.NaN, 1,
|
||||
new float[] { 0, 1 }))
|
||||
.build();
|
||||
info = new SystemVibrator.MultiVibratorInfo(
|
||||
new VibratorInfo[]{firstVibrator, missingMinFrequency});
|
||||
|
||||
assertTrue(info.getFrequencyProfile().isEmpty());
|
||||
|
||||
VibratorInfo missingFrequencyResolution = new VibratorInfo.Builder(/* id= */ 2)
|
||||
.setFrequencyProfile(new VibratorInfo.FrequencyProfile(1, 1, Float.NaN,
|
||||
new float[] { 0, 1 }))
|
||||
.build();
|
||||
info = new SystemVibrator.MultiVibratorInfo(
|
||||
new VibratorInfo[]{firstVibrator, missingFrequencyResolution});
|
||||
|
||||
assertTrue(info.getFrequencyProfile().isEmpty());
|
||||
|
||||
VibratorInfo missingMaxAmplitudes = new VibratorInfo.Builder(/* id= */ 2)
|
||||
.setFrequencyProfile(new VibratorInfo.FrequencyProfile(1, 1, 1, null))
|
||||
.build();
|
||||
info = new SystemVibrator.MultiVibratorInfo(
|
||||
new VibratorInfo[]{firstVibrator, missingMaxAmplitudes});
|
||||
|
||||
assertTrue(info.getFrequencyProfile().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getFrequencyProfile_unalignedMaxAmplitudes_returnsEmpty() {
|
||||
VibratorInfo firstVibrator = new VibratorInfo.Builder(/* id= */ 1)
|
||||
.setFrequencyProfile(new VibratorInfo.FrequencyProfile(11, 10, 0.5f,
|
||||
new float[] { 0, 1, 1, 0 }))
|
||||
.build();
|
||||
VibratorInfo unalignedMinFrequency = new VibratorInfo.Builder(/* id= */ 2)
|
||||
.setFrequencyProfile(new VibratorInfo.FrequencyProfile(11, 10.1f, 0.5f,
|
||||
new float[] { 0, 1, 1, 0 }))
|
||||
.build();
|
||||
VibratorInfo thirdVibrator = new VibratorInfo.Builder(/* id= */ 2)
|
||||
.setFrequencyProfile(new VibratorInfo.FrequencyProfile(11, 10.5f, 0.5f,
|
||||
new float[] { 0, 1, 1, 0 }))
|
||||
.build();
|
||||
VibratorInfo info = new SystemVibrator.MultiVibratorInfo(
|
||||
new VibratorInfo[]{firstVibrator, unalignedMinFrequency, thirdVibrator});
|
||||
|
||||
assertTrue(info.getFrequencyProfile().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getFrequencyProfile_alignedProfiles_returnsIntersection() {
|
||||
VibratorInfo firstVibrator = new VibratorInfo.Builder(/* id= */ 1)
|
||||
.setFrequencyProfile(new VibratorInfo.FrequencyProfile(11, 10, 0.5f,
|
||||
new float[] { 0.5f, 1, 1, 0.5f }))
|
||||
.build();
|
||||
VibratorInfo secondVibrator = new VibratorInfo.Builder(/* id= */ 2)
|
||||
.setFrequencyProfile(new VibratorInfo.FrequencyProfile(11, 10.5f, 0.5f,
|
||||
new float[] { 1, 1, 1 }))
|
||||
.build();
|
||||
VibratorInfo thirdVibrator = new VibratorInfo.Builder(/* id= */ 3)
|
||||
.setFrequencyProfile(new VibratorInfo.FrequencyProfile(11, 10.5f, 0.5f,
|
||||
new float[] { 0.8f, 1, 0.8f, 0.5f }))
|
||||
.build();
|
||||
VibratorInfo info = new SystemVibrator.MultiVibratorInfo(
|
||||
new VibratorInfo[]{firstVibrator, secondVibrator, thirdVibrator});
|
||||
|
||||
assertEquals(
|
||||
new VibratorInfo.FrequencyProfile(11, 10.5f, 0.5f, new float[] { 0.8f, 1, 0.5f }),
|
||||
info.getFrequencyProfile());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void vibrate_withVibrationAttributes_usesGivenAttributes() {
|
||||
VibrationEffect effect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
|
||||
|
||||
@@ -26,8 +26,8 @@ import android.util.Range;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Adapter that clips frequency values to {@link VibratorInfo#getFrequencyRangeHz()} and
|
||||
* amplitude values to respective {@link VibratorInfo#getMaxAmplitude}.
|
||||
* Adapter that clips frequency values to the ones specified by the
|
||||
* {@link VibratorInfo.FrequencyProfile}.
|
||||
*
|
||||
* <p>Devices with no frequency control will collapse all frequencies to the resonant frequency and
|
||||
* leave amplitudes unchanged.
|
||||
@@ -69,20 +69,20 @@ final class ClippingAmplitudeAndFrequencyAdapter
|
||||
}
|
||||
|
||||
private float clampFrequency(VibratorInfo info, float frequencyHz) {
|
||||
Range<Float> frequencyRangeHz = info.getFrequencyRangeHz();
|
||||
Range<Float> frequencyRangeHz = info.getFrequencyProfile().getFrequencyRangeHz();
|
||||
if (frequencyHz == 0 || frequencyRangeHz == null) {
|
||||
return info.getResonantFrequency();
|
||||
return info.getResonantFrequencyHz();
|
||||
}
|
||||
return frequencyRangeHz.clamp(frequencyHz);
|
||||
}
|
||||
|
||||
private float clampAmplitude(VibratorInfo info, float frequencyHz, float amplitude) {
|
||||
Range<Float> frequencyRangeHz = info.getFrequencyRangeHz();
|
||||
if (frequencyRangeHz == null) {
|
||||
// No frequency range was specified, leave amplitude unchanged, the frequency will be
|
||||
// clamped to the device's resonant frequency.
|
||||
VibratorInfo.FrequencyProfile mapping = info.getFrequencyProfile();
|
||||
if (mapping.isEmpty()) {
|
||||
// No frequency mapping was specified so leave amplitude unchanged.
|
||||
// The frequency will be clamped to the device's resonant frequency.
|
||||
return amplitude;
|
||||
}
|
||||
return MathUtils.min(amplitude, info.getMaxAmplitude(frequencyHz));
|
||||
return MathUtils.min(amplitude, mapping.getMaxAmplitude(frequencyHz));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,6 +94,6 @@ final class RampToStepAdapter implements VibrationEffectAdapters.SegmentsAdapter
|
||||
}
|
||||
|
||||
private static float fillEmptyFrequency(VibratorInfo info, float frequencyHz) {
|
||||
return frequencyHz == 0 ? info.getResonantFrequency() : frequencyHz;
|
||||
return frequencyHz == 0 ? info.getResonantFrequencyHz() : frequencyHz;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,6 +148,6 @@ final class StepToRampAdapter implements VibrationEffectAdapters.SegmentsAdapter
|
||||
}
|
||||
|
||||
private static float fillEmptyFrequency(VibratorInfo info, float frequencyHz) {
|
||||
return frequencyHz == 0 ? info.getResonantFrequency() : frequencyHz;
|
||||
return frequencyHz == 0 ? info.getResonantFrequencyHz() : frequencyHz;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,8 +39,8 @@ namespace android {
|
||||
|
||||
static JavaVM* sJvm = nullptr;
|
||||
static jmethodID sMethodIdOnComplete;
|
||||
static jclass sFrequencyMappingClass;
|
||||
static jmethodID sFrequencyMappingCtor;
|
||||
static jclass sFrequencyProfileClass;
|
||||
static jmethodID sFrequencyProfileCtor;
|
||||
static struct {
|
||||
jmethodID setCapabilities;
|
||||
jmethodID setSupportedEffects;
|
||||
@@ -51,7 +51,7 @@ static struct {
|
||||
jmethodID setPrimitiveDelayMax;
|
||||
jmethodID setCompositionSizeMax;
|
||||
jmethodID setQFactor;
|
||||
jmethodID setFrequencyMapping;
|
||||
jmethodID setFrequencyProfile;
|
||||
} sVibratorInfoBuilderClassInfo;
|
||||
static struct {
|
||||
jfieldID id;
|
||||
@@ -437,11 +437,11 @@ static jboolean vibratorGetInfo(JNIEnv* env, jclass /* clazz */, jlong ptr,
|
||||
env->SetFloatArrayRegion(maxAmplitudes, 0, amplitudes.size(),
|
||||
reinterpret_cast<jfloat*>(amplitudes.data()));
|
||||
}
|
||||
jobject frequencyMapping =
|
||||
env->NewObject(sFrequencyMappingClass, sFrequencyMappingCtor, resonantFrequency,
|
||||
jobject frequencyProfile =
|
||||
env->NewObject(sFrequencyProfileClass, sFrequencyProfileCtor, resonantFrequency,
|
||||
minFrequency, frequencyResolution, maxAmplitudes);
|
||||
env->CallObjectMethod(vibratorInfoBuilder, sVibratorInfoBuilderClassInfo.setFrequencyMapping,
|
||||
frequencyMapping);
|
||||
env->CallObjectMethod(vibratorInfoBuilder, sVibratorInfoBuilderClassInfo.setFrequencyProfile,
|
||||
frequencyProfile);
|
||||
|
||||
return info.isFailedLogged("vibratorGetInfo") ? JNI_FALSE : JNI_TRUE;
|
||||
}
|
||||
@@ -485,9 +485,9 @@ int register_android_server_vibrator_VibratorController(JavaVM* jvm, JNIEnv* env
|
||||
sRampClassInfo.endFrequencyHz = GetFieldIDOrDie(env, rampClass, "mEndFrequencyHz", "F");
|
||||
sRampClassInfo.duration = GetFieldIDOrDie(env, rampClass, "mDuration", "I");
|
||||
|
||||
jclass frequencyMappingClass = FindClassOrDie(env, "android/os/VibratorInfo$FrequencyMapping");
|
||||
sFrequencyMappingClass = static_cast<jclass>(env->NewGlobalRef(frequencyMappingClass));
|
||||
sFrequencyMappingCtor = GetMethodIDOrDie(env, sFrequencyMappingClass, "<init>", "(FFF[F)V");
|
||||
jclass frequencyProfileClass = FindClassOrDie(env, "android/os/VibratorInfo$FrequencyProfile");
|
||||
sFrequencyProfileClass = static_cast<jclass>(env->NewGlobalRef(frequencyProfileClass));
|
||||
sFrequencyProfileCtor = GetMethodIDOrDie(env, sFrequencyProfileClass, "<init>", "(FFF[F)V");
|
||||
|
||||
jclass vibratorInfoBuilderClass = FindClassOrDie(env, "android/os/VibratorInfo$Builder");
|
||||
sVibratorInfoBuilderClassInfo.setCapabilities =
|
||||
@@ -517,9 +517,9 @@ int register_android_server_vibrator_VibratorController(JavaVM* jvm, JNIEnv* env
|
||||
sVibratorInfoBuilderClassInfo.setQFactor =
|
||||
GetMethodIDOrDie(env, vibratorInfoBuilderClass, "setQFactor",
|
||||
"(F)Landroid/os/VibratorInfo$Builder;");
|
||||
sVibratorInfoBuilderClassInfo.setFrequencyMapping =
|
||||
GetMethodIDOrDie(env, vibratorInfoBuilderClass, "setFrequencyMapping",
|
||||
"(Landroid/os/VibratorInfo$FrequencyMapping;)"
|
||||
sVibratorInfoBuilderClassInfo.setFrequencyProfile =
|
||||
GetMethodIDOrDie(env, vibratorInfoBuilderClass, "setFrequencyProfile",
|
||||
"(Landroid/os/VibratorInfo$FrequencyProfile;)"
|
||||
"Landroid/os/VibratorInfo$Builder;");
|
||||
|
||||
return jniRegisterNativeMethods(env,
|
||||
|
||||
@@ -53,10 +53,10 @@ public class DeviceVibrationEffectAdapterTest {
|
||||
private static final float[] TEST_AMPLITUDE_MAP = new float[]{
|
||||
/* 50Hz= */ 0.1f, 0.2f, 0.4f, 0.8f, /* 150Hz= */ 1f, 0.9f, /* 200Hz= */ 0.8f};
|
||||
|
||||
private static final VibratorInfo.FrequencyMapping EMPTY_FREQUENCY_MAPPING =
|
||||
new VibratorInfo.FrequencyMapping(Float.NaN, Float.NaN, Float.NaN, null);
|
||||
private static final VibratorInfo.FrequencyMapping TEST_FREQUENCY_MAPPING =
|
||||
new VibratorInfo.FrequencyMapping(TEST_RESONANT_FREQUENCY, TEST_MIN_FREQUENCY,
|
||||
private static final VibratorInfo.FrequencyProfile EMPTY_FREQUENCY_PROFILE =
|
||||
new VibratorInfo.FrequencyProfile(Float.NaN, Float.NaN, Float.NaN, null);
|
||||
private static final VibratorInfo.FrequencyProfile TEST_FREQUENCY_PROFILE =
|
||||
new VibratorInfo.FrequencyProfile(TEST_RESONANT_FREQUENCY, TEST_MIN_FREQUENCY,
|
||||
TEST_FREQUENCY_RESOLUTION, TEST_AMPLITUDE_MAP);
|
||||
|
||||
private DeviceVibrationEffectAdapter mAdapter;
|
||||
@@ -79,8 +79,8 @@ public class DeviceVibrationEffectAdapterTest {
|
||||
new PrimitiveSegment(VibrationEffect.Composition.PRIMITIVE_SPIN, 0.5f, 100)),
|
||||
/* repeatIndex= */ -1);
|
||||
|
||||
assertEquals(effect, mAdapter.apply(effect, createVibratorInfo(EMPTY_FREQUENCY_MAPPING)));
|
||||
assertEquals(effect, mAdapter.apply(effect, createVibratorInfo(TEST_FREQUENCY_MAPPING)));
|
||||
assertEquals(effect, mAdapter.apply(effect, createVibratorInfo(EMPTY_FREQUENCY_PROFILE)));
|
||||
assertEquals(effect, mAdapter.apply(effect, createVibratorInfo(TEST_FREQUENCY_PROFILE)));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -97,7 +97,7 @@ public class DeviceVibrationEffectAdapterTest {
|
||||
/* repeatIndex= */ 3);
|
||||
|
||||
VibrationEffect.Composed adaptedEffect = (VibrationEffect.Composed) mAdapter.apply(effect,
|
||||
createVibratorInfo(EMPTY_FREQUENCY_MAPPING));
|
||||
createVibratorInfo(EMPTY_FREQUENCY_PROFILE));
|
||||
assertTrue(adaptedEffect.getSegments().size() > effect.getSegments().size());
|
||||
assertTrue(adaptedEffect.getRepeatIndex() >= effect.getRepeatIndex());
|
||||
|
||||
@@ -128,7 +128,7 @@ public class DeviceVibrationEffectAdapterTest {
|
||||
/* startFrequencyHz= */ 200, /* endFrequencyHz= */ 50, /* duration= */ 20)),
|
||||
/* repeatIndex= */ 2);
|
||||
|
||||
VibratorInfo info = createVibratorInfo(TEST_FREQUENCY_MAPPING,
|
||||
VibratorInfo info = createVibratorInfo(TEST_FREQUENCY_PROFILE,
|
||||
IVibrator.CAP_COMPOSE_PWLE_EFFECTS);
|
||||
assertEquals(expected, mAdapter.apply(effect, info));
|
||||
}
|
||||
@@ -159,7 +159,7 @@ public class DeviceVibrationEffectAdapterTest {
|
||||
/* duration= */ 20)),
|
||||
/* repeatIndex= */ 2);
|
||||
|
||||
VibratorInfo info = createVibratorInfo(EMPTY_FREQUENCY_MAPPING,
|
||||
VibratorInfo info = createVibratorInfo(EMPTY_FREQUENCY_PROFILE,
|
||||
IVibrator.CAP_COMPOSE_PWLE_EFFECTS);
|
||||
assertEquals(expected, mAdapter.apply(effect, info));
|
||||
}
|
||||
@@ -188,17 +188,17 @@ public class DeviceVibrationEffectAdapterTest {
|
||||
/* startFrequencyHz= */ 200, /* endFrequencyHz= */ 50, /* duration= */ 20)),
|
||||
/* repeatIndex= */ 2);
|
||||
|
||||
VibratorInfo info = createVibratorInfo(TEST_FREQUENCY_MAPPING,
|
||||
VibratorInfo info = createVibratorInfo(TEST_FREQUENCY_PROFILE,
|
||||
IVibrator.CAP_COMPOSE_PWLE_EFFECTS);
|
||||
assertEquals(expected, mAdapter.apply(effect, info));
|
||||
}
|
||||
|
||||
private static VibratorInfo createVibratorInfo(VibratorInfo.FrequencyMapping frequencyMapping,
|
||||
private static VibratorInfo createVibratorInfo(VibratorInfo.FrequencyProfile frequencyProfile,
|
||||
int... capabilities) {
|
||||
int cap = IntStream.of(capabilities).reduce((a, b) -> a | b).orElse(0);
|
||||
return new VibratorInfo.Builder(0)
|
||||
.setCapabilities(cap)
|
||||
.setFrequencyMapping(frequencyMapping)
|
||||
.setFrequencyProfile(frequencyProfile)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,7 +170,7 @@ final class FakeVibratorControllerProvider {
|
||||
}
|
||||
infoBuilder.setCompositionSizeMax(mCompositionSizeMax);
|
||||
infoBuilder.setQFactor(mQFactor);
|
||||
infoBuilder.setFrequencyMapping(new VibratorInfo.FrequencyMapping(
|
||||
infoBuilder.setFrequencyProfile(new VibratorInfo.FrequencyProfile(
|
||||
mResonantFrequency, mMinFrequency, mFrequencyResolution, mMaxAmplitudes));
|
||||
return mIsInfoLoadSuccessful;
|
||||
}
|
||||
|
||||
@@ -47,8 +47,8 @@ public class RampToStepAdapterTest {
|
||||
private static final int TEST_STEP_DURATION = 5;
|
||||
private static final float[] TEST_AMPLITUDE_MAP = new float[]{
|
||||
/* 50Hz= */ 0.1f, 0.2f, 0.4f, 0.8f, /* 150Hz= */ 1f, 0.9f, /* 200Hz= */ 0.8f};
|
||||
private static final VibratorInfo.FrequencyMapping TEST_FREQUENCY_MAPPING =
|
||||
new VibratorInfo.FrequencyMapping(
|
||||
private static final VibratorInfo.FrequencyProfile TEST_FREQUENCY_PROFILE =
|
||||
new VibratorInfo.FrequencyProfile(
|
||||
/* resonantFrequencyHz= */ 150f, /* minFrequencyHz= */ 50f,
|
||||
/* frequencyResolutionHz= */ 25f, TEST_AMPLITUDE_MAP);
|
||||
|
||||
@@ -124,7 +124,7 @@ public class RampToStepAdapterTest {
|
||||
private static VibratorInfo createVibratorInfo(int... capabilities) {
|
||||
return new VibratorInfo.Builder(0)
|
||||
.setCapabilities(IntStream.of(capabilities).reduce((a, b) -> a | b).orElse(0))
|
||||
.setFrequencyMapping(TEST_FREQUENCY_MAPPING)
|
||||
.setFrequencyProfile(TEST_FREQUENCY_PROFILE)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,8 +46,8 @@ import java.util.stream.IntStream;
|
||||
public class StepToRampAdapterTest {
|
||||
private static final float[] TEST_AMPLITUDE_MAP = new float[]{
|
||||
/* 50Hz= */ 0.1f, 0.2f, 0.4f, 0.8f, /* 150Hz= */ 1f, 0.9f, /* 200Hz= */ 0.8f};
|
||||
private static final VibratorInfo.FrequencyMapping TEST_FREQUENCY_MAPPING =
|
||||
new VibratorInfo.FrequencyMapping(
|
||||
private static final VibratorInfo.FrequencyProfile TEST_FREQUENCY_PROFILE =
|
||||
new VibratorInfo.FrequencyProfile(
|
||||
/* resonantFrequencyHz= */ 150f, /* minFrequencyHz= */ 50f,
|
||||
/* frequencyResolutionHz= */ 25f, TEST_AMPLITUDE_MAP);
|
||||
|
||||
@@ -99,7 +99,7 @@ public class StepToRampAdapterTest {
|
||||
VibratorInfo vibratorInfo = new VibratorInfo.Builder(0)
|
||||
.setCapabilities(IVibrator.CAP_COMPOSE_PWLE_EFFECTS)
|
||||
.setPwlePrimitiveDurationMax(10)
|
||||
.setFrequencyMapping(TEST_FREQUENCY_MAPPING)
|
||||
.setFrequencyProfile(TEST_FREQUENCY_PROFILE)
|
||||
.build();
|
||||
|
||||
// Update repeat index to skip the ramp splits.
|
||||
@@ -191,7 +191,7 @@ public class StepToRampAdapterTest {
|
||||
private static VibratorInfo createVibratorInfo(int... capabilities) {
|
||||
return new VibratorInfo.Builder(0)
|
||||
.setCapabilities(IntStream.of(capabilities).reduce((a, b) -> a | b).orElse(0))
|
||||
.setFrequencyMapping(TEST_FREQUENCY_MAPPING)
|
||||
.setFrequencyProfile(TEST_FREQUENCY_PROFILE)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -310,13 +310,13 @@ public class VibratorControllerTest {
|
||||
}
|
||||
|
||||
private void mockVibratorCapabilities(int capabilities) {
|
||||
VibratorInfo.FrequencyMapping frequencyMapping = new VibratorInfo.FrequencyMapping(
|
||||
VibratorInfo.FrequencyProfile frequencyProfile = new VibratorInfo.FrequencyProfile(
|
||||
Float.NaN, Float.NaN, Float.NaN, null);
|
||||
when(mNativeWrapperMock.getInfo(any(VibratorInfo.Builder.class)))
|
||||
.then(invocation -> {
|
||||
((VibratorInfo.Builder) invocation.getArgument(0))
|
||||
.setCapabilities(capabilities)
|
||||
.setFrequencyMapping(frequencyMapping);
|
||||
.setFrequencyProfile(frequencyProfile);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -316,7 +316,7 @@ public class VibratorManagerServiceTest {
|
||||
|
||||
assertNotNull(info);
|
||||
assertEquals(1, info.getId());
|
||||
assertEquals(123.f, info.getResonantFrequency(), 0.01 /*tolerance*/);
|
||||
assertEquals(123.f, info.getResonantFrequencyHz(), 0.01 /*tolerance*/);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -341,7 +341,7 @@ public class VibratorManagerServiceTest {
|
||||
info.isEffectSupported(VibrationEffect.EFFECT_TICK));
|
||||
assertTrue(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_CLICK));
|
||||
assertFalse(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_TICK));
|
||||
assertEquals(123.f, info.getResonantFrequency(), 0.01 /*tolerance*/);
|
||||
assertEquals(123.f, info.getResonantFrequencyHz(), 0.01 /*tolerance*/);
|
||||
assertTrue(Float.isNaN(info.getQFactor()));
|
||||
}
|
||||
|
||||
@@ -360,7 +360,7 @@ public class VibratorManagerServiceTest {
|
||||
VibratorInfo info = createService().getVibratorInfo(1);
|
||||
assertNotNull(info);
|
||||
assertEquals(1, info.getId());
|
||||
assertEquals(123.f, info.getResonantFrequency(), 0.01 /*tolerance*/);
|
||||
assertEquals(123.f, info.getResonantFrequencyHz(), 0.01 /*tolerance*/);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user