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