Merge "Move PWLE notification vibration pattern to config.xml" into sc-v2-dev am: 68fe893dd5
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/16584104 Change-Id: Iacb132b119c3d52d8cddebe5a0778dbaa21c213e
This commit is contained in:
commit
023883e4b1
@ -2679,6 +2679,16 @@
|
||||
<item>350</item>
|
||||
</integer-array>
|
||||
|
||||
<!-- A vibration waveform for notifications that specify DEFAULT_VIBRATE.
|
||||
This value is a float array with values grouped as
|
||||
{ targetAmplitude (within [0,1]), targetFrequency [-1,1], duration (in milliseconds) }
|
||||
This is only applied on devices with vibration frequency control. If the device doesn't
|
||||
support frequency control, then the vibration specified in
|
||||
config_defaultNotificationVibePattern is used instead.
|
||||
-->
|
||||
<array name="config_defaultNotificationVibeWaveform">
|
||||
</array>
|
||||
|
||||
<!-- Vibrator pattern to be used as the default for notifications
|
||||
that do not specify vibration but vibrate anyway because the device
|
||||
is in vibrate mode.
|
||||
@ -2690,6 +2700,16 @@
|
||||
<item>100</item>
|
||||
</integer-array>
|
||||
|
||||
<!-- A vibration waveform for notifications that do not specify vibration but vibrate anyway,
|
||||
because the device is in vibrate mode. This value is a float array with values grouped as
|
||||
{ targetAmplitude (within [0,1]), targetFrequency [-1,1], duration (in milliseconds) }
|
||||
This is only applied on devices with vibration frequency control. If the device doesn't
|
||||
support frequency control, then the vibration specified in
|
||||
config_notificationFallbackVibePattern is used instead.
|
||||
-->
|
||||
<array name="config_notificationFallbackVibeWaveform">
|
||||
</array>
|
||||
|
||||
<!-- Flag indicating if the speed up audio on mt call code should be executed -->
|
||||
<bool name="config_speed_up_audio_on_mt_calls">false</bool>
|
||||
|
||||
|
@ -1917,7 +1917,9 @@
|
||||
<java-symbol type="array" name="config_locationExtraPackageNames" />
|
||||
<java-symbol type="array" name="config_testLocationProviders" />
|
||||
<java-symbol type="array" name="config_defaultNotificationVibePattern" />
|
||||
<java-symbol type="array" name="config_defaultNotificationVibeWaveform" />
|
||||
<java-symbol type="array" name="config_notificationFallbackVibePattern" />
|
||||
<java-symbol type="array" name="config_notificationFallbackVibeWaveform" />
|
||||
<java-symbol type="bool" name="config_enableServerNotificationEffectsForAutomotive" />
|
||||
<java-symbol type="bool" name="config_useAttentionLight" />
|
||||
<java-symbol type="bool" name="config_adaptive_sleep_available" />
|
||||
|
@ -19,6 +19,7 @@ package com.android.server.notification;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.TypedArray;
|
||||
import android.media.AudioAttributes;
|
||||
import android.os.Process;
|
||||
import android.os.VibrationAttributes;
|
||||
@ -39,18 +40,16 @@ public final class VibratorHelper {
|
||||
|
||||
private static final long[] DEFAULT_VIBRATE_PATTERN = {0, 250, 250, 250};
|
||||
private static final int VIBRATE_PATTERN_MAXLEN = 8 * 2 + 1; // up to eight bumps
|
||||
private static final int CHIRP_LEVEL_DURATION_MILLIS = 100;
|
||||
private static final int DEFAULT_CHIRP_RAMP_DURATION_MILLIS = 100;
|
||||
private static final int FALLBACK_CHIRP_RAMP_DURATION_MILLIS = 50;
|
||||
|
||||
private final Vibrator mVibrator;
|
||||
private final long[] mDefaultPattern;
|
||||
private final long[] mFallbackPattern;
|
||||
@Nullable private final float[] mDefaultPwlePattern;
|
||||
@Nullable private final float[] mFallbackPwlePattern;
|
||||
|
||||
public VibratorHelper(Context context) {
|
||||
mVibrator = context.getSystemService(Vibrator.class);
|
||||
mDefaultPattern = getLongArray(
|
||||
context.getResources(),
|
||||
mDefaultPattern = getLongArray(context.getResources(),
|
||||
com.android.internal.R.array.config_defaultNotificationVibePattern,
|
||||
VIBRATE_PATTERN_MAXLEN,
|
||||
DEFAULT_VIBRATE_PATTERN);
|
||||
@ -58,6 +57,10 @@ public final class VibratorHelper {
|
||||
R.array.config_notificationFallbackVibePattern,
|
||||
VIBRATE_PATTERN_MAXLEN,
|
||||
DEFAULT_VIBRATE_PATTERN);
|
||||
mDefaultPwlePattern = getFloatArray(context.getResources(),
|
||||
com.android.internal.R.array.config_defaultNotificationVibeWaveform);
|
||||
mFallbackPwlePattern = getFloatArray(context.getResources(),
|
||||
com.android.internal.R.array.config_notificationFallbackVibeWaveform);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -82,6 +85,50 @@ public final class VibratorHelper {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Safely create a {@link VibrationEffect} from given waveform description.
|
||||
*
|
||||
* <p>The waveform is described by a sequence of values for target amplitude, frequency and
|
||||
* duration, that are forwarded to
|
||||
* {@link VibrationEffect.WaveformBuilder#addRamp(float, float, int)}.
|
||||
*
|
||||
* <p>This method returns {@code null} if the pattern is also {@code null} or invalid.
|
||||
*
|
||||
* @param values The list of values describing the waveform as a sequence of target amplitude,
|
||||
* frequency and duration.
|
||||
* @param insistent {@code true} if the vibration should loop until it is cancelled.
|
||||
*/
|
||||
@Nullable
|
||||
public static VibrationEffect createPwleWaveformVibration(@Nullable float[] values,
|
||||
boolean insistent) {
|
||||
try {
|
||||
if (values == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int length = values.length;
|
||||
// The waveform is described by triples (amplitude, frequency, duration)
|
||||
if ((length == 0) || (length % 3 != 0)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
VibrationEffect.WaveformBuilder waveformBuilder = VibrationEffect.startWaveform();
|
||||
for (int i = 0; i < length; i += 3) {
|
||||
waveformBuilder.addRamp(/* amplitude= */ values[i], /* frequency= */ values[i + 1],
|
||||
/* duration= */ (int) values[i + 2]);
|
||||
}
|
||||
|
||||
if (insistent) {
|
||||
return waveformBuilder.build(/* repeat= */ 0);
|
||||
}
|
||||
return waveformBuilder.build();
|
||||
} catch (IllegalArgumentException e) {
|
||||
Slog.e(TAG, "Error creating vibration PWLE waveform with pattern: "
|
||||
+ Arrays.toString(values));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Vibrate the device with given {@code effect}.
|
||||
*
|
||||
@ -106,7 +153,10 @@ public final class VibratorHelper {
|
||||
*/
|
||||
public VibrationEffect createFallbackVibration(boolean insistent) {
|
||||
if (mVibrator.hasFrequencyControl()) {
|
||||
return createChirpVibration(FALLBACK_CHIRP_RAMP_DURATION_MILLIS, insistent);
|
||||
VibrationEffect effect = createPwleWaveformVibration(mFallbackPwlePattern, insistent);
|
||||
if (effect != null) {
|
||||
return effect;
|
||||
}
|
||||
}
|
||||
return createWaveformVibration(mFallbackPattern, insistent);
|
||||
}
|
||||
@ -118,29 +168,29 @@ public final class VibratorHelper {
|
||||
*/
|
||||
public VibrationEffect createDefaultVibration(boolean insistent) {
|
||||
if (mVibrator.hasFrequencyControl()) {
|
||||
return createChirpVibration(DEFAULT_CHIRP_RAMP_DURATION_MILLIS, insistent);
|
||||
VibrationEffect effect = createPwleWaveformVibration(mDefaultPwlePattern, insistent);
|
||||
if (effect != null) {
|
||||
return effect;
|
||||
}
|
||||
}
|
||||
return createWaveformVibration(mDefaultPattern, insistent);
|
||||
}
|
||||
|
||||
private static VibrationEffect createChirpVibration(int rampDuration, boolean insistent) {
|
||||
VibrationEffect.WaveformBuilder waveformBuilder = VibrationEffect.startWaveform()
|
||||
.addStep(/* amplitude= */ 0, /* frequency= */ -0.85f, /* duration= */ 0)
|
||||
.addRamp(/* amplitude= */ 1, /* frequency= */ -0.25f, rampDuration)
|
||||
.addStep(/* amplitude= */ 1, /* frequency= */ -0.25f, CHIRP_LEVEL_DURATION_MILLIS)
|
||||
.addRamp(/* amplitude= */ 0, /* frequency= */ -0.85f, rampDuration);
|
||||
|
||||
if (insistent) {
|
||||
return waveformBuilder
|
||||
.addStep(/* amplitude= */ 0, CHIRP_LEVEL_DURATION_MILLIS)
|
||||
.build(/* repeat= */ 0);
|
||||
@Nullable
|
||||
private static float[] getFloatArray(Resources resources, int resId) {
|
||||
TypedArray array = resources.obtainTypedArray(resId);
|
||||
try {
|
||||
float[] values = new float[array.length()];
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
values[i] = array.getFloat(i, Float.NaN);
|
||||
if (Float.isNaN(values[i])) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return values;
|
||||
} finally {
|
||||
array.recycle();
|
||||
}
|
||||
|
||||
VibrationEffect singleBeat = waveformBuilder.build();
|
||||
return VibrationEffect.startComposition()
|
||||
.addEffect(singleBeat)
|
||||
.addEffect(singleBeat, /* delay= */ CHIRP_LEVEL_DURATION_MILLIS)
|
||||
.compose();
|
||||
}
|
||||
|
||||
private static long[] getLongArray(Resources resources, int resId, int maxLength, long[] def) {
|
||||
|
@ -40,7 +40,10 @@ import org.mockito.MockitoAnnotations;
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class VibratorHelperTest extends UiServiceTestCase {
|
||||
|
||||
// OFF/ON vibration pattern
|
||||
private static final long[] CUSTOM_PATTERN = new long[] { 100, 200, 300, 400 };
|
||||
// (amplitude, frequency, duration) triples list
|
||||
private static final float[] PWLE_PATTERN = new float[] { 1, 0, 100 };
|
||||
|
||||
@Mock private Vibrator mVibrator;
|
||||
|
||||
@ -58,12 +61,16 @@ public class VibratorHelperTest extends UiServiceTestCase {
|
||||
public void createWaveformVibration_insistent_createsRepeatingVibration() {
|
||||
assertRepeatingVibration(
|
||||
VibratorHelper.createWaveformVibration(CUSTOM_PATTERN, /* insistent= */ true));
|
||||
assertRepeatingVibration(
|
||||
VibratorHelper.createPwleWaveformVibration(PWLE_PATTERN, /* insistent= */ true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createWaveformVibration_nonInsistent_createsSingleShotVibration() {
|
||||
assertSingleVibration(
|
||||
VibratorHelper.createWaveformVibration(CUSTOM_PATTERN, /* insistent= */ false));
|
||||
assertSingleVibration(
|
||||
VibratorHelper.createPwleWaveformVibration(PWLE_PATTERN, /* insistent= */ false));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -71,6 +78,11 @@ public class VibratorHelperTest extends UiServiceTestCase {
|
||||
assertNull(VibratorHelper.createWaveformVibration(null, false));
|
||||
assertNull(VibratorHelper.createWaveformVibration(new long[0], false));
|
||||
assertNull(VibratorHelper.createWaveformVibration(new long[] { 0, 0 }, false));
|
||||
|
||||
assertNull(VibratorHelper.createPwleWaveformVibration(null, false));
|
||||
assertNull(VibratorHelper.createPwleWaveformVibration(new float[0], false));
|
||||
assertNull(VibratorHelper.createPwleWaveformVibration(new float[] { 0 }, false));
|
||||
assertNull(VibratorHelper.createPwleWaveformVibration(new float[] { 0, 0, 0 }, false));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
Loading…
x
Reference in New Issue
Block a user