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