Merge "Move PWLE notification vibration pattern to config.xml" into sc-v2-dev
This commit is contained in:
@@ -2679,6 +2679,16 @@
|
|||||||
<item>350</item>
|
<item>350</item>
|
||||||
</integer-array>
|
</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
|
<!-- Vibrator pattern to be used as the default for notifications
|
||||||
that do not specify vibration but vibrate anyway because the device
|
that do not specify vibration but vibrate anyway because the device
|
||||||
is in vibrate mode.
|
is in vibrate mode.
|
||||||
@@ -2690,6 +2700,16 @@
|
|||||||
<item>100</item>
|
<item>100</item>
|
||||||
</integer-array>
|
</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 -->
|
<!-- 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>
|
<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_locationExtraPackageNames" />
|
||||||
<java-symbol type="array" name="config_testLocationProviders" />
|
<java-symbol type="array" name="config_testLocationProviders" />
|
||||||
<java-symbol type="array" name="config_defaultNotificationVibePattern" />
|
<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_notificationFallbackVibePattern" />
|
||||||
|
<java-symbol type="array" name="config_notificationFallbackVibeWaveform" />
|
||||||
<java-symbol type="bool" name="config_enableServerNotificationEffectsForAutomotive" />
|
<java-symbol type="bool" name="config_enableServerNotificationEffectsForAutomotive" />
|
||||||
<java-symbol type="bool" name="config_useAttentionLight" />
|
<java-symbol type="bool" name="config_useAttentionLight" />
|
||||||
<java-symbol type="bool" name="config_adaptive_sleep_available" />
|
<java-symbol type="bool" name="config_adaptive_sleep_available" />
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ package com.android.server.notification;
|
|||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
|
import android.content.res.TypedArray;
|
||||||
import android.media.AudioAttributes;
|
import android.media.AudioAttributes;
|
||||||
import android.os.Process;
|
import android.os.Process;
|
||||||
import android.os.VibrationAttributes;
|
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 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 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 Vibrator mVibrator;
|
||||||
private final long[] mDefaultPattern;
|
private final long[] mDefaultPattern;
|
||||||
private final long[] mFallbackPattern;
|
private final long[] mFallbackPattern;
|
||||||
|
@Nullable private final float[] mDefaultPwlePattern;
|
||||||
|
@Nullable private final float[] mFallbackPwlePattern;
|
||||||
|
|
||||||
public VibratorHelper(Context context) {
|
public VibratorHelper(Context context) {
|
||||||
mVibrator = context.getSystemService(Vibrator.class);
|
mVibrator = context.getSystemService(Vibrator.class);
|
||||||
mDefaultPattern = getLongArray(
|
mDefaultPattern = getLongArray(context.getResources(),
|
||||||
context.getResources(),
|
|
||||||
com.android.internal.R.array.config_defaultNotificationVibePattern,
|
com.android.internal.R.array.config_defaultNotificationVibePattern,
|
||||||
VIBRATE_PATTERN_MAXLEN,
|
VIBRATE_PATTERN_MAXLEN,
|
||||||
DEFAULT_VIBRATE_PATTERN);
|
DEFAULT_VIBRATE_PATTERN);
|
||||||
@@ -58,6 +57,10 @@ public final class VibratorHelper {
|
|||||||
R.array.config_notificationFallbackVibePattern,
|
R.array.config_notificationFallbackVibePattern,
|
||||||
VIBRATE_PATTERN_MAXLEN,
|
VIBRATE_PATTERN_MAXLEN,
|
||||||
DEFAULT_VIBRATE_PATTERN);
|
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;
|
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}.
|
* Vibrate the device with given {@code effect}.
|
||||||
*
|
*
|
||||||
@@ -106,7 +153,10 @@ public final class VibratorHelper {
|
|||||||
*/
|
*/
|
||||||
public VibrationEffect createFallbackVibration(boolean insistent) {
|
public VibrationEffect createFallbackVibration(boolean insistent) {
|
||||||
if (mVibrator.hasFrequencyControl()) {
|
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);
|
return createWaveformVibration(mFallbackPattern, insistent);
|
||||||
}
|
}
|
||||||
@@ -118,29 +168,29 @@ public final class VibratorHelper {
|
|||||||
*/
|
*/
|
||||||
public VibrationEffect createDefaultVibration(boolean insistent) {
|
public VibrationEffect createDefaultVibration(boolean insistent) {
|
||||||
if (mVibrator.hasFrequencyControl()) {
|
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);
|
return createWaveformVibration(mDefaultPattern, insistent);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static VibrationEffect createChirpVibration(int rampDuration, boolean insistent) {
|
@Nullable
|
||||||
VibrationEffect.WaveformBuilder waveformBuilder = VibrationEffect.startWaveform()
|
private static float[] getFloatArray(Resources resources, int resId) {
|
||||||
.addStep(/* amplitude= */ 0, /* frequency= */ -0.85f, /* duration= */ 0)
|
TypedArray array = resources.obtainTypedArray(resId);
|
||||||
.addRamp(/* amplitude= */ 1, /* frequency= */ -0.25f, rampDuration)
|
try {
|
||||||
.addStep(/* amplitude= */ 1, /* frequency= */ -0.25f, CHIRP_LEVEL_DURATION_MILLIS)
|
float[] values = new float[array.length()];
|
||||||
.addRamp(/* amplitude= */ 0, /* frequency= */ -0.85f, rampDuration);
|
for (int i = 0; i < values.length; i++) {
|
||||||
|
values[i] = array.getFloat(i, Float.NaN);
|
||||||
if (insistent) {
|
if (Float.isNaN(values[i])) {
|
||||||
return waveformBuilder
|
return null;
|
||||||
.addStep(/* amplitude= */ 0, CHIRP_LEVEL_DURATION_MILLIS)
|
}
|
||||||
.build(/* repeat= */ 0);
|
}
|
||||||
|
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) {
|
private static long[] getLongArray(Resources resources, int resId, int maxLength, long[] def) {
|
||||||
|
|||||||
@@ -40,7 +40,10 @@ import org.mockito.MockitoAnnotations;
|
|||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
public class VibratorHelperTest extends UiServiceTestCase {
|
public class VibratorHelperTest extends UiServiceTestCase {
|
||||||
|
|
||||||
|
// OFF/ON vibration pattern
|
||||||
private static final long[] CUSTOM_PATTERN = new long[] { 100, 200, 300, 400 };
|
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;
|
@Mock private Vibrator mVibrator;
|
||||||
|
|
||||||
@@ -58,12 +61,16 @@ public class VibratorHelperTest extends UiServiceTestCase {
|
|||||||
public void createWaveformVibration_insistent_createsRepeatingVibration() {
|
public void createWaveformVibration_insistent_createsRepeatingVibration() {
|
||||||
assertRepeatingVibration(
|
assertRepeatingVibration(
|
||||||
VibratorHelper.createWaveformVibration(CUSTOM_PATTERN, /* insistent= */ true));
|
VibratorHelper.createWaveformVibration(CUSTOM_PATTERN, /* insistent= */ true));
|
||||||
|
assertRepeatingVibration(
|
||||||
|
VibratorHelper.createPwleWaveformVibration(PWLE_PATTERN, /* insistent= */ true));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void createWaveformVibration_nonInsistent_createsSingleShotVibration() {
|
public void createWaveformVibration_nonInsistent_createsSingleShotVibration() {
|
||||||
assertSingleVibration(
|
assertSingleVibration(
|
||||||
VibratorHelper.createWaveformVibration(CUSTOM_PATTERN, /* insistent= */ false));
|
VibratorHelper.createWaveformVibration(CUSTOM_PATTERN, /* insistent= */ false));
|
||||||
|
assertSingleVibration(
|
||||||
|
VibratorHelper.createPwleWaveformVibration(PWLE_PATTERN, /* insistent= */ false));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -71,6 +78,11 @@ public class VibratorHelperTest extends UiServiceTestCase {
|
|||||||
assertNull(VibratorHelper.createWaveformVibration(null, false));
|
assertNull(VibratorHelper.createWaveformVibration(null, false));
|
||||||
assertNull(VibratorHelper.createWaveformVibration(new long[0], false));
|
assertNull(VibratorHelper.createWaveformVibration(new long[0], false));
|
||||||
assertNull(VibratorHelper.createWaveformVibration(new long[] { 0, 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
|
@Test
|
||||||
|
|||||||
Reference in New Issue
Block a user