blob: 62359eccec81ab42a4d04319dbfe1a99f5a8855c [file] [log] [blame]
Michael Wright71216972017-01-31 18:33:54 +00001/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.os;
18
Michael Wrightf268bf52018-02-07 23:23:34 +000019import android.annotation.Nullable;
Alexey Kuzmin505872d2018-03-19 19:27:46 +000020import android.annotation.TestApi;
Michael Wrightf268bf52018-02-07 23:23:34 +000021import android.content.Context;
22import android.hardware.vibrator.V1_0.EffectStrength;
23import android.hardware.vibrator.V1_2.Effect;
24import android.net.Uri;
Michael Wright35a0c672018-01-24 00:32:53 +000025import android.util.MathUtils;
Michael Wright71216972017-01-31 18:33:54 +000026
27import java.util.Arrays;
28
29/**
30 * A VibrationEffect describes a haptic effect to be performed by a {@link Vibrator}.
31 *
32 * These effects may be any number of things, from single shot vibrations to complex waveforms.
33 */
34public abstract class VibrationEffect implements Parcelable {
35 private static final int PARCEL_TOKEN_ONE_SHOT = 1;
36 private static final int PARCEL_TOKEN_WAVEFORM = 2;
37 private static final int PARCEL_TOKEN_EFFECT = 3;
38
39 /**
40 * The default vibration strength of the device.
41 */
42 public static final int DEFAULT_AMPLITUDE = -1;
43
44 /**
Michael Wright35a0c672018-01-24 00:32:53 +000045 * The maximum amplitude value
46 * @hide
47 */
48 public static final int MAX_AMPLITUDE = 255;
49
50 /**
Michael Wright71216972017-01-31 18:33:54 +000051 * A click effect.
52 *
53 * @see #get(int)
54 * @hide
55 */
Michael Wrightf268bf52018-02-07 23:23:34 +000056 public static final int EFFECT_CLICK = Effect.CLICK;
Michael Wright71216972017-01-31 18:33:54 +000057
58 /**
59 * A double click effect.
60 *
61 * @see #get(int)
62 * @hide
63 */
Michael Wrightf268bf52018-02-07 23:23:34 +000064 public static final int EFFECT_DOUBLE_CLICK = Effect.DOUBLE_CLICK;
Michael Wright57d94d92017-05-31 14:44:45 +010065
66 /**
67 * A tick effect.
68 * @see #get(int)
69 * @hide
70 */
Michael Wrightf268bf52018-02-07 23:23:34 +000071 public static final int EFFECT_TICK = Effect.TICK;
72
73 /**
74 * A thud effect.
75 * @see #get(int)
76 * @hide
77 */
78 public static final int EFFECT_THUD = Effect.THUD;
79
80 /**
81 * A pop effect.
82 * @see #get(int)
83 * @hide
84 */
85 public static final int EFFECT_POP = Effect.POP;
86
87 /**
88 * A heavy click effect.
89 * @see #get(int)
90 * @hide
91 */
92 public static final int EFFECT_HEAVY_CLICK = Effect.HEAVY_CLICK;
93
94
95 /**
96 * Ringtone patterns. They may correspond with the device's ringtone audio, or may just be a
97 * pattern that can be played as a ringtone with any audio, depending on the device.
98 *
99 * @see #get(Uri, Context)
100 * @hide
101 */
Alexey Kuzmin505872d2018-03-19 19:27:46 +0000102 @TestApi
Michael Wrightf268bf52018-02-07 23:23:34 +0000103 public static final int[] RINGTONES = {
104 Effect.RINGTONE_1,
105 Effect.RINGTONE_2,
106 Effect.RINGTONE_3,
107 Effect.RINGTONE_4,
108 Effect.RINGTONE_5,
109 Effect.RINGTONE_6,
110 Effect.RINGTONE_7,
111 Effect.RINGTONE_8,
112 Effect.RINGTONE_9,
113 Effect.RINGTONE_10,
114 Effect.RINGTONE_11,
115 Effect.RINGTONE_12,
116 Effect.RINGTONE_13,
117 Effect.RINGTONE_14,
118 Effect.RINGTONE_15
119 };
Michael Wright71216972017-01-31 18:33:54 +0000120
121 /** @hide to prevent subclassing from outside of the framework */
122 public VibrationEffect() { }
123
124 /**
125 * Create a one shot vibration.
126 *
127 * One shot vibrations will vibrate constantly for the specified period of time at the
128 * specified amplitude, and then stop.
129 *
130 * @param milliseconds The number of milliseconds to vibrate. This must be a positive number.
131 * @param amplitude The strength of the vibration. This must be a value between 1 and 255, or
132 * {@link #DEFAULT_AMPLITUDE}.
133 *
134 * @return The desired effect.
135 */
136 public static VibrationEffect createOneShot(long milliseconds, int amplitude) {
137 VibrationEffect effect = new OneShot(milliseconds, amplitude);
138 effect.validate();
139 return effect;
140 }
141
142 /**
143 * Create a waveform vibration.
144 *
145 * Waveform vibrations are a potentially repeating series of timing and amplitude pairs. For
146 * each pair, the value in the amplitude array determines the strength of the vibration and the
147 * value in the timing array determines how long it vibrates for. An amplitude of 0 implies no
148 * vibration (i.e. off), and any pairs with a timing value of 0 will be ignored.
149 * <p>
150 * The amplitude array of the generated waveform will be the same size as the given
151 * timing array with alternating values of 0 (i.e. off) and {@link #DEFAULT_AMPLITUDE},
152 * starting with 0. Therefore the first timing value will be the period to wait before turning
153 * the vibrator on, the second value will be how long to vibrate at {@link #DEFAULT_AMPLITUDE}
154 * strength, etc.
155 * </p><p>
156 * To cause the pattern to repeat, pass the index into the timings array at which to start the
157 * repetition, or -1 to disable repeating.
158 * </p>
159 *
160 * @param timings The pattern of alternating on-off timings, starting with off. Timing values
161 * of 0 will cause the timing / amplitude pair to be ignored.
162 * @param repeat The index into the timings array at which to repeat, or -1 if you you don't
163 * want to repeat.
164 *
165 * @return The desired effect.
166 */
167 public static VibrationEffect createWaveform(long[] timings, int repeat) {
168 int[] amplitudes = new int[timings.length];
169 for (int i = 0; i < (timings.length / 2); i++) {
170 amplitudes[i*2 + 1] = VibrationEffect.DEFAULT_AMPLITUDE;
171 }
172 return createWaveform(timings, amplitudes, repeat);
173 }
174
175 /**
176 * Create a waveform vibration.
177 *
178 * Waveform vibrations are a potentially repeating series of timing and amplitude pairs. For
179 * each pair, the value in the amplitude array determines the strength of the vibration and the
180 * value in the timing array determines how long it vibrates for. An amplitude of 0 implies no
181 * vibration (i.e. off), and any pairs with a timing value of 0 will be ignored.
182 * </p><p>
183 * To cause the pattern to repeat, pass the index into the timings array at which to start the
184 * repetition, or -1 to disable repeating.
185 * </p>
186 *
187 * @param timings The timing values of the timing / amplitude pairs. Timing values of 0
188 * will cause the pair to be ignored.
189 * @param amplitudes The amplitude values of the timing / amplitude pairs. Amplitude values
190 * must be between 0 and 255, or equal to {@link #DEFAULT_AMPLITUDE}. An
191 * amplitude value of 0 implies the motor is off.
192 * @param repeat The index into the timings array at which to repeat, or -1 if you you don't
193 * want to repeat.
194 *
195 * @return The desired effect.
196 */
197 public static VibrationEffect createWaveform(long[] timings, int[] amplitudes, int repeat) {
198 VibrationEffect effect = new Waveform(timings, amplitudes, repeat);
199 effect.validate();
200 return effect;
201 }
202
203 /**
204 * Get a predefined vibration effect.
205 *
206 * Predefined effects are a set of common vibration effects that should be identical, regardless
207 * of the app they come from, in order to provide a cohesive experience for users across
208 * the entire device. They also may be custom tailored to the device hardware in order to
209 * provide a better experience than you could otherwise build using the generic building
210 * blocks.
211 *
Michael Wrightdc2b3be2017-08-02 20:44:45 +0100212 * This will fallback to a generic pattern if one exists and there does not exist a
213 * hardware-specific implementation of the effect.
214 *
Michael Wright71216972017-01-31 18:33:54 +0000215 * @param effectId The ID of the effect to perform:
Michael Wrightdc2b3be2017-08-02 20:44:45 +0100216 * {@link #EFFECT_CLICK}, {@link #EFFECT_DOUBLE_CLICK}, {@link #EFFECT_TICK}
Michael Wright71216972017-01-31 18:33:54 +0000217 *
218 * @return The desired effect.
219 * @hide
220 */
Alexey Kuzmin505872d2018-03-19 19:27:46 +0000221 @TestApi
Michael Wright71216972017-01-31 18:33:54 +0000222 public static VibrationEffect get(int effectId) {
Michael Wrightdc2b3be2017-08-02 20:44:45 +0100223 return get(effectId, true);
224 }
225
226 /**
227 * Get a predefined vibration effect.
228 *
229 * Predefined effects are a set of common vibration effects that should be identical, regardless
230 * of the app they come from, in order to provide a cohesive experience for users across
231 * the entire device. They also may be custom tailored to the device hardware in order to
232 * provide a better experience than you could otherwise build using the generic building
233 * blocks.
234 *
235 * Some effects you may only want to play if there's a hardware specific implementation because
236 * they may, for example, be too disruptive to the user without tuning. The {@code fallback}
237 * parameter allows you to decide whether you want to fallback to the generic implementation or
238 * only play if there's a tuned, hardware specific one available.
239 *
240 * @param effectId The ID of the effect to perform:
241 * {@link #EFFECT_CLICK}, {@link #EFFECT_DOUBLE_CLICK}, {@link #EFFECT_TICK}
242 * @param fallback Whether to fallback to a generic pattern if a hardware specific
243 * implementation doesn't exist.
244 *
245 * @return The desired effect.
246 * @hide
247 */
Alexey Kuzmin505872d2018-03-19 19:27:46 +0000248 @TestApi
Michael Wrightdc2b3be2017-08-02 20:44:45 +0100249 public static VibrationEffect get(int effectId, boolean fallback) {
250 VibrationEffect effect = new Prebaked(effectId, fallback);
Michael Wright71216972017-01-31 18:33:54 +0000251 effect.validate();
252 return effect;
253 }
254
Michael Wrightf268bf52018-02-07 23:23:34 +0000255 /**
256 * Get a predefined vibration effect associated with a given URI.
257 *
258 * Predefined effects are a set of common vibration effects that should be identical, regardless
259 * of the app they come from, in order to provide a cohesive experience for users across
260 * the entire device. They also may be custom tailored to the device hardware in order to
261 * provide a better experience than you could otherwise build using the generic building
262 * blocks.
263 *
264 * @param uri The URI associated with the haptic effect.
265 * @param context The context used to get the URI to haptic effect association.
266 *
267 * @return The desired effect, or {@code null} if there's no associated effect.
268 *
269 * @hide
270 */
Alexey Kuzmin505872d2018-03-19 19:27:46 +0000271 @TestApi
Michael Wrightf268bf52018-02-07 23:23:34 +0000272 @Nullable
273 public static VibrationEffect get(Uri uri, Context context) {
274 String[] uris = context.getResources().getStringArray(
275 com.android.internal.R.array.config_ringtoneEffectUris);
276 for (int i = 0; i < uris.length && i < RINGTONES.length; i++) {
277 if (uris[i] == null) {
278 continue;
279 }
280 if (Uri.parse(uris[i]).equals(uri)) {
281 return get(RINGTONES[i]);
282 }
283 }
284 return null;
285 }
286
Michael Wright71216972017-01-31 18:33:54 +0000287 @Override
288 public int describeContents() {
289 return 0;
290 }
291
292 /** @hide */
293 public abstract void validate();
294
Michael Wright35a0c672018-01-24 00:32:53 +0000295 /**
296 * Gets the estimated duration of the vibration in milliseconds.
297 *
298 * For effects without a defined end (e.g. a Waveform with a non-negative repeat index), this
299 * returns Long.MAX_VALUE. For effects with an unknown duration (e.g. Prebaked effects where
300 * the length is device and potentially run-time dependent), this returns -1.
301 *
302 * @hide
303 */
304 public abstract long getDuration();
305
306 /**
307 * Scale the amplitude with the given constraints.
308 *
309 * This assumes that the previous value was in the range [0, MAX_AMPLITUDE]
310 * @hide
311 */
Alexey Kuzmin505872d2018-03-19 19:27:46 +0000312 @TestApi
Michael Wright35a0c672018-01-24 00:32:53 +0000313 protected static int scale(int amplitude, float gamma, int maxAmplitude) {
314 float val = MathUtils.pow(amplitude / (float) MAX_AMPLITUDE, gamma);
315 return (int) (val * maxAmplitude);
316 }
317
Michael Wright71216972017-01-31 18:33:54 +0000318 /** @hide */
Alexey Kuzmin505872d2018-03-19 19:27:46 +0000319 @TestApi
Michael Wright71216972017-01-31 18:33:54 +0000320 public static class OneShot extends VibrationEffect implements Parcelable {
Michael Wright35a0c672018-01-24 00:32:53 +0000321 private final long mDuration;
322 private final int mAmplitude;
Michael Wright71216972017-01-31 18:33:54 +0000323
324 public OneShot(Parcel in) {
Michael Wright35a0c672018-01-24 00:32:53 +0000325 mDuration = in.readLong();
326 mAmplitude = in.readInt();
Michael Wright71216972017-01-31 18:33:54 +0000327 }
328
329 public OneShot(long milliseconds, int amplitude) {
Michael Wright35a0c672018-01-24 00:32:53 +0000330 mDuration = milliseconds;
Michael Wright71216972017-01-31 18:33:54 +0000331 mAmplitude = amplitude;
332 }
333
Michael Wright35a0c672018-01-24 00:32:53 +0000334 @Override
335 public long getDuration() {
336 return mDuration;
Michael Wright71216972017-01-31 18:33:54 +0000337 }
338
339 public int getAmplitude() {
340 return mAmplitude;
341 }
342
Michael Wright35a0c672018-01-24 00:32:53 +0000343 /**
344 * Scale the amplitude of this effect.
345 *
346 * @param gamma the gamma adjustment to apply
Alexey Kuzminfebc5d52018-03-02 18:54:06 +0000347 * @param maxAmplitude the new maximum amplitude of the effect, must be between 0 and
348 * MAX_AMPLITUDE
349 * @throws IllegalArgumentException if maxAmplitude less than 0 or more than MAX_AMPLITUDE
Michael Wright35a0c672018-01-24 00:32:53 +0000350 *
351 * @return A {@link OneShot} effect with the same timing but scaled amplitude.
352 */
Alexey Kuzminfebc5d52018-03-02 18:54:06 +0000353 public OneShot scale(float gamma, int maxAmplitude) {
354 if (maxAmplitude > MAX_AMPLITUDE || maxAmplitude < 0) {
355 throw new IllegalArgumentException(
356 "Amplitude is negative or greater than MAX_AMPLITUDE");
357 }
Michael Wright35a0c672018-01-24 00:32:53 +0000358 int newAmplitude = scale(mAmplitude, gamma, maxAmplitude);
359 return new OneShot(mDuration, newAmplitude);
360 }
361
Alexey Kuzmin55bdc592018-04-24 12:58:13 +0100362 /**
363 * Resolve default values into integer amplitude numbers.
364 *
365 * @param defaultAmplitude the default amplitude to apply, must be between 0 and
366 * MAX_AMPLITUDE
367 * @return A {@link OneShot} effect with same physical meaning but explicitly set amplitude
368 *
369 * @hide
370 */
371 public OneShot resolve(int defaultAmplitude) {
372 if (defaultAmplitude > MAX_AMPLITUDE || defaultAmplitude < 0) {
373 throw new IllegalArgumentException(
374 "Amplitude is negative or greater than MAX_AMPLITUDE");
375 }
376 if (mAmplitude == DEFAULT_AMPLITUDE) {
377 return new OneShot(mDuration, defaultAmplitude);
378 }
379 return this;
380 }
381
Michael Wright71216972017-01-31 18:33:54 +0000382 @Override
383 public void validate() {
384 if (mAmplitude < -1 || mAmplitude == 0 || mAmplitude > 255) {
385 throw new IllegalArgumentException(
Michael Wright35a0c672018-01-24 00:32:53 +0000386 "amplitude must either be DEFAULT_AMPLITUDE, "
387 + "or between 1 and 255 inclusive (amplitude=" + mAmplitude + ")");
Michael Wright71216972017-01-31 18:33:54 +0000388 }
Michael Wright35a0c672018-01-24 00:32:53 +0000389 if (mDuration <= 0) {
Michael Wright0ef6edd2017-04-05 20:57:39 +0100390 throw new IllegalArgumentException(
Michael Wright35a0c672018-01-24 00:32:53 +0000391 "duration must be positive (duration=" + mDuration + ")");
Michael Wright71216972017-01-31 18:33:54 +0000392 }
393 }
394
395 @Override
396 public boolean equals(Object o) {
397 if (!(o instanceof VibrationEffect.OneShot)) {
398 return false;
399 }
400 VibrationEffect.OneShot other = (VibrationEffect.OneShot) o;
Michael Wright35a0c672018-01-24 00:32:53 +0000401 return other.mDuration == mDuration && other.mAmplitude == mAmplitude;
Michael Wright71216972017-01-31 18:33:54 +0000402 }
403
404 @Override
405 public int hashCode() {
406 int result = 17;
Michael Wright35a0c672018-01-24 00:32:53 +0000407 result += 37 * (int) mDuration;
408 result += 37 * mAmplitude;
Michael Wright71216972017-01-31 18:33:54 +0000409 return result;
410 }
411
412 @Override
413 public String toString() {
Michael Wright35a0c672018-01-24 00:32:53 +0000414 return "OneShot{mDuration=" + mDuration + ", mAmplitude=" + mAmplitude + "}";
Michael Wright71216972017-01-31 18:33:54 +0000415 }
416
417 @Override
418 public void writeToParcel(Parcel out, int flags) {
419 out.writeInt(PARCEL_TOKEN_ONE_SHOT);
Michael Wright35a0c672018-01-24 00:32:53 +0000420 out.writeLong(mDuration);
Michael Wright71216972017-01-31 18:33:54 +0000421 out.writeInt(mAmplitude);
422 }
423
424 public static final Parcelable.Creator<OneShot> CREATOR =
425 new Parcelable.Creator<OneShot>() {
426 @Override
427 public OneShot createFromParcel(Parcel in) {
428 // Skip the type token
429 in.readInt();
430 return new OneShot(in);
431 }
432 @Override
433 public OneShot[] newArray(int size) {
434 return new OneShot[size];
435 }
436 };
437 }
438
439 /** @hide */
Alexey Kuzmin505872d2018-03-19 19:27:46 +0000440 @TestApi
Michael Wright71216972017-01-31 18:33:54 +0000441 public static class Waveform extends VibrationEffect implements Parcelable {
Michael Wright35a0c672018-01-24 00:32:53 +0000442 private final long[] mTimings;
443 private final int[] mAmplitudes;
444 private final int mRepeat;
Michael Wright71216972017-01-31 18:33:54 +0000445
446 public Waveform(Parcel in) {
447 this(in.createLongArray(), in.createIntArray(), in.readInt());
448 }
449
450 public Waveform(long[] timings, int[] amplitudes, int repeat) {
451 mTimings = new long[timings.length];
452 System.arraycopy(timings, 0, mTimings, 0, timings.length);
453 mAmplitudes = new int[amplitudes.length];
454 System.arraycopy(amplitudes, 0, mAmplitudes, 0, amplitudes.length);
455 mRepeat = repeat;
456 }
457
458 public long[] getTimings() {
459 return mTimings;
460 }
461
462 public int[] getAmplitudes() {
463 return mAmplitudes;
464 }
465
466 public int getRepeatIndex() {
467 return mRepeat;
468 }
469
470 @Override
Michael Wright35a0c672018-01-24 00:32:53 +0000471 public long getDuration() {
472 if (mRepeat >= 0) {
473 return Long.MAX_VALUE;
474 }
475 long duration = 0;
476 for (long d : mTimings) {
477 duration += d;
478 }
479 return duration;
480 }
481
482 /**
483 * Scale the Waveform with the given gamma and new max amplitude.
484 *
485 * @param gamma the gamma adjustment to apply
Alexey Kuzminfebc5d52018-03-02 18:54:06 +0000486 * @param maxAmplitude the new maximum amplitude of the effect, must be between 0 and
487 * MAX_AMPLITUDE
488 * @throws IllegalArgumentException if maxAmplitude less than 0 or more than MAX_AMPLITUDE
Michael Wright35a0c672018-01-24 00:32:53 +0000489 *
490 * @return A {@link Waveform} effect with the same timings and repeat index
491 * but scaled amplitude.
492 */
Alexey Kuzminfebc5d52018-03-02 18:54:06 +0000493 public Waveform scale(float gamma, int maxAmplitude) {
494 if (maxAmplitude > MAX_AMPLITUDE || maxAmplitude < 0) {
495 throw new IllegalArgumentException(
496 "Amplitude is negative or greater than MAX_AMPLITUDE");
497 }
Michael Wright35a0c672018-01-24 00:32:53 +0000498 if (gamma == 1.0f && maxAmplitude == MAX_AMPLITUDE) {
499 // Just return a copy of the original if there's no scaling to be done.
500 return new Waveform(mTimings, mAmplitudes, mRepeat);
501 }
502
503 int[] scaledAmplitudes = Arrays.copyOf(mAmplitudes, mAmplitudes.length);
504 for (int i = 0; i < scaledAmplitudes.length; i++) {
505 scaledAmplitudes[i] = scale(scaledAmplitudes[i], gamma, maxAmplitude);
506 }
507 return new Waveform(mTimings, scaledAmplitudes, mRepeat);
508 }
509
Alexey Kuzmin55bdc592018-04-24 12:58:13 +0100510 /**
511 * Resolve default values into integer amplitude numbers.
512 *
513 * @param defaultAmplitude the default amplitude to apply, must be between 0 and
514 * MAX_AMPLITUDE
515 * @return A {@link Waveform} effect with same physical meaning but explicitly set
516 * amplitude
517 *
518 * @hide
519 */
520 public Waveform resolve(int defaultAmplitude) {
521 if (defaultAmplitude > MAX_AMPLITUDE || defaultAmplitude < 0) {
522 throw new IllegalArgumentException(
523 "Amplitude is negative or greater than MAX_AMPLITUDE");
524 }
525 int[] resolvedAmplitudes = Arrays.copyOf(mAmplitudes, mAmplitudes.length);
526 for (int i = 0; i < resolvedAmplitudes.length; i++) {
527 if (resolvedAmplitudes[i] == DEFAULT_AMPLITUDE) {
528 resolvedAmplitudes[i] = defaultAmplitude;
529 }
530 }
531 return new Waveform(mTimings, resolvedAmplitudes, mRepeat);
532 }
533
Michael Wright35a0c672018-01-24 00:32:53 +0000534 @Override
Michael Wright71216972017-01-31 18:33:54 +0000535 public void validate() {
536 if (mTimings.length != mAmplitudes.length) {
537 throw new IllegalArgumentException(
Michael Wright35a0c672018-01-24 00:32:53 +0000538 "timing and amplitude arrays must be of equal length"
539 + " (timings.length=" + mTimings.length
540 + ", amplitudes.length=" + mAmplitudes.length + ")");
Michael Wright71216972017-01-31 18:33:54 +0000541 }
542 if (!hasNonZeroEntry(mTimings)) {
Michael Wright35a0c672018-01-24 00:32:53 +0000543 throw new IllegalArgumentException("at least one timing must be non-zero"
544 + " (timings=" + Arrays.toString(mTimings) + ")");
Michael Wright71216972017-01-31 18:33:54 +0000545 }
546 for (long timing : mTimings) {
547 if (timing < 0) {
Michael Wright35a0c672018-01-24 00:32:53 +0000548 throw new IllegalArgumentException("timings must all be >= 0"
549 + " (timings=" + Arrays.toString(mTimings) + ")");
Michael Wright71216972017-01-31 18:33:54 +0000550 }
551 }
552 for (int amplitude : mAmplitudes) {
553 if (amplitude < -1 || amplitude > 255) {
554 throw new IllegalArgumentException(
Michael Wright35a0c672018-01-24 00:32:53 +0000555 "amplitudes must all be DEFAULT_AMPLITUDE or between 0 and 255"
556 + " (amplitudes=" + Arrays.toString(mAmplitudes) + ")");
Michael Wright71216972017-01-31 18:33:54 +0000557 }
558 }
559 if (mRepeat < -1 || mRepeat >= mTimings.length) {
Michael Wright0ef6edd2017-04-05 20:57:39 +0100560 throw new IllegalArgumentException(
Michael Wright35a0c672018-01-24 00:32:53 +0000561 "repeat index must be within the bounds of the timings array"
562 + " (timings.length=" + mTimings.length + ", index=" + mRepeat + ")");
Michael Wright71216972017-01-31 18:33:54 +0000563 }
564 }
565
566 @Override
567 public boolean equals(Object o) {
568 if (!(o instanceof VibrationEffect.Waveform)) {
569 return false;
570 }
571 VibrationEffect.Waveform other = (VibrationEffect.Waveform) o;
Michael Wright35a0c672018-01-24 00:32:53 +0000572 return Arrays.equals(mTimings, other.mTimings)
573 && Arrays.equals(mAmplitudes, other.mAmplitudes)
574 && mRepeat == other.mRepeat;
Michael Wright71216972017-01-31 18:33:54 +0000575 }
576
577 @Override
578 public int hashCode() {
579 int result = 17;
Michael Wright35a0c672018-01-24 00:32:53 +0000580 result += 37 * Arrays.hashCode(mTimings);
581 result += 37 * Arrays.hashCode(mAmplitudes);
582 result += 37 * mRepeat;
Michael Wright71216972017-01-31 18:33:54 +0000583 return result;
584 }
585
586 @Override
587 public String toString() {
Michael Wright35a0c672018-01-24 00:32:53 +0000588 return "Waveform{mTimings=" + Arrays.toString(mTimings)
589 + ", mAmplitudes=" + Arrays.toString(mAmplitudes)
590 + ", mRepeat=" + mRepeat
591 + "}";
Michael Wright71216972017-01-31 18:33:54 +0000592 }
593
594 @Override
595 public void writeToParcel(Parcel out, int flags) {
596 out.writeInt(PARCEL_TOKEN_WAVEFORM);
597 out.writeLongArray(mTimings);
598 out.writeIntArray(mAmplitudes);
599 out.writeInt(mRepeat);
600 }
601
602 private static boolean hasNonZeroEntry(long[] vals) {
603 for (long val : vals) {
604 if (val != 0) {
605 return true;
606 }
607 }
608 return false;
609 }
610
611
612 public static final Parcelable.Creator<Waveform> CREATOR =
613 new Parcelable.Creator<Waveform>() {
614 @Override
615 public Waveform createFromParcel(Parcel in) {
616 // Skip the type token
617 in.readInt();
618 return new Waveform(in);
619 }
620 @Override
621 public Waveform[] newArray(int size) {
622 return new Waveform[size];
623 }
624 };
625 }
626
627 /** @hide */
Alexey Kuzmin505872d2018-03-19 19:27:46 +0000628 @TestApi
Michael Wright71216972017-01-31 18:33:54 +0000629 public static class Prebaked extends VibrationEffect implements Parcelable {
Michael Wright35a0c672018-01-24 00:32:53 +0000630 private final int mEffectId;
631 private final boolean mFallback;
632
633 private int mEffectStrength;
Michael Wright71216972017-01-31 18:33:54 +0000634
635 public Prebaked(Parcel in) {
Michael Wrightdc2b3be2017-08-02 20:44:45 +0100636 this(in.readInt(), in.readByte() != 0);
Michael Wright35a0c672018-01-24 00:32:53 +0000637 mEffectStrength = in.readInt();
Michael Wright71216972017-01-31 18:33:54 +0000638 }
639
Michael Wrightdc2b3be2017-08-02 20:44:45 +0100640 public Prebaked(int effectId, boolean fallback) {
Michael Wright71216972017-01-31 18:33:54 +0000641 mEffectId = effectId;
Michael Wrightdc2b3be2017-08-02 20:44:45 +0100642 mFallback = fallback;
Michael Wright35a0c672018-01-24 00:32:53 +0000643 mEffectStrength = EffectStrength.MEDIUM;
Michael Wright71216972017-01-31 18:33:54 +0000644 }
645
646 public int getId() {
647 return mEffectId;
648 }
649
Michael Wrightdc2b3be2017-08-02 20:44:45 +0100650 /**
651 * Whether the effect should fall back to a generic pattern if there's no hardware specific
652 * implementation of it.
653 */
654 public boolean shouldFallback() {
655 return mFallback;
656 }
657
Michael Wright71216972017-01-31 18:33:54 +0000658 @Override
Michael Wright35a0c672018-01-24 00:32:53 +0000659 public long getDuration() {
660 return -1;
661 }
662
663 /**
664 * Set the effect strength of the prebaked effect.
665 */
666 public void setEffectStrength(int strength) {
667 if (!isValidEffectStrength(strength)) {
668 throw new IllegalArgumentException("Invalid effect strength: " + strength);
669 }
670 mEffectStrength = strength;
671 }
672
673 /**
674 * Set the effect strength.
675 */
676 public int getEffectStrength() {
677 return mEffectStrength;
678 }
679
680 private static boolean isValidEffectStrength(int strength) {
681 switch (strength) {
682 case EffectStrength.LIGHT:
683 case EffectStrength.MEDIUM:
684 case EffectStrength.STRONG:
685 return true;
686 default:
687 return false;
688 }
689 }
690
691 @Override
Michael Wright71216972017-01-31 18:33:54 +0000692 public void validate() {
Michael Wright57d94d92017-05-31 14:44:45 +0100693 switch (mEffectId) {
694 case EFFECT_CLICK:
695 case EFFECT_DOUBLE_CLICK:
696 case EFFECT_TICK:
Michael Wrightf268bf52018-02-07 23:23:34 +0000697 case EFFECT_THUD:
698 case EFFECT_POP:
699 case EFFECT_HEAVY_CLICK:
Michael Wright57d94d92017-05-31 14:44:45 +0100700 break;
701 default:
Michael Wrightf268bf52018-02-07 23:23:34 +0000702 if (mEffectId < RINGTONES[0] || mEffectId > RINGTONES[RINGTONES.length - 1]) {
703 throw new IllegalArgumentException(
704 "Unknown prebaked effect type (value=" + mEffectId + ")");
705 }
Michael Wright71216972017-01-31 18:33:54 +0000706 }
Michael Wright35a0c672018-01-24 00:32:53 +0000707 if (!isValidEffectStrength(mEffectStrength)) {
708 throw new IllegalArgumentException(
709 "Unknown prebaked effect strength (value=" + mEffectStrength + ")");
710 }
Michael Wright71216972017-01-31 18:33:54 +0000711 }
712
713 @Override
714 public boolean equals(Object o) {
715 if (!(o instanceof VibrationEffect.Prebaked)) {
716 return false;
717 }
718 VibrationEffect.Prebaked other = (VibrationEffect.Prebaked) o;
Michael Wright35a0c672018-01-24 00:32:53 +0000719 return mEffectId == other.mEffectId
720 && mFallback == other.mFallback
721 && mEffectStrength == other.mEffectStrength;
Michael Wright71216972017-01-31 18:33:54 +0000722 }
723
724 @Override
725 public int hashCode() {
Michael Wright35a0c672018-01-24 00:32:53 +0000726 int result = 17;
727 result += 37 * mEffectId;
728 result += 37 * mEffectStrength;
729 return result;
Michael Wright71216972017-01-31 18:33:54 +0000730 }
731
732 @Override
733 public String toString() {
Michael Wright35a0c672018-01-24 00:32:53 +0000734 return "Prebaked{mEffectId=" + mEffectId
735 + ", mEffectStrength=" + mEffectStrength
736 + ", mFallback=" + mFallback
737 + "}";
Michael Wright71216972017-01-31 18:33:54 +0000738 }
739
740
741 @Override
742 public void writeToParcel(Parcel out, int flags) {
743 out.writeInt(PARCEL_TOKEN_EFFECT);
744 out.writeInt(mEffectId);
Michael Wrightdc2b3be2017-08-02 20:44:45 +0100745 out.writeByte((byte) (mFallback ? 1 : 0));
Michael Wright35a0c672018-01-24 00:32:53 +0000746 out.writeInt(mEffectStrength);
Michael Wright71216972017-01-31 18:33:54 +0000747 }
748
749 public static final Parcelable.Creator<Prebaked> CREATOR =
750 new Parcelable.Creator<Prebaked>() {
751 @Override
752 public Prebaked createFromParcel(Parcel in) {
753 // Skip the type token
754 in.readInt();
755 return new Prebaked(in);
756 }
757 @Override
758 public Prebaked[] newArray(int size) {
759 return new Prebaked[size];
760 }
761 };
762 }
763
764 public static final Parcelable.Creator<VibrationEffect> CREATOR =
765 new Parcelable.Creator<VibrationEffect>() {
766 @Override
767 public VibrationEffect createFromParcel(Parcel in) {
768 int token = in.readInt();
769 if (token == PARCEL_TOKEN_ONE_SHOT) {
770 return new OneShot(in);
771 } else if (token == PARCEL_TOKEN_WAVEFORM) {
772 return new Waveform(in);
773 } else if (token == PARCEL_TOKEN_EFFECT) {
774 return new Prebaked(in);
775 } else {
776 throw new IllegalStateException(
777 "Unexpected vibration event type token in parcel.");
778 }
779 }
780 @Override
781 public VibrationEffect[] newArray(int size) {
782 return new VibrationEffect[size];
783 }
784 };
785}