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.VibratorInfo; 26 27 /** 28 * Representation of a single segment of a {@link VibrationEffect}. 29 * 30 * <p>Vibration effects are represented as a sequence of segments that describes how vibration 31 * amplitude and frequency changes over time. Segments can be described as one of the following: 32 * 33 * <ol> 34 * <li>A predefined vibration effect; 35 * <li>A composable effect primitive; 36 * <li>Fixed amplitude and frequency values to be held for a specified duration; 37 * <li>Pairs of amplitude and frequency values to be ramped to for a specified duration; 38 * </ol> 39 * 40 * @hide 41 */ 42 @TestApi 43 @SuppressWarnings({"ParcelNotFinal", "ParcelCreator"}) // Parcel only extended here. 44 public abstract class VibrationEffectSegment implements Parcelable { 45 static final int PARCEL_TOKEN_PREBAKED = 1; 46 static final int PARCEL_TOKEN_PRIMITIVE = 2; 47 static final int PARCEL_TOKEN_STEP = 3; 48 static final int PARCEL_TOKEN_RAMP = 4; 49 static final int PARCEL_TOKEN_PWLE = 5; 50 static final int PARCEL_TOKEN_BASIC_PWLE = 6; 51 52 /** Prevent subclassing from outside of this package */ VibrationEffectSegment()53 VibrationEffectSegment() { 54 } 55 56 /** 57 * Gets the estimated duration of the segment in milliseconds. 58 * 59 * <p>For segments with an unknown duration (e.g. prebaked or primitive effects where the length 60 * is device and potentially run-time dependent), this returns -1. 61 */ getDuration()62 public abstract long getDuration(); 63 64 /** 65 * Gets the estimated duration of the segment for given vibrator, in milliseconds. 66 * 67 * <p>For segments with hardware-dependent constants (e.g. primitives), this returns the 68 * estimated duration based on the given {@link VibratorInfo}. For all other effects this will 69 * return the same as {@link #getDuration()}. 70 * 71 * @hide 72 */ getDuration(@ullable VibratorInfo vibratorInfo)73 public long getDuration(@Nullable VibratorInfo vibratorInfo) { 74 return getDuration(); 75 } 76 77 /** 78 * Checks if a given {@link android.os.Vibrator} can play this segment as intended. See 79 * {@link android.os.Vibrator#areVibrationFeaturesSupported(VibrationEffect)} for more 80 * information about what counts as supported by a vibrator, and what counts as not. 81 * 82 * @hide 83 */ areVibrationFeaturesSupported(@onNull VibratorInfo vibratorInfo)84 public abstract boolean areVibrationFeaturesSupported(@NonNull VibratorInfo vibratorInfo); 85 86 /** 87 * Returns true if this segment could be a haptic feedback effect candidate. 88 * 89 * @see VibrationEffect#isHapticFeedbackCandidate() 90 * @hide 91 */ isHapticFeedbackCandidate()92 public abstract boolean isHapticFeedbackCandidate(); 93 94 /** 95 * Validates the segment, throwing exceptions if any parameter is invalid. 96 * 97 * @hide 98 */ validate()99 public abstract void validate(); 100 101 /** 102 * Resolves amplitudes set to {@link VibrationEffect#DEFAULT_AMPLITUDE}. 103 * 104 * <p>This might fail with {@link IllegalArgumentException} if value is non-positive or larger 105 * than {@link VibrationEffect#MAX_AMPLITUDE}. 106 * 107 * @hide 108 */ 109 @NonNull resolve(int defaultAmplitude)110 public abstract <T extends VibrationEffectSegment> T resolve(int defaultAmplitude); 111 112 /** 113 * Scale the segment intensity with the given factor. 114 * 115 * <p> This scale is not necessarily linear and may apply a gamma correction to the scale 116 * factor before using it. 117 * 118 * @param scaleFactor scale factor to be applied to the intensity. Values within [0,1) will 119 * scale down the intensity, values larger than 1 will scale up 120 * 121 * @hide 122 */ 123 @NonNull scale(float scaleFactor)124 public abstract <T extends VibrationEffectSegment> T scale(float scaleFactor); 125 126 /** 127 * Performs a linear scaling on the segment intensity with the given factor. 128 * 129 * @param scaleFactor scale factor to be applied to the intensity. Values within [0,1) will 130 * scale down the intensity, values larger than 1 will scale up 131 * 132 * @hide 133 */ 134 @NonNull scaleLinearly(float scaleFactor)135 public abstract <T extends VibrationEffectSegment> T scaleLinearly(float scaleFactor); 136 137 /** 138 * Applies given effect strength to prebaked effects. 139 * 140 * @param effectStrength new effect strength to be applied, one of 141 * VibrationEffect.EFFECT_STRENGTH_*. 142 * 143 * @hide 144 */ 145 @NonNull applyEffectStrength(int effectStrength)146 public abstract <T extends VibrationEffectSegment> T applyEffectStrength(int effectStrength); 147 148 /** 149 * Returns a compact version of the {@link #toString()} result for debugging purposes. 150 * 151 * @hide 152 */ toDebugString()153 public abstract String toDebugString(); 154 155 /** 156 * Checks the given frequency argument is valid to represent a vibration effect frequency in 157 * hertz, i.e. a finite non-negative value. 158 * 159 * @param value the frequency argument value to be checked 160 * @param name the argument name for the error message. 161 * 162 * @hide 163 */ checkFrequencyArgument(float value, @NonNull String name)164 public static void checkFrequencyArgument(float value, @NonNull String name) { 165 // Similar to combining Preconditions checkArgumentFinite + checkArgumentNonnegative, 166 // but this implementation doesn't create the error message unless a check fail. 167 if (Float.isNaN(value)) { 168 throw new IllegalArgumentException(name + " must not be NaN"); 169 } 170 if (Float.isInfinite(value)) { 171 throw new IllegalArgumentException(name + " must not be infinite"); 172 } 173 if (value < 0) { 174 throw new IllegalArgumentException(name + " must be >= 0, got " + value); 175 } 176 } 177 178 /** 179 * Checks the given duration argument is valid, i.e. a non-negative value. 180 * 181 * @param value the duration value to be checked 182 * @param name the argument name for the error message. 183 * 184 * @hide 185 */ checkDurationArgument(long value, @NonNull String name)186 public static void checkDurationArgument(long value, @NonNull String name) { 187 if (value < 0) { 188 throw new IllegalArgumentException(name + " must be >= 0, got " + value); 189 } 190 } 191 192 /** 193 * Helper method to check if an amplitude requires a vibrator to have amplitude control to play. 194 * 195 * @hide 196 */ amplitudeRequiresAmplitudeControl(float amplitude)197 protected static boolean amplitudeRequiresAmplitudeControl(float amplitude) { 198 return (amplitude != 0) 199 && (amplitude != 1) 200 && (amplitude != VibrationEffect.DEFAULT_AMPLITUDE); 201 } 202 203 /** 204 * Helper method to check if a frequency requires a vibrator to have frequency control to play. 205 * 206 * @hide 207 */ frequencyRequiresFrequencyControl(float frequency)208 protected static boolean frequencyRequiresFrequencyControl(float frequency) { 209 // Anything other than the default frequency value (represented with "0") requires frequency 210 // control. 211 return frequency != 0; 212 } 213 214 @NonNull 215 public static final Creator<VibrationEffectSegment> CREATOR = 216 new Creator<VibrationEffectSegment>() { 217 @Override 218 public VibrationEffectSegment createFromParcel(Parcel in) { 219 switch (in.readInt()) { 220 case PARCEL_TOKEN_STEP: 221 return new StepSegment(in); 222 case PARCEL_TOKEN_RAMP: 223 return new RampSegment(in); 224 case PARCEL_TOKEN_PREBAKED: 225 return new PrebakedSegment(in); 226 case PARCEL_TOKEN_PRIMITIVE: 227 return new PrimitiveSegment(in); 228 case PARCEL_TOKEN_PWLE: 229 if (Flags.normalizedPwleEffects()) { 230 return new PwleSegment(in); 231 } 232 // Fall through to default if the flag is not enabled. 233 case PARCEL_TOKEN_BASIC_PWLE: 234 if (Flags.normalizedPwleEffects()) { 235 return new BasicPwleSegment(in); 236 } 237 // Fall through to default if the flag is not enabled. 238 default: 239 throw new IllegalStateException( 240 "Unexpected vibration event type token in parcel."); 241 } 242 } 243 244 @Override 245 public VibrationEffectSegment[] newArray(int size) { 246 return new VibrationEffectSegment[size]; 247 } 248 }; 249 } 250