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.Vibrator; 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 Vibrator vibrator)85 public boolean areVibrationFeaturesSupported(@NonNull Vibrator vibrator) { 86 boolean areFeaturesSupported = true; 87 if (frequencyRequiresFrequencyControl(mFrequencyHz)) { 88 areFeaturesSupported &= vibrator.hasFrequencyControl(); 89 } 90 if (amplitudeRequiresAmplitudeControl(mAmplitude)) { 91 areFeaturesSupported &= vibrator.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 hasNonZeroAmplitude()104 public boolean hasNonZeroAmplitude() { 105 // DEFAULT_AMPLITUDE == -1 is still a non-zero amplitude that will be resolved later. 106 return Float.compare(mAmplitude, 0) != 0; 107 } 108 109 /** @hide */ 110 @Override validate()111 public void validate() { 112 VibrationEffectSegment.checkFrequencyArgument(mFrequencyHz, "frequencyHz"); 113 VibrationEffectSegment.checkDurationArgument(mDuration, "duration"); 114 if (Float.compare(mAmplitude, VibrationEffect.DEFAULT_AMPLITUDE) != 0) { 115 Preconditions.checkArgumentInRange(mAmplitude, 0f, 1f, "amplitude"); 116 } 117 } 118 119 /** @hide */ 120 @NonNull 121 @Override resolve(int defaultAmplitude)122 public StepSegment resolve(int defaultAmplitude) { 123 if (defaultAmplitude > VibrationEffect.MAX_AMPLITUDE || defaultAmplitude <= 0) { 124 throw new IllegalArgumentException( 125 "amplitude must be between 1 and 255 inclusive (amplitude=" 126 + defaultAmplitude + ")"); 127 } 128 if (Float.compare(mAmplitude, VibrationEffect.DEFAULT_AMPLITUDE) != 0) { 129 return this; 130 } 131 return new StepSegment((float) defaultAmplitude / VibrationEffect.MAX_AMPLITUDE, 132 mFrequencyHz, 133 mDuration); 134 } 135 136 /** @hide */ 137 @NonNull 138 @Override scale(float scaleFactor)139 public StepSegment scale(float scaleFactor) { 140 if (Float.compare(mAmplitude, VibrationEffect.DEFAULT_AMPLITUDE) == 0) { 141 return this; 142 } 143 return new StepSegment(VibrationEffect.scale(mAmplitude, scaleFactor), mFrequencyHz, 144 mDuration); 145 } 146 147 /** @hide */ 148 @NonNull 149 @Override applyEffectStrength(int effectStrength)150 public StepSegment applyEffectStrength(int effectStrength) { 151 return this; 152 } 153 154 @Override hashCode()155 public int hashCode() { 156 return Objects.hash(mAmplitude, mFrequencyHz, mDuration); 157 } 158 159 @Override toString()160 public String toString() { 161 return "Step{amplitude=" + mAmplitude 162 + ", frequencyHz=" + mFrequencyHz 163 + ", duration=" + mDuration 164 + "}"; 165 } 166 167 @Override describeContents()168 public int describeContents() { 169 return 0; 170 } 171 172 @Override writeToParcel(@onNull Parcel out, int flags)173 public void writeToParcel(@NonNull Parcel out, int flags) { 174 out.writeInt(PARCEL_TOKEN_STEP); 175 out.writeFloat(mAmplitude); 176 out.writeFloat(mFrequencyHz); 177 out.writeInt(mDuration); 178 } 179 180 @NonNull 181 public static final Parcelable.Creator<StepSegment> CREATOR = 182 new Parcelable.Creator<StepSegment>() { 183 @Override 184 public StepSegment createFromParcel(Parcel in) { 185 // Skip the type token 186 in.readInt(); 187 return new StepSegment(in); 188 } 189 190 @Override 191 public StepSegment[] newArray(int size) { 192 return new StepSegment[size]; 193 } 194 }; 195 } 196