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:
Lais Andrade 2022-01-13 14:07:39 +00:00 committed by Automerger Merge Worker
commit 023883e4b1
4 changed files with 108 additions and 24 deletions

View File

@ -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>

View File

@ -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" />

View File

@ -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) {

View File

@ -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