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.VibrationEffect;
23 import android.os.Vibrator;
24 
25 import com.android.internal.util.Preconditions;
26 
27 import java.util.Objects;
28 
29 /**
30  * Representation of {@link VibrationEffectSegment} that ramps vibration amplitude and/or frequency
31  * for a specified duration.
32  *
33  * <p>The amplitudes are expressed by float values in the range [0, 1], representing the relative
34  * output acceleration for the vibrator. The frequencies are expressed in hertz by positive finite
35  * float values. The special value zero is used here for an unspecified frequency, and will be
36  * automatically mapped to the device's default vibration frequency (usually the resonant
37  * frequency).
38  *
39  * @hide
40  */
41 @TestApi
42 public final class RampSegment extends VibrationEffectSegment {
43     private final float mStartAmplitude;
44     private final float mStartFrequencyHz;
45     private final float mEndAmplitude;
46     private final float mEndFrequencyHz;
47     private final int mDuration;
48 
RampSegment(@onNull Parcel in)49     RampSegment(@NonNull Parcel in) {
50         this(in.readFloat(), in.readFloat(), in.readFloat(), in.readFloat(), in.readInt());
51     }
52 
53     /** @hide */
RampSegment(float startAmplitude, float endAmplitude, float startFrequencyHz, float endFrequencyHz, int duration)54     public RampSegment(float startAmplitude, float endAmplitude, float startFrequencyHz,
55             float endFrequencyHz, int duration) {
56         mStartAmplitude = startAmplitude;
57         mEndAmplitude = endAmplitude;
58         mStartFrequencyHz = startFrequencyHz;
59         mEndFrequencyHz = endFrequencyHz;
60         mDuration = duration;
61     }
62 
63     @Override
equals(Object o)64     public boolean equals(Object o) {
65         if (!(o instanceof RampSegment)) {
66             return false;
67         }
68         RampSegment other = (RampSegment) o;
69         return Float.compare(mStartAmplitude, other.mStartAmplitude) == 0
70                 && Float.compare(mEndAmplitude, other.mEndAmplitude) == 0
71                 && Float.compare(mStartFrequencyHz, other.mStartFrequencyHz) == 0
72                 && Float.compare(mEndFrequencyHz, other.mEndFrequencyHz) == 0
73                 && mDuration == other.mDuration;
74     }
75 
getStartAmplitude()76     public float getStartAmplitude() {
77         return mStartAmplitude;
78     }
79 
getEndAmplitude()80     public float getEndAmplitude() {
81         return mEndAmplitude;
82     }
83 
getStartFrequencyHz()84     public float getStartFrequencyHz() {
85         return mStartFrequencyHz;
86     }
87 
getEndFrequencyHz()88     public float getEndFrequencyHz() {
89         return mEndFrequencyHz;
90     }
91 
92     @Override
getDuration()93     public long getDuration() {
94         return mDuration;
95     }
96 
97     /** @hide */
98     @Override
areVibrationFeaturesSupported(@onNull Vibrator vibrator)99     public boolean areVibrationFeaturesSupported(@NonNull Vibrator vibrator) {
100         boolean areFeaturesSupported = true;
101         // If the start/end frequencies are not the same, require frequency control since we need to
102         // ramp up/down the frequency.
103         if ((mStartFrequencyHz != mEndFrequencyHz)
104                 // If there is no frequency ramping, make sure that the one frequency used does not
105                 // require frequency control.
106                 || frequencyRequiresFrequencyControl(mStartFrequencyHz)) {
107             areFeaturesSupported &= vibrator.hasFrequencyControl();
108         }
109         // If the start/end amplitudes are not the same, require amplitude control since we need to
110         // ramp up/down the amplitude.
111         if ((mStartAmplitude != mEndAmplitude)
112                 // If there is no amplitude ramping, make sure that the amplitude used does not
113                 // require amplitude control.
114                 || amplitudeRequiresAmplitudeControl(mStartAmplitude)) {
115             areFeaturesSupported &= vibrator.hasAmplitudeControl();
116         }
117         return areFeaturesSupported;
118     }
119 
120     /** @hide */
121     @Override
isHapticFeedbackCandidate()122     public boolean isHapticFeedbackCandidate() {
123         return true;
124     }
125 
126     /** @hide */
127     @Override
hasNonZeroAmplitude()128     public boolean hasNonZeroAmplitude() {
129         return mStartAmplitude > 0 || mEndAmplitude > 0;
130     }
131 
132     /** @hide */
133     @Override
validate()134     public void validate() {
135         VibrationEffectSegment.checkFrequencyArgument(mStartFrequencyHz, "startFrequencyHz");
136         VibrationEffectSegment.checkFrequencyArgument(mEndFrequencyHz, "endFrequencyHz");
137         VibrationEffectSegment.checkDurationArgument(mDuration, "duration");
138         Preconditions.checkArgumentInRange(mStartAmplitude, 0f, 1f, "startAmplitude");
139         Preconditions.checkArgumentInRange(mEndAmplitude, 0f, 1f, "endAmplitude");
140     }
141 
142     /** @hide */
143     @NonNull
144     @Override
resolve(int defaultAmplitude)145     public RampSegment resolve(int defaultAmplitude) {
146         // Default amplitude is not supported for ramping.
147         return this;
148     }
149 
150     /** @hide */
151     @NonNull
152     @Override
scale(float scaleFactor)153     public RampSegment scale(float scaleFactor) {
154         float newStartAmplitude = VibrationEffect.scale(mStartAmplitude, scaleFactor);
155         float newEndAmplitude = VibrationEffect.scale(mEndAmplitude, scaleFactor);
156         if (Float.compare(mStartAmplitude, newStartAmplitude) == 0
157                 && Float.compare(mEndAmplitude, newEndAmplitude) == 0) {
158             return this;
159         }
160         return new RampSegment(newStartAmplitude, newEndAmplitude, mStartFrequencyHz,
161                 mEndFrequencyHz,
162                 mDuration);
163     }
164 
165     /** @hide */
166     @NonNull
167     @Override
applyEffectStrength(int effectStrength)168     public RampSegment applyEffectStrength(int effectStrength) {
169         return this;
170     }
171 
172     @Override
hashCode()173     public int hashCode() {
174         return Objects.hash(mStartAmplitude, mEndAmplitude, mStartFrequencyHz, mEndFrequencyHz,
175                 mDuration);
176     }
177 
178     @Override
toString()179     public String toString() {
180         return "Ramp{startAmplitude=" + mStartAmplitude
181                 + ", endAmplitude=" + mEndAmplitude
182                 + ", startFrequencyHz=" + mStartFrequencyHz
183                 + ", endFrequencyHz=" + mEndFrequencyHz
184                 + ", duration=" + mDuration
185                 + "}";
186     }
187 
188     @Override
describeContents()189     public int describeContents() {
190         return 0;
191     }
192 
193     @Override
writeToParcel(@onNull Parcel out, int flags)194     public void writeToParcel(@NonNull Parcel out, int flags) {
195         out.writeInt(PARCEL_TOKEN_RAMP);
196         out.writeFloat(mStartAmplitude);
197         out.writeFloat(mEndAmplitude);
198         out.writeFloat(mStartFrequencyHz);
199         out.writeFloat(mEndFrequencyHz);
200         out.writeInt(mDuration);
201     }
202 
203     @NonNull
204     public static final Creator<RampSegment> CREATOR =
205             new Creator<RampSegment>() {
206                 @Override
207                 public RampSegment createFromParcel(Parcel in) {
208                     // Skip the type token
209                     in.readInt();
210                     return new RampSegment(in);
211                 }
212 
213                 @Override
214                 public RampSegment[] newArray(int size) {
215                     return new RampSegment[size];
216                 }
217             };
218 }
219