blob: e9b48535a34ef399abf8b09252ee184d56ee69e4 [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
343 * @param maxAmplitude the new maximum amplitude of the effect
344 *
345 * @return A {@link OneShot} effect with the same timing but scaled amplitude.
346 */
347 public VibrationEffect scale(float gamma, int maxAmplitude) {
348 int newAmplitude = scale(mAmplitude, gamma, maxAmplitude);
349 return new OneShot(mDuration, newAmplitude);
350 }
351
Michael Wright71216972017-01-31 18:33:54 +0000352 @Override
353 public void validate() {
354 if (mAmplitude < -1 || mAmplitude == 0 || mAmplitude > 255) {
355 throw new IllegalArgumentException(
Michael Wright35a0c672018-01-24 00:32:53 +0000356 "amplitude must either be DEFAULT_AMPLITUDE, "
357 + "or between 1 and 255 inclusive (amplitude=" + mAmplitude + ")");
Michael Wright71216972017-01-31 18:33:54 +0000358 }
Michael Wright35a0c672018-01-24 00:32:53 +0000359 if (mDuration <= 0) {
Michael Wright0ef6edd2017-04-05 20:57:39 +0100360 throw new IllegalArgumentException(
Michael Wright35a0c672018-01-24 00:32:53 +0000361 "duration must be positive (duration=" + mDuration + ")");
Michael Wright71216972017-01-31 18:33:54 +0000362 }
363 }
364
365 @Override
366 public boolean equals(Object o) {
367 if (!(o instanceof VibrationEffect.OneShot)) {
368 return false;
369 }
370 VibrationEffect.OneShot other = (VibrationEffect.OneShot) o;
Michael Wright35a0c672018-01-24 00:32:53 +0000371 return other.mDuration == mDuration && other.mAmplitude == mAmplitude;
Michael Wright71216972017-01-31 18:33:54 +0000372 }
373
374 @Override
375 public int hashCode() {
376 int result = 17;
Michael Wright35a0c672018-01-24 00:32:53 +0000377 result += 37 * (int) mDuration;
378 result += 37 * mAmplitude;
Michael Wright71216972017-01-31 18:33:54 +0000379 return result;
380 }
381
382 @Override
383 public String toString() {
Michael Wright35a0c672018-01-24 00:32:53 +0000384 return "OneShot{mDuration=" + mDuration + ", mAmplitude=" + mAmplitude + "}";
Michael Wright71216972017-01-31 18:33:54 +0000385 }
386
387 @Override
388 public void writeToParcel(Parcel out, int flags) {
389 out.writeInt(PARCEL_TOKEN_ONE_SHOT);
Michael Wright35a0c672018-01-24 00:32:53 +0000390 out.writeLong(mDuration);
Michael Wright71216972017-01-31 18:33:54 +0000391 out.writeInt(mAmplitude);
392 }
393
394 public static final Parcelable.Creator<OneShot> CREATOR =
395 new Parcelable.Creator<OneShot>() {
396 @Override
397 public OneShot createFromParcel(Parcel in) {
398 // Skip the type token
399 in.readInt();
400 return new OneShot(in);
401 }
402 @Override
403 public OneShot[] newArray(int size) {
404 return new OneShot[size];
405 }
406 };
407 }
408
409 /** @hide */
410 public static class Waveform extends VibrationEffect implements Parcelable {
Michael Wright35a0c672018-01-24 00:32:53 +0000411 private final long[] mTimings;
412 private final int[] mAmplitudes;
413 private final int mRepeat;
Michael Wright71216972017-01-31 18:33:54 +0000414
415 public Waveform(Parcel in) {
416 this(in.createLongArray(), in.createIntArray(), in.readInt());
417 }
418
419 public Waveform(long[] timings, int[] amplitudes, int repeat) {
420 mTimings = new long[timings.length];
421 System.arraycopy(timings, 0, mTimings, 0, timings.length);
422 mAmplitudes = new int[amplitudes.length];
423 System.arraycopy(amplitudes, 0, mAmplitudes, 0, amplitudes.length);
424 mRepeat = repeat;
425 }
426
427 public long[] getTimings() {
428 return mTimings;
429 }
430
431 public int[] getAmplitudes() {
432 return mAmplitudes;
433 }
434
435 public int getRepeatIndex() {
436 return mRepeat;
437 }
438
439 @Override
Michael Wright35a0c672018-01-24 00:32:53 +0000440 public long getDuration() {
441 if (mRepeat >= 0) {
442 return Long.MAX_VALUE;
443 }
444 long duration = 0;
445 for (long d : mTimings) {
446 duration += d;
447 }
448 return duration;
449 }
450
451 /**
452 * Scale the Waveform with the given gamma and new max amplitude.
453 *
454 * @param gamma the gamma adjustment to apply
455 * @param maxAmplitude the new maximum amplitude of the effect
456 *
457 * @return A {@link Waveform} effect with the same timings and repeat index
458 * but scaled amplitude.
459 */
460 public VibrationEffect scale(float gamma, int maxAmplitude) {
461 if (gamma == 1.0f && maxAmplitude == MAX_AMPLITUDE) {
462 // Just return a copy of the original if there's no scaling to be done.
463 return new Waveform(mTimings, mAmplitudes, mRepeat);
464 }
465
466 int[] scaledAmplitudes = Arrays.copyOf(mAmplitudes, mAmplitudes.length);
467 for (int i = 0; i < scaledAmplitudes.length; i++) {
468 scaledAmplitudes[i] = scale(scaledAmplitudes[i], gamma, maxAmplitude);
469 }
470 return new Waveform(mTimings, scaledAmplitudes, mRepeat);
471 }
472
473 @Override
Michael Wright71216972017-01-31 18:33:54 +0000474 public void validate() {
475 if (mTimings.length != mAmplitudes.length) {
476 throw new IllegalArgumentException(
Michael Wright35a0c672018-01-24 00:32:53 +0000477 "timing and amplitude arrays must be of equal length"
478 + " (timings.length=" + mTimings.length
479 + ", amplitudes.length=" + mAmplitudes.length + ")");
Michael Wright71216972017-01-31 18:33:54 +0000480 }
481 if (!hasNonZeroEntry(mTimings)) {
Michael Wright35a0c672018-01-24 00:32:53 +0000482 throw new IllegalArgumentException("at least one timing must be non-zero"
483 + " (timings=" + Arrays.toString(mTimings) + ")");
Michael Wright71216972017-01-31 18:33:54 +0000484 }
485 for (long timing : mTimings) {
486 if (timing < 0) {
Michael Wright35a0c672018-01-24 00:32:53 +0000487 throw new IllegalArgumentException("timings must all be >= 0"
488 + " (timings=" + Arrays.toString(mTimings) + ")");
Michael Wright71216972017-01-31 18:33:54 +0000489 }
490 }
491 for (int amplitude : mAmplitudes) {
492 if (amplitude < -1 || amplitude > 255) {
493 throw new IllegalArgumentException(
Michael Wright35a0c672018-01-24 00:32:53 +0000494 "amplitudes must all be DEFAULT_AMPLITUDE or between 0 and 255"
495 + " (amplitudes=" + Arrays.toString(mAmplitudes) + ")");
Michael Wright71216972017-01-31 18:33:54 +0000496 }
497 }
498 if (mRepeat < -1 || mRepeat >= mTimings.length) {
Michael Wright0ef6edd2017-04-05 20:57:39 +0100499 throw new IllegalArgumentException(
Michael Wright35a0c672018-01-24 00:32:53 +0000500 "repeat index must be within the bounds of the timings array"
501 + " (timings.length=" + mTimings.length + ", index=" + mRepeat + ")");
Michael Wright71216972017-01-31 18:33:54 +0000502 }
503 }
504
505 @Override
506 public boolean equals(Object o) {
507 if (!(o instanceof VibrationEffect.Waveform)) {
508 return false;
509 }
510 VibrationEffect.Waveform other = (VibrationEffect.Waveform) o;
Michael Wright35a0c672018-01-24 00:32:53 +0000511 return Arrays.equals(mTimings, other.mTimings)
512 && Arrays.equals(mAmplitudes, other.mAmplitudes)
513 && mRepeat == other.mRepeat;
Michael Wright71216972017-01-31 18:33:54 +0000514 }
515
516 @Override
517 public int hashCode() {
518 int result = 17;
Michael Wright35a0c672018-01-24 00:32:53 +0000519 result += 37 * Arrays.hashCode(mTimings);
520 result += 37 * Arrays.hashCode(mAmplitudes);
521 result += 37 * mRepeat;
Michael Wright71216972017-01-31 18:33:54 +0000522 return result;
523 }
524
525 @Override
526 public String toString() {
Michael Wright35a0c672018-01-24 00:32:53 +0000527 return "Waveform{mTimings=" + Arrays.toString(mTimings)
528 + ", mAmplitudes=" + Arrays.toString(mAmplitudes)
529 + ", mRepeat=" + mRepeat
530 + "}";
Michael Wright71216972017-01-31 18:33:54 +0000531 }
532
533 @Override
534 public void writeToParcel(Parcel out, int flags) {
535 out.writeInt(PARCEL_TOKEN_WAVEFORM);
536 out.writeLongArray(mTimings);
537 out.writeIntArray(mAmplitudes);
538 out.writeInt(mRepeat);
539 }
540
541 private static boolean hasNonZeroEntry(long[] vals) {
542 for (long val : vals) {
543 if (val != 0) {
544 return true;
545 }
546 }
547 return false;
548 }
549
550
551 public static final Parcelable.Creator<Waveform> CREATOR =
552 new Parcelable.Creator<Waveform>() {
553 @Override
554 public Waveform createFromParcel(Parcel in) {
555 // Skip the type token
556 in.readInt();
557 return new Waveform(in);
558 }
559 @Override
560 public Waveform[] newArray(int size) {
561 return new Waveform[size];
562 }
563 };
564 }
565
566 /** @hide */
567 public static class Prebaked extends VibrationEffect implements Parcelable {
Michael Wright35a0c672018-01-24 00:32:53 +0000568 private final int mEffectId;
569 private final boolean mFallback;
570
571 private int mEffectStrength;
Michael Wright71216972017-01-31 18:33:54 +0000572
573 public Prebaked(Parcel in) {
Michael Wrightdc2b3be2017-08-02 20:44:45 +0100574 this(in.readInt(), in.readByte() != 0);
Michael Wright35a0c672018-01-24 00:32:53 +0000575 mEffectStrength = in.readInt();
Michael Wright71216972017-01-31 18:33:54 +0000576 }
577
Michael Wrightdc2b3be2017-08-02 20:44:45 +0100578 public Prebaked(int effectId, boolean fallback) {
Michael Wright71216972017-01-31 18:33:54 +0000579 mEffectId = effectId;
Michael Wrightdc2b3be2017-08-02 20:44:45 +0100580 mFallback = fallback;
Michael Wright35a0c672018-01-24 00:32:53 +0000581 mEffectStrength = EffectStrength.MEDIUM;
Michael Wright71216972017-01-31 18:33:54 +0000582 }
583
584 public int getId() {
585 return mEffectId;
586 }
587
Michael Wrightdc2b3be2017-08-02 20:44:45 +0100588 /**
589 * Whether the effect should fall back to a generic pattern if there's no hardware specific
590 * implementation of it.
591 */
592 public boolean shouldFallback() {
593 return mFallback;
594 }
595
Michael Wright71216972017-01-31 18:33:54 +0000596 @Override
Michael Wright35a0c672018-01-24 00:32:53 +0000597 public long getDuration() {
598 return -1;
599 }
600
601 /**
602 * Set the effect strength of the prebaked effect.
603 */
604 public void setEffectStrength(int strength) {
605 if (!isValidEffectStrength(strength)) {
606 throw new IllegalArgumentException("Invalid effect strength: " + strength);
607 }
608 mEffectStrength = strength;
609 }
610
611 /**
612 * Set the effect strength.
613 */
614 public int getEffectStrength() {
615 return mEffectStrength;
616 }
617
618 private static boolean isValidEffectStrength(int strength) {
619 switch (strength) {
620 case EffectStrength.LIGHT:
621 case EffectStrength.MEDIUM:
622 case EffectStrength.STRONG:
623 return true;
624 default:
625 return false;
626 }
627 }
628
629 @Override
Michael Wright71216972017-01-31 18:33:54 +0000630 public void validate() {
Michael Wright57d94d92017-05-31 14:44:45 +0100631 switch (mEffectId) {
632 case EFFECT_CLICK:
633 case EFFECT_DOUBLE_CLICK:
634 case EFFECT_TICK:
Michael Wrightf268bf52018-02-07 23:23:34 +0000635 case EFFECT_THUD:
636 case EFFECT_POP:
637 case EFFECT_HEAVY_CLICK:
Michael Wright57d94d92017-05-31 14:44:45 +0100638 break;
639 default:
Michael Wrightf268bf52018-02-07 23:23:34 +0000640 if (mEffectId < RINGTONES[0] || mEffectId > RINGTONES[RINGTONES.length - 1]) {
641 throw new IllegalArgumentException(
642 "Unknown prebaked effect type (value=" + mEffectId + ")");
643 }
Michael Wright71216972017-01-31 18:33:54 +0000644 }
Michael Wright35a0c672018-01-24 00:32:53 +0000645 if (!isValidEffectStrength(mEffectStrength)) {
646 throw new IllegalArgumentException(
647 "Unknown prebaked effect strength (value=" + mEffectStrength + ")");
648 }
Michael Wright71216972017-01-31 18:33:54 +0000649 }
650
651 @Override
652 public boolean equals(Object o) {
653 if (!(o instanceof VibrationEffect.Prebaked)) {
654 return false;
655 }
656 VibrationEffect.Prebaked other = (VibrationEffect.Prebaked) o;
Michael Wright35a0c672018-01-24 00:32:53 +0000657 return mEffectId == other.mEffectId
658 && mFallback == other.mFallback
659 && mEffectStrength == other.mEffectStrength;
Michael Wright71216972017-01-31 18:33:54 +0000660 }
661
662 @Override
663 public int hashCode() {
Michael Wright35a0c672018-01-24 00:32:53 +0000664 int result = 17;
665 result += 37 * mEffectId;
666 result += 37 * mEffectStrength;
667 return result;
Michael Wright71216972017-01-31 18:33:54 +0000668 }
669
670 @Override
671 public String toString() {
Michael Wright35a0c672018-01-24 00:32:53 +0000672 return "Prebaked{mEffectId=" + mEffectId
673 + ", mEffectStrength=" + mEffectStrength
674 + ", mFallback=" + mFallback
675 + "}";
Michael Wright71216972017-01-31 18:33:54 +0000676 }
677
678
679 @Override
680 public void writeToParcel(Parcel out, int flags) {
681 out.writeInt(PARCEL_TOKEN_EFFECT);
682 out.writeInt(mEffectId);
Michael Wrightdc2b3be2017-08-02 20:44:45 +0100683 out.writeByte((byte) (mFallback ? 1 : 0));
Michael Wright35a0c672018-01-24 00:32:53 +0000684 out.writeInt(mEffectStrength);
Michael Wright71216972017-01-31 18:33:54 +0000685 }
686
687 public static final Parcelable.Creator<Prebaked> CREATOR =
688 new Parcelable.Creator<Prebaked>() {
689 @Override
690 public Prebaked createFromParcel(Parcel in) {
691 // Skip the type token
692 in.readInt();
693 return new Prebaked(in);
694 }
695 @Override
696 public Prebaked[] newArray(int size) {
697 return new Prebaked[size];
698 }
699 };
700 }
701
702 public static final Parcelable.Creator<VibrationEffect> CREATOR =
703 new Parcelable.Creator<VibrationEffect>() {
704 @Override
705 public VibrationEffect createFromParcel(Parcel in) {
706 int token = in.readInt();
707 if (token == PARCEL_TOKEN_ONE_SHOT) {
708 return new OneShot(in);
709 } else if (token == PARCEL_TOKEN_WAVEFORM) {
710 return new Waveform(in);
711 } else if (token == PARCEL_TOKEN_EFFECT) {
712 return new Prebaked(in);
713 } else {
714 throw new IllegalStateException(
715 "Unexpected vibration event type token in parcel.");
716 }
717 }
718 @Override
719 public VibrationEffect[] newArray(int size) {
720 return new VibrationEffect[size];
721 }
722 };
723}