1 /* 2 * Copyright 2023 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 androidx.core.haptics.signal 18 19 import android.os.Build 20 import androidx.annotation.IntDef 21 import androidx.annotation.RestrictTo 22 import androidx.core.haptics.VibrationWrapper 23 import androidx.core.haptics.device.HapticDeviceProfile 24 import androidx.core.haptics.impl.HapticSignalConverter 25 26 /** 27 * A predefined haptic effect that represents common vibration effects, like clicks and ticks. 28 * 29 * Predefined haptic effects should be identical, regardless of the app they come from, in order to 30 * provide a cohesive experience for users across the entire device. The predefined effects are 31 * based on the [android.os.VibrationEffect.createPredefined] platform API. 32 * 33 * @sample androidx.core.haptics.samples.PlaySystemStandardClick 34 */ 35 public class PredefinedEffectSignal 36 private constructor( 37 38 /** The type of haptic effect to be played. */ 39 @Type internal val type: Int, 40 41 /** The minimum SDK level where this effect type is available in the platform. */ 42 private val minSdk: Int, 43 ) : FiniteSignal() { 44 45 /** Typedef for the [type] attribute. */ 46 @RestrictTo(RestrictTo.Scope.LIBRARY) 47 @Retention(AnnotationRetention.SOURCE) 48 @IntDef( 49 TICK, 50 CLICK, 51 HEAVY_CLICK, 52 DOUBLE_CLICK, 53 ) 54 public annotation class Type 55 56 public companion object { 57 internal const val TICK = 2 // VibrationEffect.EFFECT_TICK 58 internal const val CLICK = 0 // VibrationEffect.EFFECT_CLICK 59 internal const val HEAVY_CLICK = 5 // VibrationEffect.EFFECT_HEAVY_CLICK 60 internal const val DOUBLE_CLICK = 1 // VibrationEffect.EFFECT_DOUBLE_CLICK 61 62 private val Tick = PredefinedEffectSignal(TICK, Build.VERSION_CODES.Q) 63 private val Click = PredefinedEffectSignal(CLICK, Build.VERSION_CODES.Q) 64 private val HeavyClick = PredefinedEffectSignal(HEAVY_CLICK, Build.VERSION_CODES.Q) 65 private val DoubleClick = PredefinedEffectSignal(DOUBLE_CLICK, Build.VERSION_CODES.Q) 66 67 internal val ALL_EFFECTS = listOf(Tick, Click, HeavyClick, DoubleClick) 68 69 /** Returns all [PredefinedEffectSignal] types available at the current SDK level. */ 70 @JvmStatic getSdkAvailableEffectsnull71 internal fun getSdkAvailableEffects(): List<PredefinedEffectSignal> = 72 ALL_EFFECTS.filter { it.minSdk <= Build.VERSION.SDK_INT }.toList() 73 74 @JvmStatic typeToStringnull75 internal fun typeToString(@Type type: Int): String { 76 return when (type) { 77 TICK -> "Tick" 78 CLICK -> "Click" 79 HEAVY_CLICK -> "HeavyClick" 80 DOUBLE_CLICK -> "DoubleClick" 81 else -> type.toString() 82 } 83 } 84 85 /** 86 * A standard tick effect. 87 * 88 * This effect is less strong than the [predefinedClick]. 89 */ predefinedTicknull90 @JvmStatic public fun predefinedTick(): PredefinedEffectSignal = Tick 91 92 /** 93 * A standard click effect. 94 * 95 * Use this effect as a baseline, as it's the most common type of click effect. 96 */ 97 @JvmStatic public fun predefinedClick(): PredefinedEffectSignal = Click 98 99 /** 100 * A heavy click effect. 101 * 102 * This effect is stronger than the [predefinedClick]. 103 */ 104 @JvmStatic public fun predefinedHeavyClick(): PredefinedEffectSignal = HeavyClick 105 106 /** A double-click effect. */ 107 @JvmStatic public fun predefinedDoubleClick(): PredefinedEffectSignal = DoubleClick 108 } 109 110 override fun equals(other: Any?): Boolean { 111 if (this === other) return true 112 if (other !is PredefinedEffectSignal) return false 113 if (type != other.type) return false 114 return true 115 } 116 hashCodenull117 override fun hashCode(): Int { 118 return type.hashCode() 119 } 120 toStringnull121 override fun toString(): String { 122 return "PredefinedEffectSignal(type=${typeToString(type)})" 123 } 124 125 /** Returns the minimum SDK level required by the effect type. */ minSdknull126 internal fun minSdk(): Int = minSdk 127 128 override fun toVibration(): VibrationWrapper? = HapticSignalConverter.toVibration(this) 129 130 override fun isSupportedBy(deviceProfile: HapticDeviceProfile): Boolean = true 131 } 132