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