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.Nullable; 21 import android.annotation.TestApi; 22 import android.os.Parcel; 23 import android.os.Parcelable; 24 import android.os.VibrationEffect; 25 import android.os.VibratorInfo; 26 27 import com.android.internal.util.Preconditions; 28 29 import java.util.Locale; 30 import java.util.Objects; 31 32 /** 33 * Representation of {@link VibrationEffectSegment} that plays a primitive vibration effect after a 34 * specified delay and applying a given scale. 35 * 36 * @hide 37 */ 38 @TestApi 39 public final class PrimitiveSegment extends VibrationEffectSegment { 40 41 /** @hide */ 42 public static final float DEFAULT_SCALE = 1f; 43 44 /** @hide */ 45 public static final int DEFAULT_DELAY_MILLIS = 0; 46 47 /** @hide */ 48 public static final int DEFAULT_DELAY_TYPE = VibrationEffect.Composition.DELAY_TYPE_PAUSE; 49 50 private final int mPrimitiveId; 51 private final float mScale; 52 private final int mDelay; 53 private final int mDelayType; 54 PrimitiveSegment(@onNull Parcel in)55 PrimitiveSegment(@NonNull Parcel in) { 56 this(in.readInt(), in.readFloat(), in.readInt(), in.readInt()); 57 } 58 59 /** @hide */ PrimitiveSegment(int id, float scale, int delay)60 public PrimitiveSegment(int id, float scale, int delay) { 61 this(id, scale, delay, DEFAULT_DELAY_TYPE); 62 } 63 64 /** @hide */ PrimitiveSegment(int id, float scale, int delay, int delayType)65 public PrimitiveSegment(int id, float scale, int delay, int delayType) { 66 mPrimitiveId = id; 67 mScale = scale; 68 mDelay = delay; 69 mDelayType = delayType; 70 } 71 getPrimitiveId()72 public int getPrimitiveId() { 73 return mPrimitiveId; 74 } 75 getScale()76 public float getScale() { 77 return mScale; 78 } 79 getDelay()80 public int getDelay() { 81 return mDelay; 82 } 83 84 /** @hide */ getDelayType()85 public int getDelayType() { 86 return mDelayType; 87 } 88 89 @Override getDuration()90 public long getDuration() { 91 return -1; 92 } 93 94 /** @hide */ 95 @Override getDuration(@ullable VibratorInfo vibratorInfo)96 public long getDuration(@Nullable VibratorInfo vibratorInfo) { 97 if (vibratorInfo == null) { 98 return getDuration(); 99 } 100 int duration = vibratorInfo.getPrimitiveDuration(mPrimitiveId); 101 return duration > 0 ? duration + mDelay : getDuration(); 102 } 103 104 /** @hide */ 105 @Override areVibrationFeaturesSupported(@onNull VibratorInfo vibratorInfo)106 public boolean areVibrationFeaturesSupported(@NonNull VibratorInfo vibratorInfo) { 107 return vibratorInfo.isPrimitiveSupported(mPrimitiveId); 108 } 109 110 /** @hide */ 111 @Override isHapticFeedbackCandidate()112 public boolean isHapticFeedbackCandidate() { 113 return true; 114 } 115 116 /** @hide */ 117 @NonNull 118 @Override resolve(int defaultAmplitude)119 public PrimitiveSegment resolve(int defaultAmplitude) { 120 return this; 121 } 122 123 /** @hide */ 124 @NonNull 125 @Override scale(float scaleFactor)126 public PrimitiveSegment scale(float scaleFactor) { 127 float newScale = VibrationEffect.scale(mScale, scaleFactor); 128 if (Float.compare(mScale, newScale) == 0) { 129 return this; 130 } 131 return new PrimitiveSegment(mPrimitiveId, newScale, mDelay, mDelayType); 132 } 133 134 /** @hide */ 135 @NonNull 136 @Override scaleLinearly(float scaleFactor)137 public PrimitiveSegment scaleLinearly(float scaleFactor) { 138 float newScale = VibrationEffect.scaleLinearly(mScale, scaleFactor); 139 if (Float.compare(mScale, newScale) == 0) { 140 return this; 141 } 142 return new PrimitiveSegment(mPrimitiveId, newScale, mDelay, mDelayType); 143 } 144 145 /** @hide */ 146 @NonNull 147 @Override applyEffectStrength(int effectStrength)148 public PrimitiveSegment applyEffectStrength(int effectStrength) { 149 return this; 150 } 151 152 /** @hide */ 153 @Override validate()154 public void validate() { 155 Preconditions.checkArgumentInRange(mPrimitiveId, VibrationEffect.Composition.PRIMITIVE_NOOP, 156 VibrationEffect.Composition.PRIMITIVE_LOW_TICK, "primitiveId"); 157 Preconditions.checkArgumentInRange(mScale, 0f, 1f, "scale"); 158 VibrationEffectSegment.checkDurationArgument(mDelay, "delay"); 159 Preconditions.checkArgument(isValidDelayType(mDelayType), "delayType"); 160 } 161 162 @Override writeToParcel(@onNull Parcel dest, int flags)163 public void writeToParcel(@NonNull Parcel dest, int flags) { 164 dest.writeInt(PARCEL_TOKEN_PRIMITIVE); 165 dest.writeInt(mPrimitiveId); 166 dest.writeFloat(mScale); 167 dest.writeInt(mDelay); 168 dest.writeInt(mDelayType); 169 } 170 171 @Override describeContents()172 public int describeContents() { 173 return 0; 174 } 175 176 @Override toString()177 public String toString() { 178 return "Primitive{" 179 + "primitive=" + VibrationEffect.Composition.primitiveToString(mPrimitiveId) 180 + ", scale=" + mScale 181 + ", delay=" + mDelay 182 + ", delayType=" + VibrationEffect.Composition.delayTypeToString(mDelayType) 183 + '}'; 184 } 185 186 /** @hide */ 187 @Override toDebugString()188 public String toDebugString() { 189 return String.format(Locale.ROOT, "Primitive=%s(scale=%.2f, %s=%dms)", 190 VibrationEffect.Composition.primitiveToString(mPrimitiveId), mScale, 191 toDelayTypeDebugString(mDelayType), mDelay); 192 } 193 194 @Override equals(@ullable Object o)195 public boolean equals(@Nullable Object o) { 196 if (this == o) return true; 197 if (o == null || getClass() != o.getClass()) return false; 198 PrimitiveSegment that = (PrimitiveSegment) o; 199 return mPrimitiveId == that.mPrimitiveId 200 && Float.compare(that.mScale, mScale) == 0 201 && mDelay == that.mDelay 202 && mDelayType == that.mDelayType; 203 } 204 205 @Override hashCode()206 public int hashCode() { 207 return Objects.hash(mPrimitiveId, mScale, mDelay, mDelayType); 208 } 209 isValidDelayType(int delayType)210 private static boolean isValidDelayType(int delayType) { 211 return switch (delayType) { 212 case VibrationEffect.Composition.DELAY_TYPE_PAUSE, 213 VibrationEffect.Composition.DELAY_TYPE_RELATIVE_START_OFFSET -> true; 214 default -> false; 215 }; 216 } 217 toDelayTypeDebugString(int delayType)218 private static String toDelayTypeDebugString(int delayType) { 219 return switch (delayType) { 220 case VibrationEffect.Composition.DELAY_TYPE_RELATIVE_START_OFFSET -> "startOffset"; 221 default -> "pause"; 222 }; 223 } 224 225 @NonNull 226 public static final Parcelable.Creator<PrimitiveSegment> CREATOR = 227 new Parcelable.Creator<PrimitiveSegment>() { 228 @Override 229 public PrimitiveSegment createFromParcel(Parcel in) { 230 // Skip the type token 231 in.readInt(); 232 return new PrimitiveSegment(in); 233 } 234 235 @Override 236 public PrimitiveSegment[] newArray(int size) { 237 return new PrimitiveSegment[size]; 238 } 239 }; 240 } 241