blob: 3a2e485a5e6ba699fd678d59a4ac280654f6168d [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
Michael Wright71216972017-01-31 18:33:54 +0000362 @Override
363 public void validate() {
364 if (mAmplitude < -1 || mAmplitude == 0 || mAmplitude > 255) {
365 throw new IllegalArgumentException(
Michael Wright35a0c672018-01-24 00:32:53 +0000366 "amplitude must either be DEFAULT_AMPLITUDE, "
367 + "or between 1 and 255 inclusive (amplitude=" + mAmplitude + ")");
Michael Wright71216972017-01-31 18:33:54 +0000368 }
Michael Wright35a0c672018-01-24 00:32:53 +0000369 if (mDuration <= 0) {
Michael Wright0ef6edd2017-04-05 20:57:39 +0100370 throw new IllegalArgumentException(
Michael Wright35a0c672018-01-24 00:32:53 +0000371 "duration must be positive (duration=" + mDuration + ")");
Michael Wright71216972017-01-31 18:33:54 +0000372 }
373 }
374
375 @Override
376 public boolean equals(Object o) {
377 if (!(o instanceof VibrationEffect.OneShot)) {
378 return false;
379 }
380 VibrationEffect.OneShot other = (VibrationEffect.OneShot) o;
Michael Wright35a0c672018-01-24 00:32:53 +0000381 return other.mDuration == mDuration && other.mAmplitude == mAmplitude;
Michael Wright71216972017-01-31 18:33:54 +0000382 }
383
384 @Override
385 public int hashCode() {
386 int result = 17;
Michael Wright35a0c672018-01-24 00:32:53 +0000387 result += 37 * (int) mDuration;
388 result += 37 * mAmplitude;
Michael Wright71216972017-01-31 18:33:54 +0000389 return result;
390 }
391
392 @Override
393 public String toString() {
Michael Wright35a0c672018-01-24 00:32:53 +0000394 return "OneShot{mDuration=" + mDuration + ", mAmplitude=" + mAmplitude + "}";
Michael Wright71216972017-01-31 18:33:54 +0000395 }
396
397 @Override
398 public void writeToParcel(Parcel out, int flags) {
399 out.writeInt(PARCEL_TOKEN_ONE_SHOT);
Michael Wright35a0c672018-01-24 00:32:53 +0000400 out.writeLong(mDuration);
Michael Wright71216972017-01-31 18:33:54 +0000401 out.writeInt(mAmplitude);
402 }
403
404 public static final Parcelable.Creator<OneShot> CREATOR =
405 new Parcelable.Creator<OneShot>() {
406 @Override
407 public OneShot createFromParcel(Parcel in) {
408 // Skip the type token
409 in.readInt();
410 return new OneShot(in);
411 }
412 @Override
413 public OneShot[] newArray(int size) {
414 return new OneShot[size];
415 }
416 };
417 }
418
419 /** @hide */
Alexey Kuzmin505872d2018-03-19 19:27:46 +0000420 @TestApi
Michael Wright71216972017-01-31 18:33:54 +0000421 public static class Waveform extends VibrationEffect implements Parcelable {
Michael Wright35a0c672018-01-24 00:32:53 +0000422 private final long[] mTimings;
423 private final int[] mAmplitudes;
424 private final int mRepeat;
Michael Wright71216972017-01-31 18:33:54 +0000425
426 public Waveform(Parcel in) {
427 this(in.createLongArray(), in.createIntArray(), in.readInt());
428 }
429
430 public Waveform(long[] timings, int[] amplitudes, int repeat) {
431 mTimings = new long[timings.length];
432 System.arraycopy(timings, 0, mTimings, 0, timings.length);
433 mAmplitudes = new int[amplitudes.length];
434 System.arraycopy(amplitudes, 0, mAmplitudes, 0, amplitudes.length);
435 mRepeat = repeat;
436 }
437
438 public long[] getTimings() {
439 return mTimings;
440 }
441
442 public int[] getAmplitudes() {
443 return mAmplitudes;
444 }
445
446 public int getRepeatIndex() {
447 return mRepeat;
448 }
449
450 @Override
Michael Wright35a0c672018-01-24 00:32:53 +0000451 public long getDuration() {
452 if (mRepeat >= 0) {
453 return Long.MAX_VALUE;
454 }
455 long duration = 0;
456 for (long d : mTimings) {
457 duration += d;
458 }
459 return duration;
460 }
461
462 /**
463 * Scale the Waveform with the given gamma and new max amplitude.
464 *
465 * @param gamma the gamma adjustment to apply
Alexey Kuzminfebc5d52018-03-02 18:54:06 +0000466 * @param maxAmplitude the new maximum amplitude of the effect, must be between 0 and
467 * MAX_AMPLITUDE
468 * @throws IllegalArgumentException if maxAmplitude less than 0 or more than MAX_AMPLITUDE
Michael Wright35a0c672018-01-24 00:32:53 +0000469 *
470 * @return A {@link Waveform} effect with the same timings and repeat index
471 * but scaled amplitude.
472 */
Alexey Kuzminfebc5d52018-03-02 18:54:06 +0000473 public Waveform scale(float gamma, int maxAmplitude) {
474 if (maxAmplitude > MAX_AMPLITUDE || maxAmplitude < 0) {
475 throw new IllegalArgumentException(
476 "Amplitude is negative or greater than MAX_AMPLITUDE");
477 }
Michael Wright35a0c672018-01-24 00:32:53 +0000478 if (gamma == 1.0f && maxAmplitude == MAX_AMPLITUDE) {
479 // Just return a copy of the original if there's no scaling to be done.
480 return new Waveform(mTimings, mAmplitudes, mRepeat);
481 }
482
483 int[] scaledAmplitudes = Arrays.copyOf(mAmplitudes, mAmplitudes.length);
484 for (int i = 0; i < scaledAmplitudes.length; i++) {
485 scaledAmplitudes[i] = scale(scaledAmplitudes[i], gamma, maxAmplitude);
486 }
487 return new Waveform(mTimings, scaledAmplitudes, mRepeat);
488 }
489
490 @Override
Michael Wright71216972017-01-31 18:33:54 +0000491 public void validate() {
492 if (mTimings.length != mAmplitudes.length) {
493 throw new IllegalArgumentException(
Michael Wright35a0c672018-01-24 00:32:53 +0000494 "timing and amplitude arrays must be of equal length"
495 + " (timings.length=" + mTimings.length
496 + ", amplitudes.length=" + mAmplitudes.length + ")");
Michael Wright71216972017-01-31 18:33:54 +0000497 }
498 if (!hasNonZeroEntry(mTimings)) {
Michael Wright35a0c672018-01-24 00:32:53 +0000499 throw new IllegalArgumentException("at least one timing must be non-zero"
500 + " (timings=" + Arrays.toString(mTimings) + ")");
Michael Wright71216972017-01-31 18:33:54 +0000501 }
502 for (long timing : mTimings) {
503 if (timing < 0) {
Michael Wright35a0c672018-01-24 00:32:53 +0000504 throw new IllegalArgumentException("timings must all be >= 0"
505 + " (timings=" + Arrays.toString(mTimings) + ")");
Michael Wright71216972017-01-31 18:33:54 +0000506 }
507 }
508 for (int amplitude : mAmplitudes) {
509 if (amplitude < -1 || amplitude > 255) {
510 throw new IllegalArgumentException(
Michael Wright35a0c672018-01-24 00:32:53 +0000511 "amplitudes must all be DEFAULT_AMPLITUDE or between 0 and 255"
512 + " (amplitudes=" + Arrays.toString(mAmplitudes) + ")");
Michael Wright71216972017-01-31 18:33:54 +0000513 }
514 }
515 if (mRepeat < -1 || mRepeat >= mTimings.length) {
Michael Wright0ef6edd2017-04-05 20:57:39 +0100516 throw new IllegalArgumentException(
Michael Wright35a0c672018-01-24 00:32:53 +0000517 "repeat index must be within the bounds of the timings array"
518 + " (timings.length=" + mTimings.length + ", index=" + mRepeat + ")");
Michael Wright71216972017-01-31 18:33:54 +0000519 }
520 }
521
522 @Override
523 public boolean equals(Object o) {
524 if (!(o instanceof VibrationEffect.Waveform)) {
525 return false;
526 }
527 VibrationEffect.Waveform other = (VibrationEffect.Waveform) o;
Michael Wright35a0c672018-01-24 00:32:53 +0000528 return Arrays.equals(mTimings, other.mTimings)
529 && Arrays.equals(mAmplitudes, other.mAmplitudes)
530 && mRepeat == other.mRepeat;
Michael Wright71216972017-01-31 18:33:54 +0000531 }
532
533 @Override
534 public int hashCode() {
535 int result = 17;
Michael Wright35a0c672018-01-24 00:32:53 +0000536 result += 37 * Arrays.hashCode(mTimings);
537 result += 37 * Arrays.hashCode(mAmplitudes);
538 result += 37 * mRepeat;
Michael Wright71216972017-01-31 18:33:54 +0000539 return result;
540 }
541
542 @Override
543 public String toString() {
Michael Wright35a0c672018-01-24 00:32:53 +0000544 return "Waveform{mTimings=" + Arrays.toString(mTimings)
545 + ", mAmplitudes=" + Arrays.toString(mAmplitudes)
546 + ", mRepeat=" + mRepeat
547 + "}";
Michael Wright71216972017-01-31 18:33:54 +0000548 }
549
550 @Override
551 public void writeToParcel(Parcel out, int flags) {
552 out.writeInt(PARCEL_TOKEN_WAVEFORM);
553 out.writeLongArray(mTimings);
554 out.writeIntArray(mAmplitudes);
555 out.writeInt(mRepeat);
556 }
557
558 private static boolean hasNonZeroEntry(long[] vals) {
559 for (long val : vals) {
560 if (val != 0) {
561 return true;
562 }
563 }
564 return false;
565 }
566
567
568 public static final Parcelable.Creator<Waveform> CREATOR =
569 new Parcelable.Creator<Waveform>() {
570 @Override
571 public Waveform createFromParcel(Parcel in) {
572 // Skip the type token
573 in.readInt();
574 return new Waveform(in);
575 }
576 @Override
577 public Waveform[] newArray(int size) {
578 return new Waveform[size];
579 }
580 };
581 }
582
583 /** @hide */
Alexey Kuzmin505872d2018-03-19 19:27:46 +0000584 @TestApi
Michael Wright71216972017-01-31 18:33:54 +0000585 public static class Prebaked extends VibrationEffect implements Parcelable {
Michael Wright35a0c672018-01-24 00:32:53 +0000586 private final int mEffectId;
587 private final boolean mFallback;
588
589 private int mEffectStrength;
Michael Wright71216972017-01-31 18:33:54 +0000590
591 public Prebaked(Parcel in) {
Michael Wrightdc2b3be2017-08-02 20:44:45 +0100592 this(in.readInt(), in.readByte() != 0);
Michael Wright35a0c672018-01-24 00:32:53 +0000593 mEffectStrength = in.readInt();
Michael Wright71216972017-01-31 18:33:54 +0000594 }
595
Michael Wrightdc2b3be2017-08-02 20:44:45 +0100596 public Prebaked(int effectId, boolean fallback) {
Michael Wright71216972017-01-31 18:33:54 +0000597 mEffectId = effectId;
Michael Wrightdc2b3be2017-08-02 20:44:45 +0100598 mFallback = fallback;
Michael Wright35a0c672018-01-24 00:32:53 +0000599 mEffectStrength = EffectStrength.MEDIUM;
Michael Wright71216972017-01-31 18:33:54 +0000600 }
601
602 public int getId() {
603 return mEffectId;
604 }
605
Michael Wrightdc2b3be2017-08-02 20:44:45 +0100606 /**
607 * Whether the effect should fall back to a generic pattern if there's no hardware specific
608 * implementation of it.
609 */
610 public boolean shouldFallback() {
611 return mFallback;
612 }
613
Michael Wright71216972017-01-31 18:33:54 +0000614 @Override
Michael Wright35a0c672018-01-24 00:32:53 +0000615 public long getDuration() {
616 return -1;
617 }
618
619 /**
620 * Set the effect strength of the prebaked effect.
621 */
622 public void setEffectStrength(int strength) {
623 if (!isValidEffectStrength(strength)) {
624 throw new IllegalArgumentException("Invalid effect strength: " + strength);
625 }
626 mEffectStrength = strength;
627 }
628
629 /**
630 * Set the effect strength.
631 */
632 public int getEffectStrength() {
633 return mEffectStrength;
634 }
635
636 private static boolean isValidEffectStrength(int strength) {
637 switch (strength) {
638 case EffectStrength.LIGHT:
639 case EffectStrength.MEDIUM:
640 case EffectStrength.STRONG:
641 return true;
642 default:
643 return false;
644 }
645 }
646
647 @Override
Michael Wright71216972017-01-31 18:33:54 +0000648 public void validate() {
Michael Wright57d94d92017-05-31 14:44:45 +0100649 switch (mEffectId) {
650 case EFFECT_CLICK:
651 case EFFECT_DOUBLE_CLICK:
652 case EFFECT_TICK:
Michael Wrightf268bf52018-02-07 23:23:34 +0000653 case EFFECT_THUD:
654 case EFFECT_POP:
655 case EFFECT_HEAVY_CLICK:
Michael Wright57d94d92017-05-31 14:44:45 +0100656 break;
657 default:
Michael Wrightf268bf52018-02-07 23:23:34 +0000658 if (mEffectId < RINGTONES[0] || mEffectId > RINGTONES[RINGTONES.length - 1]) {
659 throw new IllegalArgumentException(
660 "Unknown prebaked effect type (value=" + mEffectId + ")");
661 }
Michael Wright71216972017-01-31 18:33:54 +0000662 }
Michael Wright35a0c672018-01-24 00:32:53 +0000663 if (!isValidEffectStrength(mEffectStrength)) {
664 throw new IllegalArgumentException(
665 "Unknown prebaked effect strength (value=" + mEffectStrength + ")");
666 }
Michael Wright71216972017-01-31 18:33:54 +0000667 }
668
669 @Override
670 public boolean equals(Object o) {
671 if (!(o instanceof VibrationEffect.Prebaked)) {
672 return false;
673 }
674 VibrationEffect.Prebaked other = (VibrationEffect.Prebaked) o;
Michael Wright35a0c672018-01-24 00:32:53 +0000675 return mEffectId == other.mEffectId
676 && mFallback == other.mFallback
677 && mEffectStrength == other.mEffectStrength;
Michael Wright71216972017-01-31 18:33:54 +0000678 }
679
680 @Override
681 public int hashCode() {
Michael Wright35a0c672018-01-24 00:32:53 +0000682 int result = 17;
683 result += 37 * mEffectId;
684 result += 37 * mEffectStrength;
685 return result;
Michael Wright71216972017-01-31 18:33:54 +0000686 }
687
688 @Override
689 public String toString() {
Michael Wright35a0c672018-01-24 00:32:53 +0000690 return "Prebaked{mEffectId=" + mEffectId
691 + ", mEffectStrength=" + mEffectStrength
692 + ", mFallback=" + mFallback
693 + "}";
Michael Wright71216972017-01-31 18:33:54 +0000694 }
695
696
697 @Override
698 public void writeToParcel(Parcel out, int flags) {
699 out.writeInt(PARCEL_TOKEN_EFFECT);
700 out.writeInt(mEffectId);
Michael Wrightdc2b3be2017-08-02 20:44:45 +0100701 out.writeByte((byte) (mFallback ? 1 : 0));
Michael Wright35a0c672018-01-24 00:32:53 +0000702 out.writeInt(mEffectStrength);
Michael Wright71216972017-01-31 18:33:54 +0000703 }
704
705 public static final Parcelable.Creator<Prebaked> CREATOR =
706 new Parcelable.Creator<Prebaked>() {
707 @Override
708 public Prebaked createFromParcel(Parcel in) {
709 // Skip the type token
710 in.readInt();
711 return new Prebaked(in);
712 }
713 @Override
714 public Prebaked[] newArray(int size) {
715 return new Prebaked[size];
716 }
717 };
718 }
719
720 public static final Parcelable.Creator<VibrationEffect> CREATOR =
721 new Parcelable.Creator<VibrationEffect>() {
722 @Override
723 public VibrationEffect createFromParcel(Parcel in) {
724 int token = in.readInt();
725 if (token == PARCEL_TOKEN_ONE_SHOT) {
726 return new OneShot(in);
727 } else if (token == PARCEL_TOKEN_WAVEFORM) {
728 return new Waveform(in);
729 } else if (token == PARCEL_TOKEN_EFFECT) {
730 return new Prebaked(in);
731 } else {
732 throw new IllegalStateException(
733 "Unexpected vibration event type token in parcel.");
734 }
735 }
736 @Override
737 public VibrationEffect[] newArray(int size) {
738 return new VibrationEffect[size];
739 }
740 };
741}