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 import com.android.internal.util.Preconditions; 27 28 import java.util.Objects; 29 30 /** 31 * Representation of {@link VibrationEffectSegment} that holds a fixed vibration amplitude and 32 * frequency for a specified duration. 33 * 34 * <p>The amplitude is expressed by a float value in the range [0, 1], representing the relative 35 * output acceleration for the vibrator. The frequency is expressed in hertz by a positive finite 36 * float value. The special value zero is used here for an unspecified frequency, and will be 37 * automatically mapped to the device's default vibration frequency (usually the resonant 38 * frequency). 39 * 40 * @hide 41 */ 42 @TestApi 43 public final class StepSegment extends VibrationEffectSegment { 44 private final float mAmplitude; 45 private final float mFrequencyHz; 46 private final int mDuration; 47 StepSegment(@onNull Parcel in)48 StepSegment(@NonNull Parcel in) { 49 this(in.readFloat(), in.readFloat(), in.readInt()); 50 } 51 52 /** @hide */ StepSegment(float amplitude, float frequencyHz, int duration)53 public StepSegment(float amplitude, float frequencyHz, int duration) { 54 mAmplitude = amplitude; 55 mFrequencyHz = frequencyHz; 56 mDuration = duration; 57 } 58 59 @Override equals(Object o)60 public boolean equals(Object o) { 61 if (!(o instanceof StepSegment)) { 62 return false; 63 } 64 StepSegment other = (StepSegment) o; 65 return Float.compare(mAmplitude, other.mAmplitude) == 0 66 && Float.compare(mFrequencyHz, other.mFrequencyHz) == 0 67 && mDuration == other.mDuration; 68 } 69 getAmplitude()70 public float getAmplitude() { 71 return mAmplitude; 72 } 73 getFrequencyHz()74 public float getFrequencyHz() { 75 return mFrequencyHz; 76 } 77 78 @Override getDuration()79 public long getDuration() { 80 return mDuration; 81 } 82 83 /** @hide */ 84 @Override areVibrationFeaturesSupported(@onNull VibratorInfo vibratorInfo)85 public boolean areVibrationFeaturesSupported(@NonNull VibratorInfo vibratorInfo) { 86 boolean areFeaturesSupported = true; 87 if (frequencyRequiresFrequencyControl(mFrequencyHz)) { 88 areFeaturesSupported &= vibratorInfo.hasFrequencyControl(); 89 } 90 if (amplitudeRequiresAmplitudeControl(mAmplitude)) { 91 areFeaturesSupported &= vibratorInfo.hasAmplitudeControl(); 92 } 93 return areFeaturesSupported; 94 } 95 96 /** @hide */ 97 @Override isHapticFeedbackCandidate()98 public boolean isHapticFeedbackCandidate() { 99 return true; 100 } 101 102 /** @hide */ 103 @Override validate()104 public void validate() { 105 VibrationEffectSegment.checkFrequencyArgument(mFrequencyHz, "frequencyHz"); 106 VibrationEffectSegment.checkDurationArgument(mDuration, "duration"); 107 if (Float.compare(mAmplitude, VibrationEffect.DEFAULT_AMPLITUDE) != 0) { 108 Preconditions.checkArgumentInRange(mAmplitude, 0f, 1f, "amplitude"); 109 VibrationEffectSegment.checkFrequencyArgument(mFrequencyHz, "frequencyHz"); 110 } else if (Float.compare(mFrequencyHz, 0) != 0) { 111 throw new IllegalArgumentException( 112 "frequency must be default when amplitude is set to default"); 113 } 114 } 115 116 /** @hide */ 117 @NonNull 118 @Override resolve(int defaultAmplitude)119 public StepSegment resolve(int defaultAmplitude) { 120 if (defaultAmplitude > VibrationEffect.MAX_AMPLITUDE || defaultAmplitude <= 0) { 121 throw new IllegalArgumentException( 122 "amplitude must be between 1 and 255 inclusive (amplitude=" 123 + defaultAmplitude + ")"); 124 } 125 if (Float.compare(mAmplitude, VibrationEffect.DEFAULT_AMPLITUDE) != 0) { 126 return this; 127 } 128 return new StepSegment((float) defaultAmplitude / VibrationEffect.MAX_AMPLITUDE, 129 mFrequencyHz, 130 mDuration); 131 } 132 133 /** @hide */ 134 @NonNull 135 @Override scale(float scaleFactor)136 public StepSegment scale(float scaleFactor) { 137 if (Float.compare(mAmplitude, VibrationEffect.DEFAULT_AMPLITUDE) == 0) { 138 return this; 139 } 140 float newAmplitude = VibrationEffect.scale(mAmplitude, scaleFactor); 141 if (Float.compare(newAmplitude, mAmplitude) == 0) { 142 return this; 143 } 144 return new StepSegment(newAmplitude, mFrequencyHz, mDuration); 145 } 146 147 /** @hide */ 148 @NonNull 149 @Override scaleLinearly(float scaleFactor)150 public StepSegment scaleLinearly(float scaleFactor) { 151 if (Float.compare(mAmplitude, VibrationEffect.DEFAULT_AMPLITUDE) == 0) { 152 return this; 153 } 154 float newAmplitude = VibrationEffect.scaleLinearly(mAmplitude, scaleFactor); 155 if (Float.compare(newAmplitude, mAmplitude) == 0) { 156 return this; 157 } 158 return new StepSegment(newAmplitude, mFrequencyHz, mDuration); 159 } 160 161 /** @hide */ 162 @NonNull 163 @Override applyEffectStrength(int effectStrength)164 public StepSegment applyEffectStrength(int effectStrength) { 165 return this; 166 } 167 168 @Override hashCode()169 public int hashCode() { 170 return Objects.hash(mAmplitude, mFrequencyHz, mDuration); 171 } 172 173 @Override toString()174 public String toString() { 175 return "Step{amplitude=" + mAmplitude 176 + ", frequencyHz=" + mFrequencyHz 177 + ", duration=" + mDuration 178 + "}"; 179 } 180 181 /** @hide */ 182 @Override toDebugString()183 public String toDebugString() { 184 return String.format("Step=%dms(amplitude=%.2f%s)", mDuration, mAmplitude, 185 Float.compare(mFrequencyHz, 0) == 0 ? "" : " @ " + mFrequencyHz + "Hz"); 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_STEP); 196 out.writeFloat(mAmplitude); 197 out.writeFloat(mFrequencyHz); 198 out.writeInt(mDuration); 199 } 200 201 @NonNull 202 public static final Parcelable.Creator<StepSegment> CREATOR = 203 new Parcelable.Creator<StepSegment>() { 204 @Override 205 public StepSegment createFromParcel(Parcel in) { 206 // Skip the type token 207 in.readInt(); 208 return new StepSegment(in); 209 } 210 211 @Override 212 public StepSegment[] newArray(int size) { 213 return new StepSegment[size]; 214 } 215 }; 216 } 217