Create VibratorFrequencyProfile in Vibrator API.
Create public class to represent the vibrator response for the supported frequency range to the Vibrator public API, with basic implementation that extracts these values from FrequencyMapping. Integrate this data with the result of Vibrator.hasFrequencyControl method, which also checks the HAL capabilities. Also make public the methods to provide resonant frequency and Q-factor of the device. Update the SystemVibrator to handle multi-vibrator scenario and return values when all vibrators have the same configurations. This includes returning the intersection of all vibrator max amplitude measurements in the new profile object. Add validation to HAL values and CTS to cover expected ranges based on the device support for frequency control. Bug: 203785430 Test: android.os.cts.VibratorTest & VibratorInfoTest Change-Id: I0b7cf599ae6039355a0a198f4bdaafb85fb7422c
This commit is contained in:
@@ -32955,9 +32955,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);
|
||||
@@ -33283,6 +33287,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