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