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.Vibrator; 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 Vibrator vibrator)68 public abstract boolean areVibrationFeaturesSupported(@NonNull Vibrator vibrator); 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 * Returns true if this segment plays at a non-zero amplitude at some point. 80 * 81 * @hide 82 */ hasNonZeroAmplitude()83 public abstract boolean hasNonZeroAmplitude(); 84 85 /** 86 * Validates the segment, throwing exceptions if any parameter is invalid. 87 * 88 * @hide 89 */ validate()90 public abstract void validate(); 91 92 /** 93 * Resolves amplitudes set to {@link VibrationEffect#DEFAULT_AMPLITUDE}. 94 * 95 * <p>This might fail with {@link IllegalArgumentException} if value is non-positive or larger 96 * than {@link VibrationEffect#MAX_AMPLITUDE}. 97 * 98 * @hide 99 */ 100 @NonNull resolve(int defaultAmplitude)101 public abstract <T extends VibrationEffectSegment> T resolve(int defaultAmplitude); 102 103 /** 104 * Scale the segment intensity with the given factor. 105 * 106 * @param scaleFactor scale factor to be applied to the intensity. Values within [0,1) will 107 * scale down the intensity, values larger than 1 will scale up 108 * 109 * @hide 110 */ 111 @NonNull scale(float scaleFactor)112 public abstract <T extends VibrationEffectSegment> T scale(float scaleFactor); 113 114 /** 115 * Applies given effect strength to prebaked effects. 116 * 117 * @param effectStrength new effect strength to be applied, one of 118 * VibrationEffect.EFFECT_STRENGTH_*. 119 * 120 * @hide 121 */ 122 @NonNull applyEffectStrength(int effectStrength)123 public abstract <T extends VibrationEffectSegment> T applyEffectStrength(int effectStrength); 124 125 /** 126 * Checks the given frequency argument is valid to represent a vibration effect frequency in 127 * hertz, i.e. a finite non-negative value. 128 * 129 * @param value the frequency argument value to be checked 130 * @param name the argument name for the error message. 131 * 132 * @hide 133 */ checkFrequencyArgument(float value, @NonNull String name)134 public static void checkFrequencyArgument(float value, @NonNull String name) { 135 // Similar to combining Preconditions checkArgumentFinite + checkArgumentNonnegative, 136 // but this implementation doesn't create the error message unless a check fail. 137 if (Float.isNaN(value)) { 138 throw new IllegalArgumentException(name + " must not be NaN"); 139 } 140 if (Float.isInfinite(value)) { 141 throw new IllegalArgumentException(name + " must not be infinite"); 142 } 143 if (value < 0) { 144 throw new IllegalArgumentException(name + " must be >= 0, got " + value); 145 } 146 } 147 148 /** 149 * Checks the given duration argument is valid, i.e. a non-negative value. 150 * 151 * @param value the duration value to be checked 152 * @param name the argument name for the error message. 153 * 154 * @hide 155 */ checkDurationArgument(long value, @NonNull String name)156 public static void checkDurationArgument(long value, @NonNull String name) { 157 if (value < 0) { 158 throw new IllegalArgumentException(name + " must be >= 0, got " + value); 159 } 160 } 161 162 /** 163 * Helper method to check if an amplitude requires a vibrator to have amplitude control to play. 164 * 165 * @hide 166 */ amplitudeRequiresAmplitudeControl(float amplitude)167 protected static boolean amplitudeRequiresAmplitudeControl(float amplitude) { 168 return (amplitude != 0) 169 && (amplitude != 1) 170 && (amplitude != VibrationEffect.DEFAULT_AMPLITUDE); 171 } 172 173 /** 174 * Helper method to check if a frequency requires a vibrator to have frequency control to play. 175 * 176 * @hide 177 */ frequencyRequiresFrequencyControl(float frequency)178 protected static boolean frequencyRequiresFrequencyControl(float frequency) { 179 // Anything other than the default frequency value (represented with "0") requires frequency 180 // control. 181 return frequency != 0; 182 } 183 184 @NonNull 185 public static final Creator<VibrationEffectSegment> CREATOR = 186 new Creator<VibrationEffectSegment>() { 187 @Override 188 public VibrationEffectSegment createFromParcel(Parcel in) { 189 switch (in.readInt()) { 190 case PARCEL_TOKEN_STEP: 191 return new StepSegment(in); 192 case PARCEL_TOKEN_RAMP: 193 return new RampSegment(in); 194 case PARCEL_TOKEN_PREBAKED: 195 return new PrebakedSegment(in); 196 case PARCEL_TOKEN_PRIMITIVE: 197 return new PrimitiveSegment(in); 198 default: 199 throw new IllegalStateException( 200 "Unexpected vibration event type token in parcel."); 201 } 202 } 203 204 @Override 205 public VibrationEffectSegment[] newArray(int size) { 206 return new VibrationEffectSegment[size]; 207 } 208 }; 209 } 210