VibrationEffect.java revision 59efe9734e5499c9f1a325df480247b57e0c8da4
1/* 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 19import 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; 24import android.util.MathUtils; 25 26import com.android.internal.annotations.VisibleForTesting; 27 28import 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 /** 46 * The maximum amplitude value 47 * @hide 48 */ 49 public static final int MAX_AMPLITUDE = 255; 50 51 /** 52 * A click effect. 53 * 54 * @see #get(int) 55 * @hide 56 */ 57 public static final int EFFECT_CLICK = Effect.CLICK; 58 59 /** 60 * A double click effect. 61 * 62 * @see #get(int) 63 * @hide 64 */ 65 public static final int EFFECT_DOUBLE_CLICK = Effect.DOUBLE_CLICK; 66 67 /** 68 * A tick effect. 69 * @see #get(int) 70 * @hide 71 */ 72 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 }; 121 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 * 213 * This will fallback to a generic pattern if one exists and there does not exist a 214 * hardware-specific implementation of the effect. 215 * 216 * @param effectId The ID of the effect to perform: 217 * {@link #EFFECT_CLICK}, {@link #EFFECT_DOUBLE_CLICK}, {@link #EFFECT_TICK} 218 * 219 * @return The desired effect. 220 * @hide 221 */ 222 public static VibrationEffect get(int effectId) { 223 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); 250 effect.validate(); 251 return effect; 252 } 253 254 /** 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 285 @Override 286 public int describeContents() { 287 return 0; 288 } 289 290 /** @hide */ 291 public abstract void validate(); 292 293 /** 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 315 /** @hide */ 316 public static class OneShot extends VibrationEffect implements Parcelable { 317 private final long mDuration; 318 private final int mAmplitude; 319 320 public OneShot(Parcel in) { 321 mDuration = in.readLong(); 322 mAmplitude = in.readInt(); 323 } 324 325 public OneShot(long milliseconds, int amplitude) { 326 mDuration = milliseconds; 327 mAmplitude = amplitude; 328 } 329 330 @Override 331 public long getDuration() { 332 return mDuration; 333 } 334 335 public int getAmplitude() { 336 return mAmplitude; 337 } 338 339 /** 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 352 /** 353 * Resolve default values into integer amplitude numbers. 354 * 355 * @param defaultAmplitude the default amplitude to apply, must be between 0 and 356 * MAX_AMPLITUDE 357 * @return A {@link OneShot} effect with same physical meaning but explicitly set amplitude 358 * 359 * @hide 360 */ 361 public OneShot resolve(int defaultAmplitude) { 362 if (defaultAmplitude > MAX_AMPLITUDE || defaultAmplitude < 0) { 363 throw new IllegalArgumentException( 364 "Amplitude is negative or greater than MAX_AMPLITUDE"); 365 } 366 if (mAmplitude == DEFAULT_AMPLITUDE) { 367 return new OneShot(mDuration, defaultAmplitude); 368 } 369 return this; 370 } 371 372 @Override 373 public void validate() { 374 if (mAmplitude < -1 || mAmplitude == 0 || mAmplitude > 255) { 375 throw new IllegalArgumentException( 376 "amplitude must either be DEFAULT_AMPLITUDE, " 377 + "or between 1 and 255 inclusive (amplitude=" + mAmplitude + ")"); 378 } 379 if (mDuration <= 0) { 380 throw new IllegalArgumentException( 381 "duration must be positive (duration=" + mDuration + ")"); 382 } 383 } 384 385 @Override 386 public boolean equals(Object o) { 387 if (!(o instanceof VibrationEffect.OneShot)) { 388 return false; 389 } 390 VibrationEffect.OneShot other = (VibrationEffect.OneShot) o; 391 return other.mDuration == mDuration && other.mAmplitude == mAmplitude; 392 } 393 394 @Override 395 public int hashCode() { 396 int result = 17; 397 result += 37 * (int) mDuration; 398 result += 37 * mAmplitude; 399 return result; 400 } 401 402 @Override 403 public String toString() { 404 return "OneShot{mDuration=" + mDuration + ", mAmplitude=" + mAmplitude + "}"; 405 } 406 407 @Override 408 public void writeToParcel(Parcel out, int flags) { 409 out.writeInt(PARCEL_TOKEN_ONE_SHOT); 410 out.writeLong(mDuration); 411 out.writeInt(mAmplitude); 412 } 413 414 public static final Parcelable.Creator<OneShot> CREATOR = 415 new Parcelable.Creator<OneShot>() { 416 @Override 417 public OneShot createFromParcel(Parcel in) { 418 // Skip the type token 419 in.readInt(); 420 return new OneShot(in); 421 } 422 @Override 423 public OneShot[] newArray(int size) { 424 return new OneShot[size]; 425 } 426 }; 427 } 428 429 /** @hide */ 430 public static class Waveform extends VibrationEffect implements Parcelable { 431 private final long[] mTimings; 432 private final int[] mAmplitudes; 433 private final int mRepeat; 434 435 public Waveform(Parcel in) { 436 this(in.createLongArray(), in.createIntArray(), in.readInt()); 437 } 438 439 public Waveform(long[] timings, int[] amplitudes, int repeat) { 440 mTimings = new long[timings.length]; 441 System.arraycopy(timings, 0, mTimings, 0, timings.length); 442 mAmplitudes = new int[amplitudes.length]; 443 System.arraycopy(amplitudes, 0, mAmplitudes, 0, amplitudes.length); 444 mRepeat = repeat; 445 } 446 447 public long[] getTimings() { 448 return mTimings; 449 } 450 451 public int[] getAmplitudes() { 452 return mAmplitudes; 453 } 454 455 public int getRepeatIndex() { 456 return mRepeat; 457 } 458 459 @Override 460 public long getDuration() { 461 if (mRepeat >= 0) { 462 return Long.MAX_VALUE; 463 } 464 long duration = 0; 465 for (long d : mTimings) { 466 duration += d; 467 } 468 return duration; 469 } 470 471 /** 472 * Scale the Waveform with the given gamma and new max amplitude. 473 * 474 * @param gamma the gamma adjustment to apply 475 * @param maxAmplitude the new maximum amplitude of the effect 476 * 477 * @return A {@link Waveform} effect with the same timings and repeat index 478 * but scaled amplitude. 479 */ 480 public VibrationEffect scale(float gamma, int maxAmplitude) { 481 if (gamma == 1.0f && maxAmplitude == MAX_AMPLITUDE) { 482 // Just return a copy of the original if there's no scaling to be done. 483 return new Waveform(mTimings, mAmplitudes, mRepeat); 484 } 485 486 int[] scaledAmplitudes = Arrays.copyOf(mAmplitudes, mAmplitudes.length); 487 for (int i = 0; i < scaledAmplitudes.length; i++) { 488 scaledAmplitudes[i] = scale(scaledAmplitudes[i], gamma, maxAmplitude); 489 } 490 return new Waveform(mTimings, scaledAmplitudes, mRepeat); 491 } 492 493 /** 494 * Resolve default values into integer amplitude numbers. 495 * 496 * @param defaultAmplitude the default amplitude to apply, must be between 0 and 497 * MAX_AMPLITUDE 498 * @return A {@link Waveform} effect with same physical meaning but explicitly set 499 * amplitude 500 * 501 * @hide 502 */ 503 public Waveform resolve(int defaultAmplitude) { 504 if (defaultAmplitude > MAX_AMPLITUDE || defaultAmplitude < 0) { 505 throw new IllegalArgumentException( 506 "Amplitude is negative or greater than MAX_AMPLITUDE"); 507 } 508 int[] resolvedAmplitudes = Arrays.copyOf(mAmplitudes, mAmplitudes.length); 509 for (int i = 0; i < resolvedAmplitudes.length; i++) { 510 if (resolvedAmplitudes[i] == DEFAULT_AMPLITUDE) { 511 resolvedAmplitudes[i] = defaultAmplitude; 512 } 513 } 514 return new Waveform(mTimings, resolvedAmplitudes, mRepeat); 515 } 516 517 @Override 518 public void validate() { 519 if (mTimings.length != mAmplitudes.length) { 520 throw new IllegalArgumentException( 521 "timing and amplitude arrays must be of equal length" 522 + " (timings.length=" + mTimings.length 523 + ", amplitudes.length=" + mAmplitudes.length + ")"); 524 } 525 if (!hasNonZeroEntry(mTimings)) { 526 throw new IllegalArgumentException("at least one timing must be non-zero" 527 + " (timings=" + Arrays.toString(mTimings) + ")"); 528 } 529 for (long timing : mTimings) { 530 if (timing < 0) { 531 throw new IllegalArgumentException("timings must all be >= 0" 532 + " (timings=" + Arrays.toString(mTimings) + ")"); 533 } 534 } 535 for (int amplitude : mAmplitudes) { 536 if (amplitude < -1 || amplitude > 255) { 537 throw new IllegalArgumentException( 538 "amplitudes must all be DEFAULT_AMPLITUDE or between 0 and 255" 539 + " (amplitudes=" + Arrays.toString(mAmplitudes) + ")"); 540 } 541 } 542 if (mRepeat < -1 || mRepeat >= mTimings.length) { 543 throw new IllegalArgumentException( 544 "repeat index must be within the bounds of the timings array" 545 + " (timings.length=" + mTimings.length + ", index=" + mRepeat + ")"); 546 } 547 } 548 549 @Override 550 public boolean equals(Object o) { 551 if (!(o instanceof VibrationEffect.Waveform)) { 552 return false; 553 } 554 VibrationEffect.Waveform other = (VibrationEffect.Waveform) o; 555 return Arrays.equals(mTimings, other.mTimings) 556 && Arrays.equals(mAmplitudes, other.mAmplitudes) 557 && mRepeat == other.mRepeat; 558 } 559 560 @Override 561 public int hashCode() { 562 int result = 17; 563 result += 37 * Arrays.hashCode(mTimings); 564 result += 37 * Arrays.hashCode(mAmplitudes); 565 result += 37 * mRepeat; 566 return result; 567 } 568 569 @Override 570 public String toString() { 571 return "Waveform{mTimings=" + Arrays.toString(mTimings) 572 + ", mAmplitudes=" + Arrays.toString(mAmplitudes) 573 + ", mRepeat=" + mRepeat 574 + "}"; 575 } 576 577 @Override 578 public void writeToParcel(Parcel out, int flags) { 579 out.writeInt(PARCEL_TOKEN_WAVEFORM); 580 out.writeLongArray(mTimings); 581 out.writeIntArray(mAmplitudes); 582 out.writeInt(mRepeat); 583 } 584 585 private static boolean hasNonZeroEntry(long[] vals) { 586 for (long val : vals) { 587 if (val != 0) { 588 return true; 589 } 590 } 591 return false; 592 } 593 594 595 public static final Parcelable.Creator<Waveform> CREATOR = 596 new Parcelable.Creator<Waveform>() { 597 @Override 598 public Waveform createFromParcel(Parcel in) { 599 // Skip the type token 600 in.readInt(); 601 return new Waveform(in); 602 } 603 @Override 604 public Waveform[] newArray(int size) { 605 return new Waveform[size]; 606 } 607 }; 608 } 609 610 /** @hide */ 611 public static class Prebaked extends VibrationEffect implements Parcelable { 612 private final int mEffectId; 613 private final boolean mFallback; 614 615 private int mEffectStrength; 616 617 public Prebaked(Parcel in) { 618 this(in.readInt(), in.readByte() != 0); 619 mEffectStrength = in.readInt(); 620 } 621 622 public Prebaked(int effectId, boolean fallback) { 623 mEffectId = effectId; 624 mFallback = fallback; 625 mEffectStrength = EffectStrength.MEDIUM; 626 } 627 628 public int getId() { 629 return mEffectId; 630 } 631 632 /** 633 * Whether the effect should fall back to a generic pattern if there's no hardware specific 634 * implementation of it. 635 */ 636 public boolean shouldFallback() { 637 return mFallback; 638 } 639 640 @Override 641 public long getDuration() { 642 return -1; 643 } 644 645 /** 646 * Set the effect strength of the prebaked effect. 647 */ 648 public void setEffectStrength(int strength) { 649 if (!isValidEffectStrength(strength)) { 650 throw new IllegalArgumentException("Invalid effect strength: " + strength); 651 } 652 mEffectStrength = strength; 653 } 654 655 /** 656 * Set the effect strength. 657 */ 658 public int getEffectStrength() { 659 return mEffectStrength; 660 } 661 662 private static boolean isValidEffectStrength(int strength) { 663 switch (strength) { 664 case EffectStrength.LIGHT: 665 case EffectStrength.MEDIUM: 666 case EffectStrength.STRONG: 667 return true; 668 default: 669 return false; 670 } 671 } 672 673 @Override 674 public void validate() { 675 switch (mEffectId) { 676 case EFFECT_CLICK: 677 case EFFECT_DOUBLE_CLICK: 678 case EFFECT_TICK: 679 case EFFECT_THUD: 680 case EFFECT_POP: 681 case EFFECT_HEAVY_CLICK: 682 break; 683 default: 684 if (mEffectId < RINGTONES[0] || mEffectId > RINGTONES[RINGTONES.length - 1]) { 685 throw new IllegalArgumentException( 686 "Unknown prebaked effect type (value=" + mEffectId + ")"); 687 } 688 } 689 if (!isValidEffectStrength(mEffectStrength)) { 690 throw new IllegalArgumentException( 691 "Unknown prebaked effect strength (value=" + mEffectStrength + ")"); 692 } 693 } 694 695 @Override 696 public boolean equals(Object o) { 697 if (!(o instanceof VibrationEffect.Prebaked)) { 698 return false; 699 } 700 VibrationEffect.Prebaked other = (VibrationEffect.Prebaked) o; 701 return mEffectId == other.mEffectId 702 && mFallback == other.mFallback 703 && mEffectStrength == other.mEffectStrength; 704 } 705 706 @Override 707 public int hashCode() { 708 int result = 17; 709 result += 37 * mEffectId; 710 result += 37 * mEffectStrength; 711 return result; 712 } 713 714 @Override 715 public String toString() { 716 return "Prebaked{mEffectId=" + mEffectId 717 + ", mEffectStrength=" + mEffectStrength 718 + ", mFallback=" + mFallback 719 + "}"; 720 } 721 722 723 @Override 724 public void writeToParcel(Parcel out, int flags) { 725 out.writeInt(PARCEL_TOKEN_EFFECT); 726 out.writeInt(mEffectId); 727 out.writeByte((byte) (mFallback ? 1 : 0)); 728 out.writeInt(mEffectStrength); 729 } 730 731 public static final Parcelable.Creator<Prebaked> CREATOR = 732 new Parcelable.Creator<Prebaked>() { 733 @Override 734 public Prebaked createFromParcel(Parcel in) { 735 // Skip the type token 736 in.readInt(); 737 return new Prebaked(in); 738 } 739 @Override 740 public Prebaked[] newArray(int size) { 741 return new Prebaked[size]; 742 } 743 }; 744 } 745 746 public static final Parcelable.Creator<VibrationEffect> CREATOR = 747 new Parcelable.Creator<VibrationEffect>() { 748 @Override 749 public VibrationEffect createFromParcel(Parcel in) { 750 int token = in.readInt(); 751 if (token == PARCEL_TOKEN_ONE_SHOT) { 752 return new OneShot(in); 753 } else if (token == PARCEL_TOKEN_WAVEFORM) { 754 return new Waveform(in); 755 } else if (token == PARCEL_TOKEN_EFFECT) { 756 return new Prebaked(in); 757 } else { 758 throw new IllegalStateException( 759 "Unexpected vibration event type token in parcel."); 760 } 761 } 762 @Override 763 public VibrationEffect[] newArray(int size) { 764 return new VibrationEffect[size]; 765 } 766 }; 767} 768