1 /*
2  * Copyright (C) 2021 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.vibrator;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.annotation.TestApi;
22 import android.os.Parcel;
23 import android.os.Parcelable;
24 import android.os.VibrationEffect;
25 import android.os.Vibrator;
26 
27 import java.util.Objects;
28 
29 /**
30  * Representation of {@link VibrationEffectSegment} that plays a prebaked vibration effect.
31  *
32  * @hide
33  */
34 @TestApi
35 public final class PrebakedSegment extends VibrationEffectSegment {
36     private final int mEffectId;
37     private final boolean mFallback;
38     private final int mEffectStrength;
39 
PrebakedSegment(@onNull Parcel in)40     PrebakedSegment(@NonNull Parcel in) {
41         mEffectId = in.readInt();
42         mFallback = in.readByte() != 0;
43         mEffectStrength = in.readInt();
44     }
45 
46     /** @hide */
PrebakedSegment(int effectId, boolean shouldFallback, int effectStrength)47     public PrebakedSegment(int effectId, boolean shouldFallback, int effectStrength) {
48         mEffectId = effectId;
49         mFallback = shouldFallback;
50         mEffectStrength = effectStrength;
51     }
52 
getEffectId()53     public int getEffectId() {
54         return mEffectId;
55     }
56 
getEffectStrength()57     public int getEffectStrength() {
58         return mEffectStrength;
59     }
60 
61     /** Return true if a fallback effect should be played if this effect is not supported. */
shouldFallback()62     public boolean shouldFallback() {
63         return mFallback;
64     }
65 
66     @Override
getDuration()67     public long getDuration() {
68         return -1;
69     }
70 
71     /** @hide */
72     @Override
areVibrationFeaturesSupported(@onNull Vibrator vibrator)73     public boolean areVibrationFeaturesSupported(@NonNull Vibrator vibrator) {
74         if (vibrator.areAllEffectsSupported(mEffectId) == Vibrator.VIBRATION_EFFECT_SUPPORT_YES) {
75             return true;
76         }
77         if (!mFallback) {
78             // If the Vibrator's support is not `VIBRATION_EFFECT_SUPPORT_YES`, and this effect does
79             // not support fallbacks, the effect is considered not supported by the vibrator.
80             return false;
81         }
82         // The vibrator does not have hardware support for the effect, but the effect has fallback
83         // support. Check if a fallback will be available for the effect ID.
84         switch (mEffectId) {
85             case VibrationEffect.EFFECT_CLICK:
86             case VibrationEffect.EFFECT_DOUBLE_CLICK:
87             case VibrationEffect.EFFECT_HEAVY_CLICK:
88             case VibrationEffect.EFFECT_TICK:
89                 // Any of these effects are always supported via some form of fallback.
90                 return true;
91             default:
92                 return false;
93         }
94     }
95 
96     /** @hide */
97     @Override
isHapticFeedbackCandidate()98     public boolean isHapticFeedbackCandidate() {
99         switch (mEffectId) {
100             case VibrationEffect.EFFECT_CLICK:
101             case VibrationEffect.EFFECT_DOUBLE_CLICK:
102             case VibrationEffect.EFFECT_HEAVY_CLICK:
103             case VibrationEffect.EFFECT_POP:
104             case VibrationEffect.EFFECT_TEXTURE_TICK:
105             case VibrationEffect.EFFECT_THUD:
106             case VibrationEffect.EFFECT_TICK:
107                 return true;
108             default:
109                 // VibrationEffect.RINGTONES are not segments that could represent a haptic feedback
110                 return false;
111         }
112     }
113 
114     /** @hide */
115     @Override
hasNonZeroAmplitude()116     public boolean hasNonZeroAmplitude() {
117         return true;
118     }
119 
120     /** @hide */
121     @NonNull
122     @Override
resolve(int defaultAmplitude)123     public PrebakedSegment resolve(int defaultAmplitude) {
124         return this;
125     }
126 
127     /** @hide */
128     @NonNull
129     @Override
scale(float scaleFactor)130     public PrebakedSegment scale(float scaleFactor) {
131         // Prebaked effect strength cannot be scaled with this method.
132         return this;
133     }
134 
135     /** @hide */
136     @NonNull
137     @Override
applyEffectStrength(int effectStrength)138     public PrebakedSegment applyEffectStrength(int effectStrength) {
139         if (effectStrength != mEffectStrength && isValidEffectStrength(effectStrength)) {
140             return new PrebakedSegment(mEffectId, mFallback, effectStrength);
141         }
142         return this;
143     }
144 
isValidEffectStrength(int strength)145     private static boolean isValidEffectStrength(int strength) {
146         switch (strength) {
147             case VibrationEffect.EFFECT_STRENGTH_LIGHT:
148             case VibrationEffect.EFFECT_STRENGTH_MEDIUM:
149             case VibrationEffect.EFFECT_STRENGTH_STRONG:
150                 return true;
151             default:
152                 return false;
153         }
154     }
155 
156     /** @hide */
157     @Override
validate()158     public void validate() {
159         switch (mEffectId) {
160             case VibrationEffect.EFFECT_CLICK:
161             case VibrationEffect.EFFECT_DOUBLE_CLICK:
162             case VibrationEffect.EFFECT_HEAVY_CLICK:
163             case VibrationEffect.EFFECT_POP:
164             case VibrationEffect.EFFECT_TEXTURE_TICK:
165             case VibrationEffect.EFFECT_THUD:
166             case VibrationEffect.EFFECT_TICK:
167                 break;
168             default:
169                 int[] ringtones = VibrationEffect.RINGTONES;
170                 if (mEffectId < ringtones[0] || mEffectId > ringtones[ringtones.length - 1]) {
171                     throw new IllegalArgumentException(
172                             "Unknown prebaked effect type (value=" + mEffectId + ")");
173                 }
174         }
175         if (!isValidEffectStrength(mEffectStrength)) {
176             throw new IllegalArgumentException(
177                     "Unknown prebaked effect strength (value=" + mEffectStrength + ")");
178         }
179     }
180 
181     @Override
equals(@ullable Object o)182     public boolean equals(@Nullable Object o) {
183         if (!(o instanceof PrebakedSegment)) {
184             return false;
185         }
186         PrebakedSegment other = (PrebakedSegment) o;
187         return mEffectId == other.mEffectId
188                 && mFallback == other.mFallback
189                 && mEffectStrength == other.mEffectStrength;
190     }
191 
192     @Override
hashCode()193     public int hashCode() {
194         return Objects.hash(mEffectId, mFallback, mEffectStrength);
195     }
196 
197     @Override
toString()198     public String toString() {
199         return "Prebaked{effect=" + VibrationEffect.effectIdToString(mEffectId)
200                 + ", strength=" + VibrationEffect.effectStrengthToString(mEffectStrength)
201                 + ", fallback=" + mFallback
202                 + "}";
203     }
204 
205     @Override
describeContents()206     public int describeContents() {
207         return 0;
208     }
209 
210     @Override
writeToParcel(@onNull Parcel out, int flags)211     public void writeToParcel(@NonNull Parcel out, int flags) {
212         out.writeInt(PARCEL_TOKEN_PREBAKED);
213         out.writeInt(mEffectId);
214         out.writeByte((byte) (mFallback ? 1 : 0));
215         out.writeInt(mEffectStrength);
216     }
217 
218     @NonNull
219     public static final Parcelable.Creator<PrebakedSegment> CREATOR =
220             new Parcelable.Creator<PrebakedSegment>() {
221                 @Override
222                 public PrebakedSegment createFromParcel(Parcel in) {
223                     // Skip the type token
224                     in.readInt();
225                     return new PrebakedSegment(in);
226                 }
227 
228                 @Override
229                 public PrebakedSegment[] newArray(int size) {
230                     return new PrebakedSegment[size];
231                 }
232             };
233 }
234