Home | History | Annotate | Download | only in os
      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 
     17 package android.os;
     18 
     19 import android.annotation.Nullable;
     20 import android.content.ContentResolver;
     21 import android.content.Context;
     22 import android.hardware.vibrator.V1_0.EffectStrength;
     23 import android.hardware.vibrator.V1_2.Effect;
     24 import android.net.Uri;
     25 import android.util.MathUtils;
     26 
     27 import com.android.internal.annotations.VisibleForTesting;
     28 
     29 import java.util.Arrays;
     30 
     31 /**
     32  * A VibrationEffect describes a haptic effect to be performed by a {@link Vibrator}.
     33  *
     34  * These effects may be any number of things, from single shot vibrations to complex waveforms.
     35  */
     36 public abstract class VibrationEffect implements Parcelable {
     37     private static final int PARCEL_TOKEN_ONE_SHOT = 1;
     38     private static final int PARCEL_TOKEN_WAVEFORM = 2;
     39     private static final int PARCEL_TOKEN_EFFECT = 3;
     40 
     41     /**
     42      * The default vibration strength of the device.
     43      */
     44     public static final int DEFAULT_AMPLITUDE = -1;
     45 
     46     /**
     47      * The maximum amplitude value
     48      * @hide
     49      */
     50     public static final int MAX_AMPLITUDE = 255;
     51 
     52     /**
     53      * A click effect.
     54      *
     55      * @see #get(int)
     56      * @hide
     57      */
     58     public static final int EFFECT_CLICK = Effect.CLICK;
     59 
     60     /**
     61      * A double click effect.
     62      *
     63      * @see #get(int)
     64      * @hide
     65      */
     66     public static final int EFFECT_DOUBLE_CLICK = Effect.DOUBLE_CLICK;
     67 
     68     /**
     69      * A tick effect.
     70      * @see #get(int)
     71      * @hide
     72      */
     73     public static final int EFFECT_TICK = Effect.TICK;
     74 
     75     /**
     76      * A thud effect.
     77      * @see #get(int)
     78      * @hide
     79      */
     80     public static final int EFFECT_THUD = Effect.THUD;
     81 
     82     /**
     83      * A pop effect.
     84      * @see #get(int)
     85      * @hide
     86      */
     87     public static final int EFFECT_POP = Effect.POP;
     88 
     89     /**
     90      * A heavy click effect.
     91      * @see #get(int)
     92      * @hide
     93      */
     94     public static final int EFFECT_HEAVY_CLICK = Effect.HEAVY_CLICK;
     95 
     96 
     97     /**
     98      * Ringtone patterns. They may correspond with the device's ringtone audio, or may just be a
     99      * pattern that can be played as a ringtone with any audio, depending on the device.
    100      *
    101      * @see #get(Uri, Context)
    102      * @hide
    103      */
    104     @VisibleForTesting
    105     public static final int[] RINGTONES = {
    106         Effect.RINGTONE_1,
    107         Effect.RINGTONE_2,
    108         Effect.RINGTONE_3,
    109         Effect.RINGTONE_4,
    110         Effect.RINGTONE_5,
    111         Effect.RINGTONE_6,
    112         Effect.RINGTONE_7,
    113         Effect.RINGTONE_8,
    114         Effect.RINGTONE_9,
    115         Effect.RINGTONE_10,
    116         Effect.RINGTONE_11,
    117         Effect.RINGTONE_12,
    118         Effect.RINGTONE_13,
    119         Effect.RINGTONE_14,
    120         Effect.RINGTONE_15
    121     };
    122 
    123     /** @hide to prevent subclassing from outside of the framework */
    124     public VibrationEffect() { }
    125 
    126     /**
    127      * Create a one shot vibration.
    128      *
    129      * One shot vibrations will vibrate constantly for the specified period of time at the
    130      * specified amplitude, and then stop.
    131      *
    132      * @param milliseconds The number of milliseconds to vibrate. This must be a positive number.
    133      * @param amplitude The strength of the vibration. This must be a value between 1 and 255, or
    134      * {@link #DEFAULT_AMPLITUDE}.
    135      *
    136      * @return The desired effect.
    137      */
    138     public static VibrationEffect createOneShot(long milliseconds, int amplitude) {
    139         VibrationEffect effect = new OneShot(milliseconds, amplitude);
    140         effect.validate();
    141         return effect;
    142     }
    143 
    144     /**
    145      * Create a waveform vibration.
    146      *
    147      * Waveform vibrations are a potentially repeating series of timing and amplitude pairs. For
    148      * each pair, the value in the amplitude array determines the strength of the vibration and the
    149      * value in the timing array determines how long it vibrates for. An amplitude of 0 implies no
    150      * vibration (i.e. off), and any pairs with a timing value of 0 will be ignored.
    151      * <p>
    152      * The amplitude array of the generated waveform will be the same size as the given
    153      * timing array with alternating values of 0 (i.e. off) and {@link #DEFAULT_AMPLITUDE},
    154      * starting with 0. Therefore the first timing value will be the period to wait before turning
    155      * the vibrator on, the second value will be how long to vibrate at {@link #DEFAULT_AMPLITUDE}
    156      * strength, etc.
    157      * </p><p>
    158      * To cause the pattern to repeat, pass the index into the timings array at which to start the
    159      * repetition, or -1 to disable repeating.
    160      * </p>
    161      *
    162      * @param timings The pattern of alternating on-off timings, starting with off. Timing values
    163      *                of 0 will cause the timing / amplitude pair to be ignored.
    164      * @param repeat The index into the timings array at which to repeat, or -1 if you you don't
    165      *               want to repeat.
    166      *
    167      * @return The desired effect.
    168      */
    169     public static VibrationEffect createWaveform(long[] timings, int repeat) {
    170         int[] amplitudes = new int[timings.length];
    171         for (int i = 0; i < (timings.length / 2); i++) {
    172             amplitudes[i*2 + 1] = VibrationEffect.DEFAULT_AMPLITUDE;
    173         }
    174         return createWaveform(timings, amplitudes, repeat);
    175     }
    176 
    177     /**
    178      * Create a waveform vibration.
    179      *
    180      * Waveform vibrations are a potentially repeating series of timing and amplitude pairs. For
    181      * each pair, the value in the amplitude array determines the strength of the vibration and the
    182      * value in the timing array determines how long it vibrates for. An amplitude of 0 implies no
    183      * vibration (i.e. off), and any pairs with a timing value of 0 will be ignored.
    184      * </p><p>
    185      * To cause the pattern to repeat, pass the index into the timings array at which to start the
    186      * repetition, or -1 to disable repeating.
    187      * </p>
    188      *
    189      * @param timings The timing values of the timing / amplitude pairs. Timing values of 0
    190      *                will cause the pair to be ignored.
    191      * @param amplitudes The amplitude values of the timing / amplitude pairs. Amplitude values
    192      *                   must be between 0 and 255, or equal to {@link #DEFAULT_AMPLITUDE}. An
    193      *                   amplitude value of 0 implies the motor is off.
    194      * @param repeat The index into the timings array at which to repeat, or -1 if you you don't
    195      *               want to repeat.
    196      *
    197      * @return The desired effect.
    198      */
    199     public static VibrationEffect createWaveform(long[] timings, int[] amplitudes, int repeat) {
    200         VibrationEffect effect = new Waveform(timings, amplitudes, repeat);
    201         effect.validate();
    202         return effect;
    203     }
    204 
    205     /**
    206      * Get a predefined vibration effect.
    207      *
    208      * Predefined effects are a set of common vibration effects that should be identical, regardless
    209      * of the app they come from, in order to provide a cohesive experience for users across
    210      * the entire device. They also may be custom tailored to the device hardware in order to
    211      * provide a better experience than you could otherwise build using the generic building
    212      * blocks.
    213      *
    214      * This will fallback to a generic pattern if one exists and there does not exist a
    215      * hardware-specific implementation of the effect.
    216      *
    217      * @param effectId The ID of the effect to perform:
    218      *                 {@link #EFFECT_CLICK}, {@link #EFFECT_DOUBLE_CLICK}, {@link #EFFECT_TICK}
    219      *
    220      * @return The desired effect.
    221      * @hide
    222      */
    223     public static VibrationEffect get(int effectId) {
    224         return get(effectId, true);
    225     }
    226 
    227     /**
    228      * Get a predefined vibration effect.
    229      *
    230      * Predefined effects are a set of common vibration effects that should be identical, regardless
    231      * of the app they come from, in order to provide a cohesive experience for users across
    232      * the entire device. They also may be custom tailored to the device hardware in order to
    233      * provide a better experience than you could otherwise build using the generic building
    234      * blocks.
    235      *
    236      * Some effects you may only want to play if there's a hardware specific implementation because
    237      * they may, for example, be too disruptive to the user without tuning. The {@code fallback}
    238      * parameter allows you to decide whether you want to fallback to the generic implementation or
    239      * only play if there's a tuned, hardware specific one available.
    240      *
    241      * @param effectId The ID of the effect to perform:
    242      *                 {@link #EFFECT_CLICK}, {@link #EFFECT_DOUBLE_CLICK}, {@link #EFFECT_TICK}
    243      * @param fallback Whether to fallback to a generic pattern if a hardware specific
    244      *                 implementation doesn't exist.
    245      *
    246      * @return The desired effect.
    247      * @hide
    248      */
    249     public static VibrationEffect get(int effectId, boolean fallback) {
    250         VibrationEffect effect = new Prebaked(effectId, fallback);
    251         effect.validate();
    252         return effect;
    253     }
    254 
    255     /**
    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      */
    271     @Nullable
    272     public static VibrationEffect get(Uri uri, Context context) {
    273         String[] uris = context.getResources().getStringArray(
    274                 com.android.internal.R.array.config_ringtoneEffectUris);
    275         for (int i = 0; i < uris.length && i < RINGTONES.length; i++) {
    276             if (uris[i] == null) {
    277                 continue;
    278             }
    279             ContentResolver cr = context.getContentResolver();
    280             Uri mappedUri = cr.uncanonicalize(Uri.parse(uris[i]));
    281             if (mappedUri == null) {
    282                 continue;
    283             }
    284             if (mappedUri.equals(uri)) {
    285                 return get(RINGTONES[i]);
    286             }
    287         }
    288         return null;
    289     }
    290 
    291     @Override
    292     public int describeContents() {
    293         return 0;
    294     }
    295 
    296     /** @hide */
    297     public abstract void validate();
    298 
    299     /**
    300      * Gets the estimated duration of the vibration in milliseconds.
    301      *
    302      * For effects without a defined end (e.g. a Waveform with a non-negative repeat index), this
    303      * returns Long.MAX_VALUE. For effects with an unknown duration (e.g. Prebaked effects where
    304      * the length is device and potentially run-time dependent), this returns -1.
    305      *
    306      * @hide
    307      */
    308     public abstract long getDuration();
    309 
    310     /**
    311      * Scale the amplitude with the given constraints.
    312      *
    313      * This assumes that the previous value was in the range [0, MAX_AMPLITUDE]
    314      * @hide
    315      */
    316     protected static int scale(int amplitude, float gamma, int maxAmplitude) {
    317         float val = MathUtils.pow(amplitude / (float) MAX_AMPLITUDE, gamma);
    318         return (int) (val * maxAmplitude);
    319     }
    320 
    321     /** @hide */
    322     public static class OneShot extends VibrationEffect implements Parcelable {
    323         private final long mDuration;
    324         private final int mAmplitude;
    325 
    326         public OneShot(Parcel in) {
    327             mDuration = in.readLong();
    328             mAmplitude = in.readInt();
    329         }
    330 
    331         public OneShot(long milliseconds, int amplitude) {
    332             mDuration = milliseconds;
    333             mAmplitude = amplitude;
    334         }
    335 
    336         @Override
    337         public long getDuration() {
    338             return mDuration;
    339         }
    340 
    341         public int getAmplitude() {
    342             return mAmplitude;
    343         }
    344 
    345         /**
    346          * Scale the amplitude of this effect.
    347          *
    348          * @param gamma the gamma adjustment to apply
    349          * @param maxAmplitude the new maximum amplitude of the effect
    350          *
    351          * @return A {@link OneShot} effect with the same timing but scaled amplitude.
    352          */
    353         public VibrationEffect scale(float gamma, int maxAmplitude) {
    354             int newAmplitude = scale(mAmplitude, gamma, maxAmplitude);
    355             return new OneShot(mDuration, newAmplitude);
    356         }
    357 
    358         /**
    359          * Resolve default values into integer amplitude numbers.
    360          *
    361          * @param defaultAmplitude the default amplitude to apply, must be between 0 and
    362          *         MAX_AMPLITUDE
    363          * @return A {@link OneShot} effect with same physical meaning but explicitly set amplitude
    364          *
    365          * @hide
    366          */
    367         public OneShot resolve(int defaultAmplitude) {
    368             if (defaultAmplitude > MAX_AMPLITUDE || defaultAmplitude < 0) {
    369                 throw new IllegalArgumentException(
    370                         "Amplitude is negative or greater than MAX_AMPLITUDE");
    371             }
    372             if (mAmplitude == DEFAULT_AMPLITUDE) {
    373                 return new OneShot(mDuration, defaultAmplitude);
    374             }
    375             return this;
    376         }
    377 
    378         @Override
    379         public void validate() {
    380             if (mAmplitude < -1 || mAmplitude == 0 || mAmplitude > 255) {
    381                 throw new IllegalArgumentException(
    382                         "amplitude must either be DEFAULT_AMPLITUDE, "
    383                         + "or between 1 and 255 inclusive (amplitude=" + mAmplitude + ")");
    384             }
    385             if (mDuration <= 0) {
    386                 throw new IllegalArgumentException(
    387                         "duration must be positive (duration=" + mDuration + ")");
    388             }
    389         }
    390 
    391         @Override
    392         public boolean equals(Object o) {
    393             if (!(o instanceof VibrationEffect.OneShot)) {
    394                 return false;
    395             }
    396             VibrationEffect.OneShot other = (VibrationEffect.OneShot) o;
    397             return other.mDuration == mDuration && other.mAmplitude == mAmplitude;
    398         }
    399 
    400         @Override
    401         public int hashCode() {
    402             int result = 17;
    403             result += 37 * (int) mDuration;
    404             result += 37 * mAmplitude;
    405             return result;
    406         }
    407 
    408         @Override
    409         public String toString() {
    410             return "OneShot{mDuration=" + mDuration + ", mAmplitude=" + mAmplitude + "}";
    411         }
    412 
    413         @Override
    414         public void writeToParcel(Parcel out, int flags) {
    415             out.writeInt(PARCEL_TOKEN_ONE_SHOT);
    416             out.writeLong(mDuration);
    417             out.writeInt(mAmplitude);
    418         }
    419 
    420         public static final Parcelable.Creator<OneShot> CREATOR =
    421             new Parcelable.Creator<OneShot>() {
    422                 @Override
    423                 public OneShot createFromParcel(Parcel in) {
    424                     // Skip the type token
    425                     in.readInt();
    426                     return new OneShot(in);
    427                 }
    428                 @Override
    429                 public OneShot[] newArray(int size) {
    430                     return new OneShot[size];
    431                 }
    432             };
    433     }
    434 
    435     /** @hide */
    436     public static class Waveform extends VibrationEffect implements Parcelable {
    437         private final long[] mTimings;
    438         private final int[] mAmplitudes;
    439         private final int mRepeat;
    440 
    441         public Waveform(Parcel in) {
    442             this(in.createLongArray(), in.createIntArray(), in.readInt());
    443         }
    444 
    445         public Waveform(long[] timings, int[] amplitudes, int repeat) {
    446             mTimings = new long[timings.length];
    447             System.arraycopy(timings, 0, mTimings, 0, timings.length);
    448             mAmplitudes = new int[amplitudes.length];
    449             System.arraycopy(amplitudes, 0, mAmplitudes, 0, amplitudes.length);
    450             mRepeat = repeat;
    451         }
    452 
    453         public long[] getTimings() {
    454             return mTimings;
    455         }
    456 
    457         public int[] getAmplitudes() {
    458             return mAmplitudes;
    459         }
    460 
    461         public int getRepeatIndex() {
    462             return mRepeat;
    463         }
    464 
    465         @Override
    466         public long getDuration() {
    467             if (mRepeat >= 0) {
    468                 return Long.MAX_VALUE;
    469             }
    470             long duration = 0;
    471             for (long d : mTimings) {
    472                 duration += d;
    473             }
    474             return duration;
    475         }
    476 
    477         /**
    478          * Scale the Waveform with the given gamma and new max amplitude.
    479          *
    480          * @param gamma the gamma adjustment to apply
    481          * @param maxAmplitude the new maximum amplitude of the effect
    482          *
    483          * @return A {@link Waveform} effect with the same timings and repeat index
    484          *         but scaled amplitude.
    485          */
    486         public VibrationEffect scale(float gamma, int maxAmplitude) {
    487             if (gamma == 1.0f && maxAmplitude == MAX_AMPLITUDE) {
    488                 // Just return a copy of the original if there's no scaling to be done.
    489                 return new Waveform(mTimings, mAmplitudes, mRepeat);
    490             }
    491 
    492             int[] scaledAmplitudes = Arrays.copyOf(mAmplitudes, mAmplitudes.length);
    493             for (int i = 0; i < scaledAmplitudes.length; i++) {
    494                 scaledAmplitudes[i] = scale(scaledAmplitudes[i], gamma, maxAmplitude);
    495             }
    496             return new Waveform(mTimings, scaledAmplitudes, mRepeat);
    497         }
    498 
    499         /**
    500          * Resolve default values into integer amplitude numbers.
    501          *
    502          * @param defaultAmplitude the default amplitude to apply, must be between 0 and
    503          *         MAX_AMPLITUDE
    504          * @return A {@link Waveform} effect with same physical meaning but explicitly set
    505          *         amplitude
    506          *
    507          * @hide
    508          */
    509         public Waveform resolve(int defaultAmplitude) {
    510             if (defaultAmplitude > MAX_AMPLITUDE || defaultAmplitude < 0) {
    511                 throw new IllegalArgumentException(
    512                         "Amplitude is negative or greater than MAX_AMPLITUDE");
    513             }
    514             int[] resolvedAmplitudes = Arrays.copyOf(mAmplitudes, mAmplitudes.length);
    515             for (int i = 0; i < resolvedAmplitudes.length; i++) {
    516                 if (resolvedAmplitudes[i] == DEFAULT_AMPLITUDE) {
    517                     resolvedAmplitudes[i] = defaultAmplitude;
    518                 }
    519             }
    520             return new Waveform(mTimings, resolvedAmplitudes, mRepeat);
    521         }
    522 
    523         @Override
    524         public void validate() {
    525             if (mTimings.length != mAmplitudes.length) {
    526                 throw new IllegalArgumentException(
    527                         "timing and amplitude arrays must be of equal length"
    528                         + " (timings.length=" + mTimings.length
    529                         + ", amplitudes.length=" + mAmplitudes.length + ")");
    530             }
    531             if (!hasNonZeroEntry(mTimings)) {
    532                 throw new IllegalArgumentException("at least one timing must be non-zero"
    533                         + " (timings=" + Arrays.toString(mTimings) + ")");
    534             }
    535             for (long timing : mTimings) {
    536                 if (timing < 0) {
    537                     throw new IllegalArgumentException("timings must all be >= 0"
    538                             + " (timings=" + Arrays.toString(mTimings) + ")");
    539                 }
    540             }
    541             for (int amplitude : mAmplitudes) {
    542                 if (amplitude < -1 || amplitude > 255) {
    543                     throw new IllegalArgumentException(
    544                             "amplitudes must all be DEFAULT_AMPLITUDE or between 0 and 255"
    545                             + " (amplitudes=" + Arrays.toString(mAmplitudes) + ")");
    546                 }
    547             }
    548             if (mRepeat < -1 || mRepeat >= mTimings.length) {
    549                 throw new IllegalArgumentException(
    550                         "repeat index must be within the bounds of the timings array"
    551                         + " (timings.length=" + mTimings.length + ", index=" + mRepeat + ")");
    552             }
    553         }
    554 
    555         @Override
    556         public boolean equals(Object o) {
    557             if (!(o instanceof VibrationEffect.Waveform)) {
    558                 return false;
    559             }
    560             VibrationEffect.Waveform other = (VibrationEffect.Waveform) o;
    561             return Arrays.equals(mTimings, other.mTimings)
    562                 && Arrays.equals(mAmplitudes, other.mAmplitudes)
    563                 && mRepeat == other.mRepeat;
    564         }
    565 
    566         @Override
    567         public int hashCode() {
    568             int result = 17;
    569             result += 37 * Arrays.hashCode(mTimings);
    570             result += 37 * Arrays.hashCode(mAmplitudes);
    571             result += 37 * mRepeat;
    572             return result;
    573         }
    574 
    575         @Override
    576         public String toString() {
    577             return "Waveform{mTimings=" + Arrays.toString(mTimings)
    578                 + ", mAmplitudes=" + Arrays.toString(mAmplitudes)
    579                 + ", mRepeat=" + mRepeat
    580                 + "}";
    581         }
    582 
    583         @Override
    584         public void writeToParcel(Parcel out, int flags) {
    585             out.writeInt(PARCEL_TOKEN_WAVEFORM);
    586             out.writeLongArray(mTimings);
    587             out.writeIntArray(mAmplitudes);
    588             out.writeInt(mRepeat);
    589         }
    590 
    591         private static boolean hasNonZeroEntry(long[] vals) {
    592             for (long val : vals) {
    593                 if (val != 0) {
    594                     return true;
    595                 }
    596             }
    597             return false;
    598         }
    599 
    600 
    601         public static final Parcelable.Creator<Waveform> CREATOR =
    602             new Parcelable.Creator<Waveform>() {
    603                 @Override
    604                 public Waveform createFromParcel(Parcel in) {
    605                     // Skip the type token
    606                     in.readInt();
    607                     return new Waveform(in);
    608                 }
    609                 @Override
    610                 public Waveform[] newArray(int size) {
    611                     return new Waveform[size];
    612                 }
    613             };
    614     }
    615 
    616     /** @hide */
    617     public static class Prebaked extends VibrationEffect implements Parcelable {
    618         private final int mEffectId;
    619         private final boolean mFallback;
    620 
    621         private int mEffectStrength;
    622 
    623         public Prebaked(Parcel in) {
    624             this(in.readInt(), in.readByte() != 0);
    625             mEffectStrength = in.readInt();
    626         }
    627 
    628         public Prebaked(int effectId, boolean fallback) {
    629             mEffectId = effectId;
    630             mFallback = fallback;
    631             mEffectStrength = EffectStrength.MEDIUM;
    632         }
    633 
    634         public int getId() {
    635             return mEffectId;
    636         }
    637 
    638         /**
    639          * Whether the effect should fall back to a generic pattern if there's no hardware specific
    640          * implementation of it.
    641          */
    642         public boolean shouldFallback() {
    643             return mFallback;
    644         }
    645 
    646         @Override
    647         public long getDuration() {
    648             return -1;
    649         }
    650 
    651         /**
    652          * Set the effect strength of the prebaked effect.
    653          */
    654         public void setEffectStrength(int strength) {
    655             if (!isValidEffectStrength(strength)) {
    656                 throw new IllegalArgumentException("Invalid effect strength: " + strength);
    657             }
    658             mEffectStrength = strength;
    659         }
    660 
    661         /**
    662          * Set the effect strength.
    663          */
    664         public int getEffectStrength() {
    665             return mEffectStrength;
    666         }
    667 
    668         private static boolean isValidEffectStrength(int strength) {
    669             switch (strength) {
    670                 case EffectStrength.LIGHT:
    671                 case EffectStrength.MEDIUM:
    672                 case EffectStrength.STRONG:
    673                     return true;
    674                 default:
    675                     return false;
    676             }
    677         }
    678 
    679         @Override
    680         public void validate() {
    681             switch (mEffectId) {
    682                 case EFFECT_CLICK:
    683                 case EFFECT_DOUBLE_CLICK:
    684                 case EFFECT_TICK:
    685                 case EFFECT_THUD:
    686                 case EFFECT_POP:
    687                 case EFFECT_HEAVY_CLICK:
    688                     break;
    689                 default:
    690                     if (mEffectId < RINGTONES[0] || mEffectId > RINGTONES[RINGTONES.length - 1]) {
    691                         throw new IllegalArgumentException(
    692                                 "Unknown prebaked effect type (value=" + mEffectId + ")");
    693                     }
    694             }
    695             if (!isValidEffectStrength(mEffectStrength)) {
    696                 throw new IllegalArgumentException(
    697                         "Unknown prebaked effect strength (value=" + mEffectStrength + ")");
    698             }
    699         }
    700 
    701         @Override
    702         public boolean equals(Object o) {
    703             if (!(o instanceof VibrationEffect.Prebaked)) {
    704                 return false;
    705             }
    706             VibrationEffect.Prebaked other = (VibrationEffect.Prebaked) o;
    707             return mEffectId == other.mEffectId
    708                 && mFallback == other.mFallback
    709                 && mEffectStrength == other.mEffectStrength;
    710         }
    711 
    712         @Override
    713         public int hashCode() {
    714             int result = 17;
    715             result += 37 * mEffectId;
    716             result += 37 * mEffectStrength;
    717             return result;
    718         }
    719 
    720         @Override
    721         public String toString() {
    722             return "Prebaked{mEffectId=" + mEffectId
    723                 + ", mEffectStrength=" + mEffectStrength
    724                 + ", mFallback=" + mFallback
    725                 + "}";
    726         }
    727 
    728 
    729         @Override
    730         public void writeToParcel(Parcel out, int flags) {
    731             out.writeInt(PARCEL_TOKEN_EFFECT);
    732             out.writeInt(mEffectId);
    733             out.writeByte((byte) (mFallback ? 1 : 0));
    734             out.writeInt(mEffectStrength);
    735         }
    736 
    737         public static final Parcelable.Creator<Prebaked> CREATOR =
    738             new Parcelable.Creator<Prebaked>() {
    739                 @Override
    740                 public Prebaked createFromParcel(Parcel in) {
    741                     // Skip the type token
    742                     in.readInt();
    743                     return new Prebaked(in);
    744                 }
    745                 @Override
    746                 public Prebaked[] newArray(int size) {
    747                     return new Prebaked[size];
    748                 }
    749             };
    750     }
    751 
    752     public static final Parcelable.Creator<VibrationEffect> CREATOR =
    753             new Parcelable.Creator<VibrationEffect>() {
    754                 @Override
    755                 public VibrationEffect createFromParcel(Parcel in) {
    756                     int token = in.readInt();
    757                     if (token == PARCEL_TOKEN_ONE_SHOT) {
    758                         return new OneShot(in);
    759                     } else if (token == PARCEL_TOKEN_WAVEFORM) {
    760                         return new Waveform(in);
    761                     } else if (token == PARCEL_TOKEN_EFFECT) {
    762                         return new Prebaked(in);
    763                     } else {
    764                         throw new IllegalStateException(
    765                                 "Unexpected vibration event type token in parcel.");
    766                     }
    767                 }
    768                 @Override
    769                 public VibrationEffect[] newArray(int size) {
    770                     return new VibrationEffect[size];
    771                 }
    772             };
    773 }
    774