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.VibratorInfo; 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 VibratorInfo vibratorInfo)99 public boolean areVibrationFeaturesSupported(@NonNull VibratorInfo vibratorInfo) { 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 &= vibratorInfo.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 &= vibratorInfo.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 validate()128 public void validate() { 129 VibrationEffectSegment.checkFrequencyArgument(mStartFrequencyHz, "startFrequencyHz"); 130 VibrationEffectSegment.checkFrequencyArgument(mEndFrequencyHz, "endFrequencyHz"); 131 VibrationEffectSegment.checkDurationArgument(mDuration, "duration"); 132 Preconditions.checkArgumentInRange(mStartAmplitude, 0f, 1f, "startAmplitude"); 133 Preconditions.checkArgumentInRange(mEndAmplitude, 0f, 1f, "endAmplitude"); 134 } 135 136 /** @hide */ 137 @NonNull 138 @Override resolve(int defaultAmplitude)139 public RampSegment resolve(int defaultAmplitude) { 140 // Default amplitude is not supported for ramping. 141 return this; 142 } 143 144 /** @hide */ 145 @NonNull 146 @Override scale(float scaleFactor)147 public RampSegment scale(float scaleFactor) { 148 float newStartAmplitude = VibrationEffect.scale(mStartAmplitude, scaleFactor); 149 float newEndAmplitude = VibrationEffect.scale(mEndAmplitude, scaleFactor); 150 if (Float.compare(mStartAmplitude, newStartAmplitude) == 0 151 && Float.compare(mEndAmplitude, newEndAmplitude) == 0) { 152 return this; 153 } 154 return new RampSegment(newStartAmplitude, newEndAmplitude, mStartFrequencyHz, 155 mEndFrequencyHz, 156 mDuration); 157 } 158 159 /** @hide */ 160 @NonNull 161 @Override scaleLinearly(float scaleFactor)162 public RampSegment scaleLinearly(float scaleFactor) { 163 float newStartAmplitude = VibrationEffect.scaleLinearly(mStartAmplitude, scaleFactor); 164 float newEndAmplitude = VibrationEffect.scaleLinearly(mEndAmplitude, scaleFactor); 165 if (Float.compare(mStartAmplitude, newStartAmplitude) == 0 166 && Float.compare(mEndAmplitude, newEndAmplitude) == 0) { 167 return this; 168 } 169 return new RampSegment(newStartAmplitude, newEndAmplitude, mStartFrequencyHz, 170 mEndFrequencyHz, 171 mDuration); 172 } 173 174 /** @hide */ 175 @NonNull 176 @Override applyEffectStrength(int effectStrength)177 public RampSegment applyEffectStrength(int effectStrength) { 178 return this; 179 } 180 181 @Override hashCode()182 public int hashCode() { 183 return Objects.hash(mStartAmplitude, mEndAmplitude, mStartFrequencyHz, mEndFrequencyHz, 184 mDuration); 185 } 186 187 @Override toString()188 public String toString() { 189 return "Ramp{startAmplitude=" + mStartAmplitude 190 + ", endAmplitude=" + mEndAmplitude 191 + ", startFrequencyHz=" + mStartFrequencyHz 192 + ", endFrequencyHz=" + mEndFrequencyHz 193 + ", duration=" + mDuration 194 + "}"; 195 } 196 197 /** @hide */ 198 @Override toDebugString()199 public String toDebugString() { 200 return String.format("Ramp=%dms(amplitude=%.2f%s to %.2f%s)", 201 mDuration, 202 mStartAmplitude, 203 Float.compare(mStartFrequencyHz, 0) == 0 ? "" : " @ " + mStartFrequencyHz + "Hz", 204 mEndAmplitude, 205 Float.compare(mEndFrequencyHz, 0) == 0 ? "" : " @ " + mEndFrequencyHz + "Hz"); 206 } 207 208 @Override describeContents()209 public int describeContents() { 210 return 0; 211 } 212 213 @Override writeToParcel(@onNull Parcel out, int flags)214 public void writeToParcel(@NonNull Parcel out, int flags) { 215 out.writeInt(PARCEL_TOKEN_RAMP); 216 out.writeFloat(mStartAmplitude); 217 out.writeFloat(mEndAmplitude); 218 out.writeFloat(mStartFrequencyHz); 219 out.writeFloat(mEndFrequencyHz); 220 out.writeInt(mDuration); 221 } 222 223 @NonNull 224 public static final Creator<RampSegment> CREATOR = 225 new Creator<RampSegment>() { 226 @Override 227 public RampSegment createFromParcel(Parcel in) { 228 // Skip the type token 229 in.readInt(); 230 return new RampSegment(in); 231 } 232 233 @Override 234 public RampSegment[] newArray(int size) { 235 return new RampSegment[size]; 236 } 237 }; 238 } 239