blob: 583319af9aa7595b81144f1adad0582804c9ebf9 [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;
20import android.content.Context;
21import android.hardware.vibrator.V1_0.EffectStrength;
22import android.hardware.vibrator.V1_2.Effect;
23import android.net.Uri;
Michael Wright35a0c672018-01-24 00:32:53 +000024import android.util.MathUtils;
Michael Wright71216972017-01-31 18:33:54 +000025
Michael Wrightf268bf52018-02-07 23:23:34 +000026import com.android.internal.annotations.VisibleForTesting;
27
Michael Wright71216972017-01-31 18:33:54 +000028import java.util.Arrays;
29
30/**
31 * A VibrationEffect describes a haptic effect to be performed by a {@link Vibrator}.
32 *
33 * These effects may be any number of things, from single shot vibrations to complex waveforms.
34 */
35public abstract class VibrationEffect implements Parcelable {
36 private static final int PARCEL_TOKEN_ONE_SHOT = 1;
37 private static final int PARCEL_TOKEN_WAVEFORM = 2;
38 private static final int PARCEL_TOKEN_EFFECT = 3;
39
40 /**
41 * The default vibration strength of the device.
42 */
43 public static final int DEFAULT_AMPLITUDE = -1;
44
45 /**
Michael Wright35a0c672018-01-24 00:32:53 +000046 * The maximum amplitude value
47 * @hide
48 */
49 public static final int MAX_AMPLITUDE = 255;
50
51 /**
Michael Wright71216972017-01-31 18:33:54 +000052 * A click effect.
53 *
54 * @see #get(int)
55 * @hide
56 */
Michael Wrightf268bf52018-02-07 23:23:34 +000057 public static final int EFFECT_CLICK = Effect.CLICK;
Michael Wright71216972017-01-31 18:33:54 +000058
59 /**
60 * A double click effect.
61 *
62 * @see #get(int)
63 * @hide
64 */
Michael Wrightf268bf52018-02-07 23:23:34 +000065 public static final int EFFECT_DOUBLE_CLICK = Effect.DOUBLE_CLICK;
Michael Wright57d94d92017-05-31 14:44:45 +010066
67 /**
68 * A tick effect.
69 * @see #get(int)
70 * @hide
71 */
Michael Wrightf268bf52018-02-07 23:23:34 +000072 public static final int EFFECT_TICK = Effect.TICK;
73
74 /**
75 * A thud effect.
76 * @see #get(int)
77 * @hide
78 */
79 public static final int EFFECT_THUD = Effect.THUD;
80
81 /**
82 * A pop effect.
83 * @see #get(int)
84 * @hide
85 */
86 public static final int EFFECT_POP = Effect.POP;
87
88 /**
89 * A heavy click effect.
90 * @see #get(int)
91 * @hide
92 */
93 public static final int EFFECT_HEAVY_CLICK = Effect.HEAVY_CLICK;
94
95
96 /**
97 * Ringtone patterns. They may correspond with the device's ringtone audio, or may just be a
98 * pattern that can be played as a ringtone with any audio, depending on the device.
99 *
100 * @see #get(Uri, Context)
101 * @hide
102 */
103 @VisibleForTesting
104 public static final int[] RINGTONES = {
105 Effect.RINGTONE_1,
106 Effect.RINGTONE_2,
107 Effect.RINGTONE_3,
108 Effect.RINGTONE_4,
109 Effect.RINGTONE_5,
110 Effect.RINGTONE_6,
111 Effect.RINGTONE_7,
112 Effect.RINGTONE_8,
113 Effect.RINGTONE_9,
114 Effect.RINGTONE_10,
115 Effect.RINGTONE_11,
116 Effect.RINGTONE_12,
117 Effect.RINGTONE_13,
118 Effect.RINGTONE_14,
119 Effect.RINGTONE_15
120 };
Michael Wright71216972017-01-31 18:33:54 +0000121
122 /** @hide to prevent subclassing from outside of the framework */
123 public VibrationEffect() { }
124
125 /**
126 * Create a one shot vibration.
127 *
128 * One shot vibrations will vibrate constantly for the specified period of time at the
129 * specified amplitude, and then stop.
130 *
131 * @param milliseconds The number of milliseconds to vibrate. This must be a positive number.
132 * @param amplitude The strength of the vibration. This must be a value between 1 and 255, or
133 * {@link #DEFAULT_AMPLITUDE}.
134 *
135 * @return The desired effect.
136 */
137 public static VibrationEffect createOneShot(long milliseconds, int amplitude) {
138 VibrationEffect effect = new OneShot(milliseconds, amplitude);
139 effect.validate();
140 return effect;
141 }
142
143 /**
144 * Create a waveform vibration.
145 *
146 * Waveform vibrations are a potentially repeating series of timing and amplitude pairs. For
147 * each pair, the value in the amplitude array determines the strength of the vibration and the
148 * value in the timing array determines how long it vibrates for. An amplitude of 0 implies no
149 * vibration (i.e. off), and any pairs with a timing value of 0 will be ignored.
150 * <p>
151 * The amplitude array of the generated waveform will be the same size as the given
152 * timing array with alternating values of 0 (i.e. off) and {@link #DEFAULT_AMPLITUDE},
153 * starting with 0. Therefore the first timing value will be the period to wait before turning
154 * the vibrator on, the second value will be how long to vibrate at {@link #DEFAULT_AMPLITUDE}
155 * strength, etc.
156 * </p><p>
157 * To cause the pattern to repeat, pass the index into the timings array at which to start the
158 * repetition, or -1 to disable repeating.
159 * </p>
160 *
161 * @param timings The pattern of alternating on-off timings, starting with off. Timing values
162 * of 0 will cause the timing / amplitude pair to be ignored.
163 * @param repeat The index into the timings array at which to repeat, or -1 if you you don't
164 * want to repeat.
165 *
166 * @return The desired effect.
167 */
168 public static VibrationEffect createWaveform(long[] timings, int repeat) {
169 int[] amplitudes = new int[timings.length];
170 for (int i = 0; i < (timings.length / 2); i++) {
171 amplitudes[i*2 + 1] = VibrationEffect.DEFAULT_AMPLITUDE;
172 }
173 return createWaveform(timings, amplitudes, repeat);
174 }
175
176 /**
177 * Create a waveform vibration.
178 *
179 * Waveform vibrations are a potentially repeating series of timing and amplitude pairs. For
180 * each pair, the value in the amplitude array determines the strength of the vibration and the
181 * value in the timing array determines how long it vibrates for. An amplitude of 0 implies no
182 * vibration (i.e. off), and any pairs with a timing value of 0 will be ignored.
183 * </p><p>
184 * To cause the pattern to repeat, pass the index into the timings array at which to start the
185 * repetition, or -1 to disable repeating.
186 * </p>
187 *
188 * @param timings The timing values of the timing / amplitude pairs. Timing values of 0
189 * will cause the pair to be ignored.
190 * @param amplitudes The amplitude values of the timing / amplitude pairs. Amplitude values
191 * must be between 0 and 255, or equal to {@link #DEFAULT_AMPLITUDE}. An
192 * amplitude value of 0 implies the motor is off.
193 * @param repeat The index into the timings array at which to repeat, or -1 if you you don't
194 * want to repeat.
195 *
196 * @return The desired effect.
197 */
198 public static VibrationEffect createWaveform(long[] timings, int[] amplitudes, int repeat) {
199 VibrationEffect effect = new Waveform(timings, amplitudes, repeat);
200 effect.validate();
201 return effect;
202 }
203
204 /**
205 * Get a predefined vibration effect.
206 *
207 * Predefined effects are a set of common vibration effects that should be identical, regardless
208 * of the app they come from, in order to provide a cohesive experience for users across
209 * the entire device. They also may be custom tailored to the device hardware in order to
210 * provide a better experience than you could otherwise build using the generic building
211 * blocks.
212 *
Michael Wrightdc2b3be2017-08-02 20:44:45 +0100213 * This will fallback to a generic pattern if one exists and there does not exist a
214 * hardware-specific implementation of the effect.
215 *
Michael Wright71216972017-01-31 18:33:54 +0000216 * @param effectId The ID of the effect to perform:
Michael Wrightdc2b3be2017-08-02 20:44:45 +0100217 * {@link #EFFECT_CLICK}, {@link #EFFECT_DOUBLE_CLICK}, {@link #EFFECT_TICK}
Michael Wright71216972017-01-31 18:33:54 +0000218 *
219 * @return The desired effect.
220 * @hide
221 */
222 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 */
248 public static VibrationEffect get(int effectId, boolean fallback) {
249 VibrationEffect effect = new Prebaked(effectId, fallback);
Michael Wright71216972017-01-31 18:33:54 +0000250 effect.validate();
251 return effect;
252 }
253
Michael Wrightf268bf52018-02-07 23:23:34 +0000254 /**
255 * Get a predefined vibration effect associated with a given URI.
256 *
257 * Predefined effects are a set of common vibration effects that should be identical, regardless
258 * of the app they come from, in order to provide a cohesive experience for users across
259 * the entire device. They also may be custom tailored to the device hardware in order to
260 * provide a better experience than you could otherwise build using the generic building
261 * blocks.
262 *
263 * @param uri The URI associated with the haptic effect.
264 * @param context The context used to get the URI to haptic effect association.
265 *
266 * @return The desired effect, or {@code null} if there's no associated effect.
267 *
268 * @hide
269 */
270 @Nullable
271 public static VibrationEffect get(Uri uri, Context context) {
272 String[] uris = context.getResources().getStringArray(
273 com.android.internal.R.array.config_ringtoneEffectUris);
274 for (int i = 0; i < uris.length && i < RINGTONES.length; i++) {
275 if (uris[i] == null) {
276 continue;
277 }
278 if (Uri.parse(uris[i]).equals(uri)) {
279 return get(RINGTONES[i]);
280 }
281 }
282 return null;
283 }
284
Michael Wright71216972017-01-31 18:33:54 +0000285 @Override
286 public int describeContents() {
287 return 0;
288 }
289
290 /** @hide */
291 public abstract void validate();
292
Michael Wright35a0c672018-01-24 00:32:53 +0000293 /**
294 * Gets the estimated duration of the vibration in milliseconds.
295 *
296 * For effects without a defined end (e.g. a Waveform with a non-negative repeat index), this
297 * returns Long.MAX_VALUE. For effects with an unknown duration (e.g. Prebaked effects where
298 * the length is device and potentially run-time dependent), this returns -1.
299 *
300 * @hide
301 */
302 public abstract long getDuration();
303
304 /**
305 * Scale the amplitude with the given constraints.
306 *
307 * This assumes that the previous value was in the range [0, MAX_AMPLITUDE]
308 * @hide
309 */
310 protected static int scale(int amplitude, float gamma, int maxAmplitude) {
311 float val = MathUtils.pow(amplitude / (float) MAX_AMPLITUDE, gamma);
312 return (int) (val * maxAmplitude);
313 }
314
Michael Wright71216972017-01-31 18:33:54 +0000315 /** @hide */
316 public static class OneShot extends VibrationEffect implements Parcelable {
Michael Wright35a0c672018-01-24 00:32:53 +0000317 private final long mDuration;
318 private final int mAmplitude;
Michael Wright71216972017-01-31 18:33:54 +0000319
320 public OneShot(Parcel in) {
Michael Wright35a0c672018-01-24 00:32:53 +0000321 mDuration = in.readLong();
322 mAmplitude = in.readInt();
Michael Wright71216972017-01-31 18:33:54 +0000323 }
324
325 public OneShot(long milliseconds, int amplitude) {
Michael Wright35a0c672018-01-24 00:32:53 +0000326 mDuration = milliseconds;
Michael Wright71216972017-01-31 18:33:54 +0000327 mAmplitude = amplitude;
328 }
329
Michael Wright35a0c672018-01-24 00:32:53 +0000330 @Override
331 public long getDuration() {
332 return mDuration;
Michael Wright71216972017-01-31 18:33:54 +0000333 }
334
335 public int getAmplitude() {
336 return mAmplitude;
337 }
338
Michael Wright35a0c672018-01-24 00:32:53 +0000339 /**
340 * Scale the amplitude of this effect.
341 *
342 * @param gamma the gamma adjustment to apply
Alexey Kuzminfebc5d52018-03-02 18:54:06 +0000343 * @param maxAmplitude the new maximum amplitude of the effect, must be between 0 and
344 * MAX_AMPLITUDE
345 * @throws IllegalArgumentException if maxAmplitude less than 0 or more than MAX_AMPLITUDE
Michael Wright35a0c672018-01-24 00:32:53 +0000346 *
347 * @return A {@link OneShot} effect with the same timing but scaled amplitude.
348 */
Alexey Kuzminfebc5d52018-03-02 18:54:06 +0000349 public OneShot scale(float gamma, int maxAmplitude) {
350 if (maxAmplitude > MAX_AMPLITUDE || maxAmplitude < 0) {
351 throw new IllegalArgumentException(
352 "Amplitude is negative or greater than MAX_AMPLITUDE");
353 }
Michael Wright35a0c672018-01-24 00:32:53 +0000354 int newAmplitude = scale(mAmplitude, gamma, maxAmplitude);
355 return new OneShot(mDuration, newAmplitude);
356 }
357
Michael Wright71216972017-01-31 18:33:54 +0000358 @Override
359 public void validate() {
360 if (mAmplitude < -1 || mAmplitude == 0 || mAmplitude > 255) {
361 throw new IllegalArgumentException(
Michael Wright35a0c672018-01-24 00:32:53 +0000362 "amplitude must either be DEFAULT_AMPLITUDE, "
363 + "or between 1 and 255 inclusive (amplitude=" + mAmplitude + ")");
Michael Wright71216972017-01-31 18:33:54 +0000364 }
Michael Wright35a0c672018-01-24 00:32:53 +0000365 if (mDuration <= 0) {
Michael Wright0ef6edd2017-04-05 20:57:39 +0100366 throw new IllegalArgumentException(
Michael Wright35a0c672018-01-24 00:32:53 +0000367 "duration must be positive (duration=" + mDuration + ")");
Michael Wright71216972017-01-31 18:33:54 +0000368 }
369 }
370
371 @Override
372 public boolean equals(Object o) {
373 if (!(o instanceof VibrationEffect.OneShot)) {
374 return false;
375 }
376 VibrationEffect.OneShot other = (VibrationEffect.OneShot) o;
Michael Wright35a0c672018-01-24 00:32:53 +0000377 return other.mDuration == mDuration && other.mAmplitude == mAmplitude;
Michael Wright71216972017-01-31 18:33:54 +0000378 }
379
380 @Override
381 public int hashCode() {
382 int result = 17;
Michael Wright35a0c672018-01-24 00:32:53 +0000383 result += 37 * (int) mDuration;
384 result += 37 * mAmplitude;
Michael Wright71216972017-01-31 18:33:54 +0000385 return result;
386 }
387
388 @Override
389 public String toString() {
Michael Wright35a0c672018-01-24 00:32:53 +0000390 return "OneShot{mDuration=" + mDuration + ", mAmplitude=" + mAmplitude + "}";
Michael Wright71216972017-01-31 18:33:54 +0000391 }
392
393 @Override
394 public void writeToParcel(Parcel out, int flags) {
395 out.writeInt(PARCEL_TOKEN_ONE_SHOT);
Michael Wright35a0c672018-01-24 00:32:53 +0000396 out.writeLong(mDuration);
Michael Wright71216972017-01-31 18:33:54 +0000397 out.writeInt(mAmplitude);
398 }
399
400 public static final Parcelable.Creator<OneShot> CREATOR =
401 new Parcelable.Creator<OneShot>() {
402 @Override
403 public OneShot createFromParcel(Parcel in) {
404 // Skip the type token
405 in.readInt();
406 return new OneShot(in);
407 }
408 @Override
409 public OneShot[] newArray(int size) {
410 return new OneShot[size];
411 }
412 };
413 }
414
415 /** @hide */
416 public static class Waveform extends VibrationEffect implements Parcelable {
Michael Wright35a0c672018-01-24 00:32:53 +0000417 private final long[] mTimings;
418 private final int[] mAmplitudes;
419 private final int mRepeat;
Michael Wright71216972017-01-31 18:33:54 +0000420
421 public Waveform(Parcel in) {
422 this(in.createLongArray(), in.createIntArray(), in.readInt());
423 }
424
425 public Waveform(long[] timings, int[] amplitudes, int repeat) {
426 mTimings = new long[timings.length];
427 System.arraycopy(timings, 0, mTimings, 0, timings.length);
428 mAmplitudes = new int[amplitudes.length];
429 System.arraycopy(amplitudes, 0, mAmplitudes, 0, amplitudes.length);
430 mRepeat = repeat;
431 }
432
433 public long[] getTimings() {
434 return mTimings;
435 }
436
437 public int[] getAmplitudes() {
438 return mAmplitudes;
439 }
440
441 public int getRepeatIndex() {
442 return mRepeat;
443 }
444
445 @Override
Michael Wright35a0c672018-01-24 00:32:53 +0000446 public long getDuration() {
447 if (mRepeat >= 0) {
448 return Long.MAX_VALUE;
449 }
450 long duration = 0;
451 for (long d : mTimings) {
452 duration += d;
453 }
454 return duration;
455 }
456
457 /**
458 * Scale the Waveform with the given gamma and new max amplitude.
459 *
460 * @param gamma the gamma adjustment to apply
Alexey Kuzminfebc5d52018-03-02 18:54:06 +0000461 * @param maxAmplitude the new maximum amplitude of the effect, must be between 0 and
462 * MAX_AMPLITUDE
463 * @throws IllegalArgumentException if maxAmplitude less than 0 or more than MAX_AMPLITUDE
Michael Wright35a0c672018-01-24 00:32:53 +0000464 *
465 * @return A {@link Waveform} effect with the same timings and repeat index
466 * but scaled amplitude.
467 */
Alexey Kuzminfebc5d52018-03-02 18:54:06 +0000468 public Waveform scale(float gamma, int maxAmplitude) {
469 if (maxAmplitude > MAX_AMPLITUDE || maxAmplitude < 0) {
470 throw new IllegalArgumentException(
471 "Amplitude is negative or greater than MAX_AMPLITUDE");
472 }
Michael Wright35a0c672018-01-24 00:32:53 +0000473 if (gamma == 1.0f && maxAmplitude == MAX_AMPLITUDE) {
474 // Just return a copy of the original if there's no scaling to be done.
475 return new Waveform(mTimings, mAmplitudes, mRepeat);
476 }
477
478 int[] scaledAmplitudes = Arrays.copyOf(mAmplitudes, mAmplitudes.length);
479 for (int i = 0; i < scaledAmplitudes.length; i++) {
480 scaledAmplitudes[i] = scale(scaledAmplitudes[i], gamma, maxAmplitude);
481 }
482 return new Waveform(mTimings, scaledAmplitudes, mRepeat);
483 }
484
485 @Override
Michael Wright71216972017-01-31 18:33:54 +0000486 public void validate() {
487 if (mTimings.length != mAmplitudes.length) {
488 throw new IllegalArgumentException(
Michael Wright35a0c672018-01-24 00:32:53 +0000489 "timing and amplitude arrays must be of equal length"
490 + " (timings.length=" + mTimings.length
491 + ", amplitudes.length=" + mAmplitudes.length + ")");
Michael Wright71216972017-01-31 18:33:54 +0000492 }
493 if (!hasNonZeroEntry(mTimings)) {
Michael Wright35a0c672018-01-24 00:32:53 +0000494 throw new IllegalArgumentException("at least one timing must be non-zero"
495 + " (timings=" + Arrays.toString(mTimings) + ")");
Michael Wright71216972017-01-31 18:33:54 +0000496 }
497 for (long timing : mTimings) {
498 if (timing < 0) {
Michael Wright35a0c672018-01-24 00:32:53 +0000499 throw new IllegalArgumentException("timings must all be >= 0"
500 + " (timings=" + Arrays.toString(mTimings) + ")");
Michael Wright71216972017-01-31 18:33:54 +0000501 }
502 }
503 for (int amplitude : mAmplitudes) {
504 if (amplitude < -1 || amplitude > 255) {
505 throw new IllegalArgumentException(
Michael Wright35a0c672018-01-24 00:32:53 +0000506 "amplitudes must all be DEFAULT_AMPLITUDE or between 0 and 255"
507 + " (amplitudes=" + Arrays.toString(mAmplitudes) + ")");
Michael Wright71216972017-01-31 18:33:54 +0000508 }
509 }
510 if (mRepeat < -1 || mRepeat >= mTimings.length) {
Michael Wright0ef6edd2017-04-05 20:57:39 +0100511 throw new IllegalArgumentException(
Michael Wright35a0c672018-01-24 00:32:53 +0000512 "repeat index must be within the bounds of the timings array"
513 + " (timings.length=" + mTimings.length + ", index=" + mRepeat + ")");
Michael Wright71216972017-01-31 18:33:54 +0000514 }
515 }
516
517 @Override
518 public boolean equals(Object o) {
519 if (!(o instanceof VibrationEffect.Waveform)) {
520 return false;
521 }
522 VibrationEffect.Waveform other = (VibrationEffect.Waveform) o;
Michael Wright35a0c672018-01-24 00:32:53 +0000523 return Arrays.equals(mTimings, other.mTimings)
524 && Arrays.equals(mAmplitudes, other.mAmplitudes)
525 && mRepeat == other.mRepeat;
Michael Wright71216972017-01-31 18:33:54 +0000526 }
527
528 @Override
529 public int hashCode() {
530 int result = 17;
Michael Wright35a0c672018-01-24 00:32:53 +0000531 result += 37 * Arrays.hashCode(mTimings);
532 result += 37 * Arrays.hashCode(mAmplitudes);
533 result += 37 * mRepeat;
Michael Wright71216972017-01-31 18:33:54 +0000534 return result;
535 }
536
537 @Override
538 public String toString() {
Michael Wright35a0c672018-01-24 00:32:53 +0000539 return "Waveform{mTimings=" + Arrays.toString(mTimings)
540 + ", mAmplitudes=" + Arrays.toString(mAmplitudes)
541 + ", mRepeat=" + mRepeat
542 + "}";
Michael Wright71216972017-01-31 18:33:54 +0000543 }
544
545 @Override
546 public void writeToParcel(Parcel out, int flags) {
547 out.writeInt(PARCEL_TOKEN_WAVEFORM);
548 out.writeLongArray(mTimings);
549 out.writeIntArray(mAmplitudes);
550 out.writeInt(mRepeat);
551 }
552
553 private static boolean hasNonZeroEntry(long[] vals) {
554 for (long val : vals) {
555 if (val != 0) {
556 return true;
557 }
558 }
559 return false;
560 }
561
562
563 public static final Parcelable.Creator<Waveform> CREATOR =
564 new Parcelable.Creator<Waveform>() {
565 @Override
566 public Waveform createFromParcel(Parcel in) {
567 // Skip the type token
568 in.readInt();
569 return new Waveform(in);
570 }
571 @Override
572 public Waveform[] newArray(int size) {
573 return new Waveform[size];
574 }
575 };
576 }
577
578 /** @hide */
579 public static class Prebaked extends VibrationEffect implements Parcelable {
Michael Wright35a0c672018-01-24 00:32:53 +0000580 private final int mEffectId;
581 private final boolean mFallback;
582
583 private int mEffectStrength;
Michael Wright71216972017-01-31 18:33:54 +0000584
585 public Prebaked(Parcel in) {
Michael Wrightdc2b3be2017-08-02 20:44:45 +0100586 this(in.readInt(), in.readByte() != 0);
Michael Wright35a0c672018-01-24 00:32:53 +0000587 mEffectStrength = in.readInt();
Michael Wright71216972017-01-31 18:33:54 +0000588 }
589
Michael Wrightdc2b3be2017-08-02 20:44:45 +0100590 public Prebaked(int effectId, boolean fallback) {
Michael Wright71216972017-01-31 18:33:54 +0000591 mEffectId = effectId;
Michael Wrightdc2b3be2017-08-02 20:44:45 +0100592 mFallback = fallback;
Michael Wright35a0c672018-01-24 00:32:53 +0000593 mEffectStrength = EffectStrength.MEDIUM;
Michael Wright71216972017-01-31 18:33:54 +0000594 }
595
596 public int getId() {
597 return mEffectId;
598 }
599
Michael Wrightdc2b3be2017-08-02 20:44:45 +0100600 /**
601 * Whether the effect should fall back to a generic pattern if there's no hardware specific
602 * implementation of it.
603 */
604 public boolean shouldFallback() {
605 return mFallback;
606 }
607
Michael Wright71216972017-01-31 18:33:54 +0000608 @Override
Michael Wright35a0c672018-01-24 00:32:53 +0000609 public long getDuration() {
610 return -1;
611 }
612
613 /**
614 * Set the effect strength of the prebaked effect.
615 */
616 public void setEffectStrength(int strength) {
617 if (!isValidEffectStrength(strength)) {
618 throw new IllegalArgumentException("Invalid effect strength: " + strength);
619 }
620 mEffectStrength = strength;
621 }
622
623 /**
624 * Set the effect strength.
625 */
626 public int getEffectStrength() {
627 return mEffectStrength;
628 }
629
630 private static boolean isValidEffectStrength(int strength) {
631 switch (strength) {
632 case EffectStrength.LIGHT:
633 case EffectStrength.MEDIUM:
634 case EffectStrength.STRONG:
635 return true;
636 default:
637 return false;
638 }
639 }
640
641 @Override
Michael Wright71216972017-01-31 18:33:54 +0000642 public void validate() {
Michael Wright57d94d92017-05-31 14:44:45 +0100643 switch (mEffectId) {
644 case EFFECT_CLICK:
645 case EFFECT_DOUBLE_CLICK:
646 case EFFECT_TICK:
Michael Wrightf268bf52018-02-07 23:23:34 +0000647 case EFFECT_THUD:
648 case EFFECT_POP:
649 case EFFECT_HEAVY_CLICK:
Michael Wright57d94d92017-05-31 14:44:45 +0100650 break;
651 default:
Michael Wrightf268bf52018-02-07 23:23:34 +0000652 if (mEffectId < RINGTONES[0] || mEffectId > RINGTONES[RINGTONES.length - 1]) {
653 throw new IllegalArgumentException(
654 "Unknown prebaked effect type (value=" + mEffectId + ")");
655 }
Michael Wright71216972017-01-31 18:33:54 +0000656 }
Michael Wright35a0c672018-01-24 00:32:53 +0000657 if (!isValidEffectStrength(mEffectStrength)) {
658 throw new IllegalArgumentException(
659 "Unknown prebaked effect strength (value=" + mEffectStrength + ")");
660 }
Michael Wright71216972017-01-31 18:33:54 +0000661 }
662
663 @Override
664 public boolean equals(Object o) {
665 if (!(o instanceof VibrationEffect.Prebaked)) {
666 return false;
667 }
668 VibrationEffect.Prebaked other = (VibrationEffect.Prebaked) o;
Michael Wright35a0c672018-01-24 00:32:53 +0000669 return mEffectId == other.mEffectId
670 && mFallback == other.mFallback
671 && mEffectStrength == other.mEffectStrength;
Michael Wright71216972017-01-31 18:33:54 +0000672 }
673
674 @Override
675 public int hashCode() {
Michael Wright35a0c672018-01-24 00:32:53 +0000676 int result = 17;
677 result += 37 * mEffectId;
678 result += 37 * mEffectStrength;
679 return result;
Michael Wright71216972017-01-31 18:33:54 +0000680 }
681
682 @Override
683 public String toString() {
Michael Wright35a0c672018-01-24 00:32:53 +0000684 return "Prebaked{mEffectId=" + mEffectId
685 + ", mEffectStrength=" + mEffectStrength
686 + ", mFallback=" + mFallback
687 + "}";
Michael Wright71216972017-01-31 18:33:54 +0000688 }
689
690
691 @Override
692 public void writeToParcel(Parcel out, int flags) {
693 out.writeInt(PARCEL_TOKEN_EFFECT);
694 out.writeInt(mEffectId);
Michael Wrightdc2b3be2017-08-02 20:44:45 +0100695 out.writeByte((byte) (mFallback ? 1 : 0));
Michael Wright35a0c672018-01-24 00:32:53 +0000696 out.writeInt(mEffectStrength);
Michael Wright71216972017-01-31 18:33:54 +0000697 }
698
699 public static final Parcelable.Creator<Prebaked> CREATOR =
700 new Parcelable.Creator<Prebaked>() {
701 @Override
702 public Prebaked createFromParcel(Parcel in) {
703 // Skip the type token
704 in.readInt();
705 return new Prebaked(in);
706 }
707 @Override
708 public Prebaked[] newArray(int size) {
709 return new Prebaked[size];
710 }
711 };
712 }
713
714 public static final Parcelable.Creator<VibrationEffect> CREATOR =
715 new Parcelable.Creator<VibrationEffect>() {
716 @Override
717 public VibrationEffect createFromParcel(Parcel in) {
718 int token = in.readInt();
719 if (token == PARCEL_TOKEN_ONE_SHOT) {
720 return new OneShot(in);
721 } else if (token == PARCEL_TOKEN_WAVEFORM) {
722 return new Waveform(in);
723 } else if (token == PARCEL_TOKEN_EFFECT) {
724 return new Prebaked(in);
725 } else {
726 throw new IllegalStateException(
727 "Unexpected vibration event type token in parcel.");
728 }
729 }
730 @Override
731 public VibrationEffect[] newArray(int size) {
732 return new VibrationEffect[size];
733 }
734 };
735}