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