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 18 19 import android.media.AudioAttributes 20 import android.os.Build 21 import android.os.VibrationAttributes 22 import androidx.annotation.IntDef 23 import androidx.annotation.RequiresApi 24 import androidx.annotation.RestrictTo 25 import androidx.core.haptics.impl.HapticAttributesConverter 26 import java.util.Objects 27 28 /** 29 * Collection of attributes describing information about a haptic effect or signal. 30 * 31 * The attributes described here will be mapped to the corresponding value from either 32 * [android.os.VibrationAttributes] or [android.media.AudioAttributes], depending on the SDK level 33 * available. 34 * 35 * @sample androidx.core.haptics.samples.PlaySystemStandardClick 36 */ 37 public class HapticAttributes 38 @JvmOverloads 39 constructor( 40 41 /** The usage to apply the correct system policies and user settings to the vibration. */ 42 @Usage public val usage: Int, 43 44 /** The flags to control the vibration behavior. */ 45 @Flag public val flags: Int = 0, 46 ) { 47 48 /** Creates a [HapticAttributes] mapping fields from given [VibrationAttributes]. */ 49 @RequiresApi(Build.VERSION_CODES.R) 50 public constructor( 51 attrs: VibrationAttributes 52 ) : this( 53 HapticAttributesConverter.usageFromVibrationAttributes(attrs), 54 HapticAttributesConverter.flagsFromVibrationAttributes(attrs), 55 ) 56 57 /** Creates a [HapticAttributes] mapping fields from given [AudioAttributes]. */ 58 @RequiresApi(Build.VERSION_CODES.LOLLIPOP) 59 public constructor( 60 attrs: AudioAttributes 61 ) : this( 62 HapticAttributesConverter.usageFromAudioAttributes(attrs), 63 flags = 0, 64 ) 65 66 /** Builder class for [HapticAttributes]. */ 67 public class Builder 68 private constructor( 69 @Usage private var usage: Int, 70 @Flag private var flags: Int, 71 ) { 72 73 /** Creates a builder for [HapticAttributes] with given usage. */ 74 public constructor(@Usage usage: Int) : this(usage, flags = 0) 75 76 /** Creates a builder for [HapticAttributes] copying all fields from given attributes. */ 77 public constructor(attrs: HapticAttributes) : this(attrs.usage, attrs.flags) 78 79 /** Creates a builder for [HapticAttributes] copying mapped fields from given attributes. */ 80 @RequiresApi(Build.VERSION_CODES.R) 81 public constructor( 82 attrs: VibrationAttributes 83 ) : this( 84 HapticAttributesConverter.usageFromVibrationAttributes(attrs), 85 HapticAttributesConverter.flagsFromVibrationAttributes(attrs), 86 ) 87 88 /** Creates a builder for [HapticAttributes] copying mapped fields from given attributes. */ 89 @RequiresApi(Build.VERSION_CODES.LOLLIPOP) 90 public constructor( 91 attrs: AudioAttributes 92 ) : this( 93 HapticAttributesConverter.usageFromAudioAttributes(attrs), 94 flags = 0, 95 ) 96 97 /** Sets the usage that maps correct system policies and user settings to the vibration. */ <lambda>null98 public fun setUsage(@Usage usage: Int): Builder = apply { this.usage = usage } 99 100 /** Sets the flags to control the vibration behavior. */ <lambda>null101 public fun setFlags(@Flag flags: Int): Builder = apply { this.flags = flags } 102 103 /** Returns a built [HapticAttributes]. */ buildnull104 public fun build(): HapticAttributes = HapticAttributes(usage, flags) 105 } 106 107 override fun equals(other: Any?): Boolean { 108 if (this === other) return true 109 if (other !is HapticAttributes) return false 110 if (usage != other.usage) return false 111 if (flags != other.flags) return false 112 return true 113 } 114 hashCodenull115 override fun hashCode(): Int = Objects.hash(usage, flags) 116 117 override fun toString(): String = "HapticAttributes(usage=$usage, flags=$flags)" 118 119 internal fun toAttributes(): AttributesWrapper? = HapticAttributesConverter.toAttributes(this) 120 121 public companion object { 122 // Values from VibrationAttributes.USAGE_* and VibrationAttributes.FLAG_* 123 124 /** Usage value to use for accessibility vibrations, such as with a screen reader. */ 125 public const val USAGE_ACCESSIBILITY: Int = 66 126 127 /** Usage value to use for alarm vibrations. */ 128 public const val USAGE_ALARM: Int = 1 129 130 /** 131 * Usage value to use for vibrations which mean a request to enter/end a communication with 132 * the user, such as a voice prompt. 133 */ 134 public const val USAGE_COMMUNICATION_REQUEST: Int = 65 135 136 /** 137 * Usage value to use for vibrations which provide a feedback for hardware component 138 * interaction, such as a fingerprint sensor. 139 */ 140 public const val USAGE_HARDWARE_FEEDBACK: Int = 50 141 142 /** 143 * Usage value to use for media vibrations, such as music, movie, soundtrack, animations, 144 * games, or any interactive media that isn't for touch feedback specifically. 145 */ 146 public const val USAGE_MEDIA: Int = 19 147 148 /** Usage value to use for notification vibrations. */ 149 public const val USAGE_NOTIFICATION: Int = 49 150 151 /** 152 * Usage value to use for vibrations which emulate physical hardware reactions, such as edge 153 * squeeze. 154 * 155 * Note that normal screen-touch feedback "click" effects would typically be classed as 156 * [USAGE_TOUCH], and that on-screen "physical" animations like bouncing would be 157 * [USAGE_MEDIA]. 158 */ 159 public const val USAGE_PHYSICAL_EMULATION: Int = 34 160 161 /** Usage value to use for ringtone vibrations. */ 162 public const val USAGE_RINGTONE: Int = 33 163 164 /** 165 * Usage value to use for touch vibrations. 166 * 167 * Most typical haptic feedback should be classed as touch feedback. Examples include 168 * vibrations for tap, long press, drag and scroll. 169 */ 170 public const val USAGE_TOUCH: Int = 18 171 172 /** Usage value to use when usage is unknown. */ 173 public const val USAGE_UNKNOWN: Int = 0 174 175 /** 176 * Flag requesting vibration effect to be played even under limited interruptions. 177 * 178 * Only privileged apps can ignore user settings that limit interruptions, and this flag 179 * will be ignored otherwise. 180 */ 181 public const val FLAG_BYPASS_INTERRUPTION_POLICY: Int = 1 182 } 183 184 /** Typedef for the usage attribute. */ 185 @IntDef( 186 USAGE_ACCESSIBILITY, 187 USAGE_ALARM, 188 USAGE_COMMUNICATION_REQUEST, 189 USAGE_HARDWARE_FEEDBACK, 190 USAGE_MEDIA, 191 USAGE_NOTIFICATION, 192 USAGE_PHYSICAL_EMULATION, 193 USAGE_RINGTONE, 194 USAGE_TOUCH, 195 USAGE_UNKNOWN, 196 ) 197 @RestrictTo(RestrictTo.Scope.LIBRARY) 198 @Retention(AnnotationRetention.SOURCE) 199 internal annotation class Usage 200 201 /** Typedef for the flag attribute. */ 202 @IntDef( 203 flag = true, 204 value = 205 [ 206 FLAG_BYPASS_INTERRUPTION_POLICY, 207 ], 208 ) 209 @RestrictTo(RestrictTo.Scope.LIBRARY) 210 @Retention(AnnotationRetention.SOURCE) 211 internal annotation class Flag 212 } 213