Expose vibrator frequency bandwith to the service
Add hidden APIs to expose the supported frequency range and maximum amplitudes at each frequency for the device in Vibrator.java. Also add a hidden method hasFrequencyControl(). Bug: 167947076 Test: VibratorTest Change-Id: If2d5f8f0aa92223b4a342893f5fd3a1668dfcff1
This commit is contained in:
@@ -20,11 +20,13 @@ import android.annotation.CallbackExecutor;
|
||||
import android.annotation.NonNull;
|
||||
import android.app.ActivityThread;
|
||||
import android.content.Context;
|
||||
import android.hardware.vibrator.IVibrator;
|
||||
import android.os.Binder;
|
||||
import android.os.IVibratorStateListener;
|
||||
import android.os.VibrationAttributes;
|
||||
import android.os.VibrationEffect;
|
||||
import android.os.Vibrator;
|
||||
import android.os.VibratorInfo;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.Log;
|
||||
|
||||
@@ -41,7 +43,7 @@ final class InputDeviceVibrator extends Vibrator {
|
||||
|
||||
// mDeviceId represents InputDevice ID the vibrator belongs to
|
||||
private final int mDeviceId;
|
||||
private final int mVibratorId;
|
||||
private final VibratorInfo mVibratorInfo;
|
||||
private final Binder mToken;
|
||||
private final InputManager mInputManager;
|
||||
|
||||
@@ -52,7 +54,13 @@ final class InputDeviceVibrator extends Vibrator {
|
||||
InputDeviceVibrator(InputManager inputManager, int deviceId, int vibratorId) {
|
||||
mInputManager = inputManager;
|
||||
mDeviceId = deviceId;
|
||||
mVibratorId = vibratorId;
|
||||
mVibratorInfo = new VibratorInfo.Builder(vibratorId)
|
||||
.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL)
|
||||
// Set predefined support to empty as we know input devices do not support them.
|
||||
.setSupportedEffects()
|
||||
.setSupportedPrimitives()
|
||||
.setSupportedBraking()
|
||||
.build();
|
||||
mToken = new Binder();
|
||||
}
|
||||
|
||||
@@ -74,8 +82,8 @@ final class InputDeviceVibrator extends Vibrator {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return mVibratorId;
|
||||
protected VibratorInfo getInfo() {
|
||||
return mVibratorInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -159,7 +167,7 @@ final class InputDeviceVibrator extends Vibrator {
|
||||
|
||||
@Override
|
||||
public boolean hasAmplitudeControl() {
|
||||
return true;
|
||||
return mVibratorInfo.hasCapability(IVibrator.CAP_AMPLITUDE_CONTROL);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -28,7 +28,6 @@ import android.util.SparseArray;
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
@@ -50,6 +49,9 @@ public class SystemVibrator extends Vibrator {
|
||||
private final ArrayMap<OnVibratorStateChangedListener, AllVibratorsStateListener>
|
||||
mRegisteredListeners = new ArrayMap<>();
|
||||
|
||||
private final Object mLock = new Object();
|
||||
private AllVibratorsInfo mVibratorInfo;
|
||||
|
||||
@UnsupportedAppUsage
|
||||
public SystemVibrator(Context context) {
|
||||
super(context);
|
||||
@@ -57,6 +59,25 @@ public class SystemVibrator extends Vibrator {
|
||||
mVibratorManager = mContext.getSystemService(VibratorManager.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected VibratorInfo getInfo() {
|
||||
synchronized (mLock) {
|
||||
if (mVibratorInfo != null) {
|
||||
return mVibratorInfo;
|
||||
}
|
||||
if (mVibratorManager == null) {
|
||||
Log.w(TAG, "Failed to retrieve vibrator info; no vibrator manager.");
|
||||
return VibratorInfo.EMPTY_VIBRATOR_INFO;
|
||||
}
|
||||
int[] vibratorIds = mVibratorManager.getVibratorIds();
|
||||
VibratorInfo[] vibratorInfos = new VibratorInfo[vibratorIds.length];
|
||||
for (int i = 0; i < vibratorIds.length; i++) {
|
||||
vibratorInfos[i] = mVibratorManager.getVibrator(vibratorIds[i]).getInfo();
|
||||
}
|
||||
return mVibratorInfo = new AllVibratorsInfo(vibratorInfos);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasVibrator() {
|
||||
if (mVibratorManager == null) {
|
||||
@@ -144,20 +165,7 @@ public class SystemVibrator extends Vibrator {
|
||||
|
||||
@Override
|
||||
public boolean hasAmplitudeControl() {
|
||||
if (mVibratorManager == null) {
|
||||
Log.w(TAG, "Failed to check vibrator has amplitude control; no vibrator manager.");
|
||||
return false;
|
||||
}
|
||||
int[] vibratorIds = mVibratorManager.getVibratorIds();
|
||||
if (vibratorIds.length == 0) {
|
||||
return false;
|
||||
}
|
||||
for (int vibratorId : vibratorIds) {
|
||||
if (!mVibratorManager.getVibrator(vibratorId).hasAmplitudeControl()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return getInfo().hasAmplitudeControl();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -183,70 +191,6 @@ public class SystemVibrator extends Vibrator {
|
||||
mVibratorManager.vibrate(uid, opPkg, combinedEffect, reason, attributes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] areEffectsSupported(@VibrationEffect.EffectType int... effectIds) {
|
||||
int[] supported = new int[effectIds.length];
|
||||
if (mVibratorManager == null) {
|
||||
Log.w(TAG, "Failed to check supported effects; no vibrator manager.");
|
||||
Arrays.fill(supported, Vibrator.VIBRATION_EFFECT_SUPPORT_NO);
|
||||
return supported;
|
||||
}
|
||||
int[] vibratorIds = mVibratorManager.getVibratorIds();
|
||||
if (vibratorIds.length == 0) {
|
||||
Arrays.fill(supported, Vibrator.VIBRATION_EFFECT_SUPPORT_NO);
|
||||
return supported;
|
||||
}
|
||||
int[][] vibratorSupportMap = new int[vibratorIds.length][effectIds.length];
|
||||
for (int i = 0; i < vibratorIds.length; i++) {
|
||||
vibratorSupportMap[i] = mVibratorManager.getVibrator(
|
||||
vibratorIds[i]).areEffectsSupported(effectIds);
|
||||
}
|
||||
Arrays.fill(supported, Vibrator.VIBRATION_EFFECT_SUPPORT_YES);
|
||||
for (int effectIdx = 0; effectIdx < effectIds.length; effectIdx++) {
|
||||
for (int vibratorIdx = 0; vibratorIdx < vibratorIds.length; vibratorIdx++) {
|
||||
int effectSupported = vibratorSupportMap[vibratorIdx][effectIdx];
|
||||
if (effectSupported == Vibrator.VIBRATION_EFFECT_SUPPORT_NO) {
|
||||
supported[effectIdx] = Vibrator.VIBRATION_EFFECT_SUPPORT_NO;
|
||||
break;
|
||||
} else if (effectSupported == Vibrator.VIBRATION_EFFECT_SUPPORT_UNKNOWN) {
|
||||
supported[effectIdx] = Vibrator.VIBRATION_EFFECT_SUPPORT_UNKNOWN;
|
||||
}
|
||||
}
|
||||
}
|
||||
return supported;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean[] arePrimitivesSupported(
|
||||
@NonNull @VibrationEffect.Composition.PrimitiveType int... primitiveIds) {
|
||||
boolean[] supported = new boolean[primitiveIds.length];
|
||||
if (mVibratorManager == null) {
|
||||
Log.w(TAG, "Failed to check supported primitives; no vibrator manager.");
|
||||
Arrays.fill(supported, false);
|
||||
return supported;
|
||||
}
|
||||
int[] vibratorIds = mVibratorManager.getVibratorIds();
|
||||
if (vibratorIds.length == 0) {
|
||||
Arrays.fill(supported, false);
|
||||
return supported;
|
||||
}
|
||||
boolean[][] vibratorSupportMap = new boolean[vibratorIds.length][primitiveIds.length];
|
||||
for (int i = 0; i < vibratorIds.length; i++) {
|
||||
vibratorSupportMap[i] = mVibratorManager.getVibrator(
|
||||
vibratorIds[i]).arePrimitivesSupported(primitiveIds);
|
||||
}
|
||||
Arrays.fill(supported, true);
|
||||
for (int primitiveIdx = 0; primitiveIdx < primitiveIds.length; primitiveIdx++) {
|
||||
for (int vibratorIdx = 0; vibratorIdx < vibratorIds.length; vibratorIdx++) {
|
||||
if (!vibratorSupportMap[vibratorIdx][primitiveIdx]) {
|
||||
supported[primitiveIdx] = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return supported;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
if (mVibratorManager == null) {
|
||||
@@ -295,6 +239,58 @@ public class SystemVibrator extends Vibrator {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents all the vibrators information 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
|
||||
* support.
|
||||
*/
|
||||
private static class AllVibratorsInfo extends VibratorInfo {
|
||||
private final VibratorInfo[] mVibratorInfos;
|
||||
|
||||
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) {
|
||||
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) {
|
||||
for (VibratorInfo info : mVibratorInfos) {
|
||||
if (!info.isPrimitiveSupported(primitiveId)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static int capabilitiesIntersection(VibratorInfo[] infos) {
|
||||
if (infos.length == 0) {
|
||||
return 0;
|
||||
}
|
||||
int intersection = ~0;
|
||||
for (VibratorInfo info : infos) {
|
||||
intersection &= info.getCapabilities();
|
||||
}
|
||||
return intersection;
|
||||
}
|
||||
}
|
||||
|
||||
/** Listener for all vibrators state change. */
|
||||
private static class AllVibratorsStateListener {
|
||||
private final Object mLock = new Object();
|
||||
|
||||
@@ -185,8 +185,8 @@ public class SystemVibratorManager extends VibratorManager {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return mVibratorInfo.getId();
|
||||
protected VibratorInfo getInfo() {
|
||||
return mVibratorInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -199,36 +199,6 @@ public class SystemVibratorManager extends VibratorManager {
|
||||
return mVibratorInfo.hasAmplitudeControl();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getResonantFrequency() {
|
||||
return mVibratorInfo.getResonantFrequency();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getQFactor() {
|
||||
return mVibratorInfo.getQFactor();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public int[] areEffectsSupported(@NonNull int... effectIds) {
|
||||
int[] supported = new int[effectIds.length];
|
||||
for (int i = 0; i < effectIds.length; i++) {
|
||||
supported[i] = mVibratorInfo.isEffectSupported(effectIds[i]);
|
||||
}
|
||||
return supported;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean[] arePrimitivesSupported(
|
||||
@NonNull @VibrationEffect.Composition.PrimitiveType int... primitiveIds) {
|
||||
boolean[] supported = new boolean[primitiveIds.length];
|
||||
for (int i = 0; i < primitiveIds.length; i++) {
|
||||
supported[i] = mVibratorInfo.isPrimitiveSupported(primitiveIds[i]);
|
||||
}
|
||||
return supported;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setAlwaysOnEffect(int uid, String opPkg, int alwaysOnId,
|
||||
@Nullable VibrationEffect effect, @Nullable AudioAttributes attributes) {
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package android.os;
|
||||
|
||||
import android.annotation.CallbackExecutor;
|
||||
import android.annotation.FloatRange;
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
@@ -26,12 +27,13 @@ import android.annotation.SystemService;
|
||||
import android.app.ActivityThread;
|
||||
import android.compat.annotation.UnsupportedAppUsage;
|
||||
import android.content.Context;
|
||||
import android.hardware.vibrator.IVibrator;
|
||||
import android.media.AudioAttributes;
|
||||
import android.util.Log;
|
||||
import android.util.Range;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
/**
|
||||
@@ -156,6 +158,11 @@ public abstract class Vibrator {
|
||||
return ctx != null ? ctx.getResources().getInteger(resId) : VIBRATION_INTENSITY_MEDIUM;
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
protected VibratorInfo getInfo() {
|
||||
return VibratorInfo.EMPTY_VIBRATOR_INFO;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default vibration intensity for haptic feedback.
|
||||
*
|
||||
@@ -190,7 +197,7 @@ public abstract class Vibrator {
|
||||
* service, or -1 this service is not attached to any physical vibrator.
|
||||
*/
|
||||
public int getId() {
|
||||
return -1;
|
||||
return getInfo().getId();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -207,15 +214,27 @@ public abstract class Vibrator {
|
||||
*/
|
||||
public abstract boolean hasAmplitudeControl();
|
||||
|
||||
/**
|
||||
* Check whether the vibrator has independent frequency control.
|
||||
*
|
||||
* @return True if the hardware can control the frequency of the vibrations, otherwise false.
|
||||
* @hide
|
||||
*/
|
||||
public boolean hasFrequencyControl() {
|
||||
// We currently can only control frequency of the vibration using the compose PWLE method.
|
||||
return getInfo().hasCapability(
|
||||
IVibrator.CAP_FREQUENCY_CONTROL | IVibrator.CAP_COMPOSE_PWLE_EFFECTS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the resonant frequency of the vibrator.
|
||||
*
|
||||
* @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.
|
||||
* this vibrator is a composite of multiple physical devices.
|
||||
* @hide
|
||||
*/
|
||||
public float getResonantFrequency() {
|
||||
return Float.NaN;
|
||||
return getInfo().getResonantFrequency();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -226,7 +245,44 @@ public abstract class Vibrator {
|
||||
* @hide
|
||||
*/
|
||||
public float getQFactor() {
|
||||
return Float.NaN;
|
||||
return getInfo().getQFactor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a range of relative frequency values supported by the vibrator.
|
||||
*
|
||||
* <p>These values can be used to create waveforms that controls the vibration frequency via
|
||||
* {@link VibrationEffect.WaveformBuilder}.
|
||||
*
|
||||
* @return A range of relative frequency values supported. The range will always contain the
|
||||
* value 0, representing the device resonant frequency. Devices without frequency control will
|
||||
* return the range [0,0]. Devices with frequency control will always return a range containing
|
||||
* the safe range [-1, 1].
|
||||
* @hide
|
||||
*/
|
||||
public Range<Float> getRelativeFrequencyRange() {
|
||||
return getInfo().getFrequencyRange();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the maximum amplitude the vibrator can play at given relative frequency.
|
||||
*
|
||||
* <p>Devices without frequency control will return 1 for the input zero (resonant frequency),
|
||||
* and 0 to any other input.
|
||||
*
|
||||
* <p>Devices with frequency control will return the supported value, for input in
|
||||
* {@link #getRelativeFrequencyRange()}, and 0 for any other input.
|
||||
*
|
||||
* <p>These values can be used to create waveforms that plays vibrations outside the resonant
|
||||
* frequency via {@link VibrationEffect.WaveformBuilder}.
|
||||
*
|
||||
* @return a value in [0,1] representing the maximum amplitude the device can play at given
|
||||
* relative frequency.
|
||||
* @hide
|
||||
*/
|
||||
@FloatRange(from = 0, to = 1)
|
||||
public float getMaximumAmplitude(float relativeFrequency) {
|
||||
return getInfo().getMaxAmplitude(relativeFrequency);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -414,9 +470,12 @@ public abstract class Vibrator {
|
||||
@VibrationEffectSupport
|
||||
public int[] areEffectsSupported(
|
||||
@NonNull @VibrationEffect.EffectType int... effectIds) {
|
||||
final int[] support = new int[effectIds.length];
|
||||
Arrays.fill(support, VIBRATION_EFFECT_SUPPORT_NO);
|
||||
return support;
|
||||
VibratorInfo info = getInfo();
|
||||
int[] supported = new int[effectIds.length];
|
||||
for (int i = 0; i < effectIds.length; i++) {
|
||||
supported[i] = info.isEffectSupported(effectIds[i]);
|
||||
}
|
||||
return supported;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -468,7 +527,12 @@ public abstract class Vibrator {
|
||||
@NonNull
|
||||
public boolean[] arePrimitivesSupported(
|
||||
@NonNull @VibrationEffect.Composition.PrimitiveType int... primitiveIds) {
|
||||
return new boolean[primitiveIds.length];
|
||||
VibratorInfo info = getInfo();
|
||||
boolean[] supported = new boolean[primitiveIds.length];
|
||||
for (int i = 0; i < primitiveIds.length; i++) {
|
||||
supported[i] = info.isPrimitiveSupported(primitiveIds[i]);
|
||||
}
|
||||
return supported;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -38,9 +38,12 @@ import java.util.Objects;
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public final class VibratorInfo implements Parcelable {
|
||||
public class VibratorInfo implements Parcelable {
|
||||
private static final String TAG = "VibratorInfo";
|
||||
|
||||
/** @hide */
|
||||
public static final VibratorInfo EMPTY_VIBRATOR_INFO = new VibratorInfo.Builder(-1).build();
|
||||
|
||||
private final int mId;
|
||||
private final long mCapabilities;
|
||||
@Nullable
|
||||
@@ -74,6 +77,23 @@ public final class VibratorInfo implements Parcelable {
|
||||
mFrequencyMapping = frequencyMapping;
|
||||
}
|
||||
|
||||
protected VibratorInfo(int id, int capabilities, VibratorInfo baseVibrator) {
|
||||
mId = id;
|
||||
mCapabilities = capabilities;
|
||||
mSupportedEffects = baseVibrator.mSupportedEffects == null ? null :
|
||||
baseVibrator.mSupportedEffects.clone();
|
||||
mSupportedBraking = baseVibrator.mSupportedBraking == null ? null :
|
||||
baseVibrator.mSupportedBraking.clone();
|
||||
mSupportedPrimitives = baseVibrator.mSupportedPrimitives == null ? null :
|
||||
baseVibrator.mSupportedPrimitives.clone();
|
||||
mQFactor = baseVibrator.mQFactor;
|
||||
mFrequencyMapping = new FrequencyMapping(baseVibrator.mFrequencyMapping.mMinFrequencyHz,
|
||||
baseVibrator.mFrequencyMapping.mResonantFrequencyHz,
|
||||
baseVibrator.mFrequencyMapping.mFrequencyResolutionHz,
|
||||
baseVibrator.mFrequencyMapping.mSuggestedSafeRangeHz,
|
||||
baseVibrator.mFrequencyMapping.mMaxAmplitudes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeInt(mId);
|
||||
@@ -145,6 +165,7 @@ public final class VibratorInfo implements Parcelable {
|
||||
* Returns a default value to be applied to composed PWLE effects for braking.
|
||||
*
|
||||
* @return a supported braking value, one of android.hardware.vibrator.Braking.*
|
||||
* @hide
|
||||
*/
|
||||
public int getDefaultBraking() {
|
||||
if (mSupportedBraking != null) {
|
||||
@@ -265,6 +286,10 @@ public final class VibratorInfo implements Parcelable {
|
||||
return mFrequencyMapping.toHertz(relativeFrequency);
|
||||
}
|
||||
|
||||
protected long getCapabilities() {
|
||||
return mCapabilities;
|
||||
}
|
||||
|
||||
private String[] getCapabilitiesNames() {
|
||||
List<String> names = new ArrayList<>();
|
||||
if (hasCapability(IVibrator.CAP_ON_CALLBACK)) {
|
||||
@@ -370,7 +395,7 @@ public final class VibratorInfo implements Parcelable {
|
||||
* <p>The mapping is defined linearly by the following points:
|
||||
*
|
||||
* <ol>
|
||||
* <li>{@code toHertz(relativeMinFrequency} = minFrequency
|
||||
* <li>{@code toHertz(relativeMinFrequency) = minFrequency}
|
||||
* <li>{@code toHertz(-1) = resonantFrequency - safeRange / 2}
|
||||
* <li>{@code toHertz(0) = resonantFrequency}
|
||||
* <li>{@code toHertz(1) = resonantFrequency + safeRange / 2}
|
||||
@@ -555,6 +580,75 @@ public final class VibratorInfo implements Parcelable {
|
||||
};
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public static final class Builder {
|
||||
private final int mId;
|
||||
private int mCapabilities = 0;
|
||||
private int[] mSupportedEffects = null;
|
||||
private int[] mSupportedBraking = null;
|
||||
private int[] mSupportedPrimitives = null;
|
||||
private float mQFactor = Float.NaN;
|
||||
private FrequencyMapping mFrequencyMapping =
|
||||
new FrequencyMapping(Float.NaN, Float.NaN, Float.NaN, Float.NaN, null);
|
||||
|
||||
/** A builder class for a {@link VibratorInfo}. */
|
||||
public Builder(int id) {
|
||||
mId = id;
|
||||
}
|
||||
|
||||
/** Configure the vibrator capabilities with a combination of IVibrator.CAP_* values. */
|
||||
@NonNull
|
||||
public Builder setCapabilities(int capabilities) {
|
||||
mCapabilities = capabilities;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Configure the effects supported with {@link android.hardware.vibrator.Effect} values. */
|
||||
@NonNull
|
||||
public Builder setSupportedEffects(int... supportedEffects) {
|
||||
mSupportedEffects = supportedEffects;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Configure braking supported with {@link android.hardware.vibrator.Braking} values. */
|
||||
@NonNull
|
||||
public Builder setSupportedBraking(int... supportedBraking) {
|
||||
mSupportedBraking = supportedBraking;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the primitives supported with
|
||||
* {@link android.hardware.vibrator.CompositePrimitive} values.
|
||||
*/
|
||||
@NonNull
|
||||
public Builder setSupportedPrimitives(int... supportedPrimitives) {
|
||||
mSupportedPrimitives = supportedPrimitives;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Configure the vibrator quality factor. */
|
||||
@NonNull
|
||||
public Builder setQFactor(float qFactor) {
|
||||
mQFactor = qFactor;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Configure the vibrator frequency information like resonant frequency and bandwidth. */
|
||||
@NonNull
|
||||
public Builder setFrequencyMapping(FrequencyMapping frequencyMapping) {
|
||||
mFrequencyMapping = frequencyMapping;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Build the configured {@link VibratorInfo}. */
|
||||
@NonNull
|
||||
public VibratorInfo build() {
|
||||
return new VibratorInfo(mId, mCapabilities, mSupportedEffects, mSupportedBraking,
|
||||
mSupportedPrimitives, mQFactor, mFrequencyMapping);
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static final Creator<VibratorInfo> CREATOR =
|
||||
new Creator<VibratorInfo>() {
|
||||
|
||||
@@ -35,6 +35,7 @@ import org.junit.runners.JUnit4;
|
||||
public class VibratorInfoTest {
|
||||
private static final float TEST_TOLERANCE = 1e-5f;
|
||||
|
||||
private static final int TEST_VIBRATOR_ID = 1;
|
||||
private static final float TEST_MIN_FREQUENCY = 50;
|
||||
private static final float TEST_RESONANT_FREQUENCY = 150;
|
||||
private static final float TEST_FREQUENCY_RESOLUTION = 25;
|
||||
@@ -50,9 +51,9 @@ public class VibratorInfoTest {
|
||||
|
||||
@Test
|
||||
public void testHasAmplitudeControl() {
|
||||
VibratorInfo noCapabilities = new InfoBuilder().build();
|
||||
VibratorInfo noCapabilities = new VibratorInfo.Builder(TEST_VIBRATOR_ID).build();
|
||||
assertFalse(noCapabilities.hasAmplitudeControl());
|
||||
VibratorInfo composeAndAmplitudeControl = new InfoBuilder()
|
||||
VibratorInfo composeAndAmplitudeControl = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
|
||||
.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS
|
||||
| IVibrator.CAP_AMPLITUDE_CONTROL)
|
||||
.build();
|
||||
@@ -61,7 +62,7 @@ public class VibratorInfoTest {
|
||||
|
||||
@Test
|
||||
public void testHasCapabilities() {
|
||||
VibratorInfo info = new InfoBuilder()
|
||||
VibratorInfo info = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
|
||||
.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
|
||||
.build();
|
||||
assertTrue(info.hasCapability(IVibrator.CAP_COMPOSE_EFFECTS));
|
||||
@@ -70,8 +71,8 @@ public class VibratorInfoTest {
|
||||
|
||||
@Test
|
||||
public void testIsEffectSupported() {
|
||||
VibratorInfo noEffects = new InfoBuilder().build();
|
||||
VibratorInfo canClick = new InfoBuilder()
|
||||
VibratorInfo noEffects = new VibratorInfo.Builder(TEST_VIBRATOR_ID).build();
|
||||
VibratorInfo canClick = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
|
||||
.setSupportedEffects(VibrationEffect.EFFECT_CLICK)
|
||||
.build();
|
||||
assertEquals(Vibrator.VIBRATION_EFFECT_SUPPORT_UNKNOWN,
|
||||
@@ -84,7 +85,7 @@ public class VibratorInfoTest {
|
||||
|
||||
@Test
|
||||
public void testIsPrimitiveSupported() {
|
||||
VibratorInfo info = new InfoBuilder()
|
||||
VibratorInfo info = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
|
||||
.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
|
||||
.setSupportedPrimitives(VibrationEffect.Composition.PRIMITIVE_CLICK)
|
||||
.build();
|
||||
@@ -92,7 +93,7 @@ public class VibratorInfoTest {
|
||||
assertFalse(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_TICK));
|
||||
|
||||
// Returns false when there is no compose capability.
|
||||
info = new InfoBuilder()
|
||||
info = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
|
||||
.setSupportedPrimitives(VibrationEffect.Composition.PRIMITIVE_CLICK)
|
||||
.build();
|
||||
assertFalse(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_CLICK));
|
||||
@@ -100,9 +101,10 @@ public class VibratorInfoTest {
|
||||
|
||||
@Test
|
||||
public void testGetDefaultBraking_returnsFirstSupportedBraking() {
|
||||
assertEquals(Braking.NONE, new InfoBuilder().build().getDefaultBraking());
|
||||
assertEquals(Braking.NONE, new VibratorInfo.Builder(
|
||||
TEST_VIBRATOR_ID).build().getDefaultBraking());
|
||||
assertEquals(Braking.CLAB,
|
||||
new InfoBuilder()
|
||||
new VibratorInfo.Builder(TEST_VIBRATOR_ID)
|
||||
.setSupportedBraking(Braking.NONE, Braking.CLAB)
|
||||
.build()
|
||||
.getDefaultBraking());
|
||||
@@ -111,33 +113,34 @@ public class VibratorInfoTest {
|
||||
@Test
|
||||
public void testGetFrequencyRange_invalidFrequencyMappingReturnsEmptyRange() {
|
||||
// Invalid, contains NaN values or empty array.
|
||||
assertEquals(Range.create(0f, 0f), new InfoBuilder().build().getFrequencyRange());
|
||||
assertEquals(Range.create(0f, 0f), new InfoBuilder()
|
||||
assertEquals(Range.create(0f, 0f), new VibratorInfo.Builder(
|
||||
TEST_VIBRATOR_ID).build().getFrequencyRange());
|
||||
assertEquals(Range.create(0f, 0f), new VibratorInfo.Builder(TEST_VIBRATOR_ID)
|
||||
.setFrequencyMapping(new VibratorInfo.FrequencyMapping(
|
||||
Float.NaN, 150, 25, 50, TEST_AMPLITUDE_MAP))
|
||||
.build().getFrequencyRange());
|
||||
assertEquals(Range.create(0f, 0f), new InfoBuilder()
|
||||
assertEquals(Range.create(0f, 0f), new VibratorInfo.Builder(TEST_VIBRATOR_ID)
|
||||
.setFrequencyMapping(new VibratorInfo.FrequencyMapping(
|
||||
50, Float.NaN, 25, 50, TEST_AMPLITUDE_MAP))
|
||||
.build().getFrequencyRange());
|
||||
assertEquals(Range.create(0f, 0f), new InfoBuilder()
|
||||
assertEquals(Range.create(0f, 0f), new VibratorInfo.Builder(TEST_VIBRATOR_ID)
|
||||
.setFrequencyMapping(new VibratorInfo.FrequencyMapping(
|
||||
50, 150, Float.NaN, 50, TEST_AMPLITUDE_MAP))
|
||||
.build().getFrequencyRange());
|
||||
assertEquals(Range.create(0f, 0f), new InfoBuilder()
|
||||
assertEquals(Range.create(0f, 0f), new VibratorInfo.Builder(TEST_VIBRATOR_ID)
|
||||
.setFrequencyMapping(new VibratorInfo.FrequencyMapping(
|
||||
50, 150, 25, Float.NaN, TEST_AMPLITUDE_MAP))
|
||||
.build().getFrequencyRange());
|
||||
assertEquals(Range.create(0f, 0f), new InfoBuilder()
|
||||
assertEquals(Range.create(0f, 0f), new VibratorInfo.Builder(TEST_VIBRATOR_ID)
|
||||
.setFrequencyMapping(new VibratorInfo.FrequencyMapping(50, 150, 25, 50, null))
|
||||
.build().getFrequencyRange());
|
||||
// Invalid, minFrequency > resonantFrequency
|
||||
assertEquals(Range.create(0f, 0f), new InfoBuilder()
|
||||
assertEquals(Range.create(0f, 0f), new VibratorInfo.Builder(TEST_VIBRATOR_ID)
|
||||
.setFrequencyMapping(new VibratorInfo.FrequencyMapping(
|
||||
/* minFrequencyHz= */ 250, /* resonantFrequency= */ 150, 25, 50, null))
|
||||
.build().getFrequencyRange());
|
||||
// Invalid, maxFrequency < resonantFrequency by changing resolution.
|
||||
assertEquals(Range.create(0f, 0f), new InfoBuilder()
|
||||
assertEquals(Range.create(0f, 0f), new VibratorInfo.Builder(TEST_VIBRATOR_ID)
|
||||
.setFrequencyMapping(new VibratorInfo.FrequencyMapping(
|
||||
50, 150, /* frequencyResolutionHz= */10, 50, null))
|
||||
.build().getFrequencyRange());
|
||||
@@ -145,7 +148,7 @@ public class VibratorInfoTest {
|
||||
|
||||
@Test
|
||||
public void testGetFrequencyRange_safeRangeLimitedByMaxFrequency() {
|
||||
VibratorInfo info = new InfoBuilder()
|
||||
VibratorInfo info = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
|
||||
.setFrequencyMapping(new VibratorInfo.FrequencyMapping(
|
||||
/* minFrequencyHz= */ 50, /* resonantFrequencyHz= */ 150,
|
||||
/* frequencyResolutionHz= */ 25, /* suggestedSafeRangeHz= */ 200,
|
||||
@@ -159,7 +162,7 @@ public class VibratorInfoTest {
|
||||
|
||||
@Test
|
||||
public void testGetFrequencyRange_safeRangeLimitedByMinFrequency() {
|
||||
VibratorInfo info = new InfoBuilder()
|
||||
VibratorInfo info = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
|
||||
.setFrequencyMapping(new VibratorInfo.FrequencyMapping(
|
||||
/* minFrequencyHz= */ 50, /* resonantFrequencyHz= */ 150,
|
||||
/* frequencyResolutionHz= */ 50, /* suggestedSafeRangeHz= */ 200,
|
||||
@@ -173,7 +176,7 @@ public class VibratorInfoTest {
|
||||
|
||||
@Test
|
||||
public void testGetFrequencyRange_validMappingReturnsFullRelativeRange() {
|
||||
VibratorInfo info = new InfoBuilder()
|
||||
VibratorInfo info = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
|
||||
.setFrequencyMapping(new VibratorInfo.FrequencyMapping(
|
||||
/* minFrequencyHz= */ 50, /* resonantFrequencyHz= */ 150,
|
||||
/* frequencyResolutionHz= */ 50, /* suggestedSafeRangeHz= */ 100,
|
||||
@@ -187,7 +190,7 @@ public class VibratorInfoTest {
|
||||
|
||||
@Test
|
||||
public void testAbsoluteFrequency_emptyMappingReturnsNaN() {
|
||||
VibratorInfo info = new InfoBuilder().build();
|
||||
VibratorInfo info = new VibratorInfo.Builder(TEST_VIBRATOR_ID).build();
|
||||
assertTrue(Float.isNaN(info.getAbsoluteFrequency(-1)));
|
||||
assertTrue(Float.isNaN(info.getAbsoluteFrequency(0)));
|
||||
assertTrue(Float.isNaN(info.getAbsoluteFrequency(1)));
|
||||
@@ -195,7 +198,8 @@ public class VibratorInfoTest {
|
||||
|
||||
@Test
|
||||
public void testAbsoluteFrequency_validRangeReturnsOriginalValue() {
|
||||
VibratorInfo info = new InfoBuilder().setFrequencyMapping(TEST_FREQUENCY_MAPPING).build();
|
||||
VibratorInfo info = new VibratorInfo.Builder(TEST_VIBRATOR_ID).setFrequencyMapping(
|
||||
TEST_FREQUENCY_MAPPING).build();
|
||||
assertEquals(TEST_RESONANT_FREQUENCY, info.getAbsoluteFrequency(0), TEST_TOLERANCE);
|
||||
|
||||
// Safe range [-1, 1] = [125Hz, 175Hz] defined by suggested safe range 100Hz
|
||||
@@ -213,7 +217,7 @@ public class VibratorInfoTest {
|
||||
|
||||
@Test
|
||||
public void testGetMaxAmplitude_emptyMappingReturnsOnlyResonantFrequency() {
|
||||
VibratorInfo info = new InfoBuilder().build();
|
||||
VibratorInfo info = new VibratorInfo.Builder(TEST_VIBRATOR_ID).build();
|
||||
assertEquals(1f, info.getMaxAmplitude(0), TEST_TOLERANCE);
|
||||
assertEquals(0f, info.getMaxAmplitude(0.1f), TEST_TOLERANCE);
|
||||
assertEquals(0f, info.getMaxAmplitude(-1), TEST_TOLERANCE);
|
||||
@@ -221,7 +225,7 @@ public class VibratorInfoTest {
|
||||
|
||||
@Test
|
||||
public void testGetMaxAmplitude_validMappingReturnsMappedValues() {
|
||||
VibratorInfo info = new InfoBuilder()
|
||||
VibratorInfo info = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
|
||||
.setFrequencyMapping(new VibratorInfo.FrequencyMapping(/* minFrequencyHz= */ 50,
|
||||
/* resonantFrequencyHz= */ 150, /* frequencyResolutionHz= */ 25,
|
||||
/* suggestedSafeRangeHz= */ 50, TEST_AMPLITUDE_MAP))
|
||||
@@ -243,8 +247,7 @@ public class VibratorInfoTest {
|
||||
|
||||
@Test
|
||||
public void testEquals() {
|
||||
InfoBuilder completeBuilder = new InfoBuilder()
|
||||
.setId(1)
|
||||
VibratorInfo.Builder completeBuilder = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
|
||||
.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL)
|
||||
.setSupportedEffects(VibrationEffect.EFFECT_CLICK)
|
||||
.setSupportedPrimitives(VibrationEffect.Composition.PRIMITIVE_CLICK)
|
||||
@@ -298,9 +301,8 @@ public class VibratorInfoTest {
|
||||
.build();
|
||||
assertNotEquals(complete, completeWithDifferentQFactor);
|
||||
|
||||
VibratorInfo empty = new InfoBuilder().setId(1).build();
|
||||
VibratorInfo emptyWithKnownSupport = new InfoBuilder()
|
||||
.setId(1)
|
||||
VibratorInfo empty = new VibratorInfo.Builder(TEST_VIBRATOR_ID).build();
|
||||
VibratorInfo emptyWithKnownSupport = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
|
||||
.setSupportedEffects()
|
||||
.setSupportedPrimitives()
|
||||
.build();
|
||||
@@ -309,8 +311,7 @@ public class VibratorInfoTest {
|
||||
|
||||
@Test
|
||||
public void testParceling() {
|
||||
VibratorInfo original = new InfoBuilder()
|
||||
.setId(1)
|
||||
VibratorInfo original = new VibratorInfo.Builder(TEST_VIBRATOR_ID)
|
||||
.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS)
|
||||
.setSupportedEffects(VibrationEffect.EFFECT_CLICK)
|
||||
.setSupportedPrimitives(null)
|
||||
@@ -324,54 +325,4 @@ public class VibratorInfoTest {
|
||||
VibratorInfo restored = VibratorInfo.CREATOR.createFromParcel(parcel);
|
||||
assertEquals(original, restored);
|
||||
}
|
||||
|
||||
private static class InfoBuilder {
|
||||
private int mId = 0;
|
||||
private int mCapabilities = 0;
|
||||
private int[] mSupportedEffects = null;
|
||||
private int[] mSupportedBraking = null;
|
||||
private int[] mSupportedPrimitives = null;
|
||||
private float mQFactor = Float.NaN;
|
||||
private VibratorInfo.FrequencyMapping mFrequencyMapping = EMPTY_FREQUENCY_MAPPING;
|
||||
|
||||
public InfoBuilder setId(int id) {
|
||||
mId = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
public InfoBuilder setCapabilities(int capabilities) {
|
||||
mCapabilities = capabilities;
|
||||
return this;
|
||||
}
|
||||
|
||||
public InfoBuilder setSupportedEffects(int... supportedEffects) {
|
||||
mSupportedEffects = supportedEffects;
|
||||
return this;
|
||||
}
|
||||
|
||||
public InfoBuilder setSupportedBraking(int... supportedBraking) {
|
||||
mSupportedBraking = supportedBraking;
|
||||
return this;
|
||||
}
|
||||
|
||||
public InfoBuilder setSupportedPrimitives(int... supportedPrimitives) {
|
||||
mSupportedPrimitives = supportedPrimitives;
|
||||
return this;
|
||||
}
|
||||
|
||||
public InfoBuilder setQFactor(float qFactor) {
|
||||
mQFactor = qFactor;
|
||||
return this;
|
||||
}
|
||||
|
||||
public InfoBuilder setFrequencyMapping(VibratorInfo.FrequencyMapping frequencyMapping) {
|
||||
mFrequencyMapping = frequencyMapping;
|
||||
return this;
|
||||
}
|
||||
|
||||
public VibratorInfo build() {
|
||||
return new VibratorInfo(mId, mCapabilities, mSupportedEffects, mSupportedBraking,
|
||||
mSupportedPrimitives, mQFactor, mFrequencyMapping);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,6 +54,11 @@ public class VibratorTest {
|
||||
mVibratorSpy = spy(InstrumentationRegistry.getContext().getSystemService(Vibrator.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getId_returnsDefaultId() {
|
||||
assertEquals(-1, mVibratorSpy.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void areEffectsSupported_returnsArrayOfSameSize() {
|
||||
assertEquals(0, mVibratorSpy.areEffectsSupported(new int[0]).length);
|
||||
|
||||
@@ -182,7 +182,9 @@ public class DeviceVibrationEffectAdapterTest {
|
||||
private static VibratorInfo createVibratorInfo(VibratorInfo.FrequencyMapping frequencyMapping,
|
||||
int... capabilities) {
|
||||
int cap = IntStream.of(capabilities).reduce((a, b) -> a | b).orElse(0);
|
||||
return new VibratorInfo(/* id= */ 0, cap, null, null, null, /* qFactor= */ Float.NaN,
|
||||
frequencyMapping);
|
||||
return new VibratorInfo.Builder(0)
|
||||
.setCapabilities(cap)
|
||||
.setFrequencyMapping(frequencyMapping)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -298,8 +298,10 @@ public class VibratorControllerTest {
|
||||
VibratorInfo.FrequencyMapping frequencyMapping = new VibratorInfo.FrequencyMapping(
|
||||
Float.NaN, Float.NaN, Float.NaN, Float.NaN, null);
|
||||
when(mNativeWrapperMock.getInfo(/* suggestedFrequencyRange= */ 100)).thenReturn(
|
||||
new VibratorInfo(VIBRATOR_ID, capabilities, null, null, null, Float.NaN,
|
||||
frequencyMapping));
|
||||
new VibratorInfo.Builder(VIBRATOR_ID)
|
||||
.setCapabilities(capabilities)
|
||||
.setFrequencyMapping(frequencyMapping)
|
||||
.build());
|
||||
}
|
||||
|
||||
private PrebakedSegment createPrebaked(int effectId, int effectStrength) {
|
||||
|
||||
Reference in New Issue
Block a user