Files
Lawnchair/quickstep/src/com/android/quickstep/util/ContextualSearchHapticManager.kt
T
Andy Wickham 0b936727d6 Migrate Contextual Search code to AOSP
- Adds ContextualSearchInvoker
 - Adds ContextualSearchStateManager
 - Adds ContextualSearchHapticManager

Example output from ContextualSearchStateManager
in TouchInteractionService dump:
https://paste.googleplex.com/5536017815961600

Bug: 353715553
Test: go/contextual-search-entrypoints-test-plan
Flag: EXEMPT moving code from vendor to aosp

Change-Id: I8253294d6be427e0fc29d6052994de0c03f05a3e
2024-10-10 21:03:42 +00:00

102 lines
4.0 KiB
Kotlin

/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.quickstep.util
import android.content.Context
import android.os.VibrationEffect
import android.os.VibrationEffect.Composition
import android.os.Vibrator
import com.android.launcher3.dagger.ApplicationContext
import com.android.launcher3.dagger.LauncherAppSingleton
import com.android.launcher3.util.VibratorWrapper
import com.android.quickstep.DeviceConfigWrapper.Companion.get
import javax.inject.Inject
import kotlin.math.pow
/** Manages haptics relating to Contextual Search invocations. */
@LauncherAppSingleton
class ContextualSearchHapticManager
@Inject
internal constructor(@ApplicationContext private val context: Context) {
private var searchEffect = createSearchEffect()
private var contextualSearchStateManager = ContextualSearchStateManager.INSTANCE[context]
private fun createSearchEffect() =
if (
context
.getSystemService(Vibrator::class.java)!!
.areAllPrimitivesSupported(Composition.PRIMITIVE_TICK)
) {
VibrationEffect.startComposition()
.addPrimitive(Composition.PRIMITIVE_TICK, 1f)
.compose()
} else {
// fallback for devices without composition support
VibrationEffect.createPredefined(VibrationEffect.EFFECT_HEAVY_CLICK)
}
/** Indicates that search has been invoked. */
fun vibrateForSearch() {
searchEffect.let { VibratorWrapper.INSTANCE[context].vibrate(it) }
}
/** Indicates that search will be invoked if the current gesture is maintained. */
fun vibrateForSearchHint() {
val navbarConfig = get()
// Whether we should play the hint (ramp up) haptic
val shouldVibrate: Boolean =
if (
context
.getSystemService(Vibrator::class.java)!!
.areAllPrimitivesSupported(Composition.PRIMITIVE_LOW_TICK)
) {
if (contextualSearchStateManager.shouldPlayHapticOverride.isPresent) {
contextualSearchStateManager.shouldPlayHapticOverride.get()
} else {
navbarConfig.enableSearchHapticHint
}
} else {
false
}
if (shouldVibrate) {
val startScale = navbarConfig.lpnhHapticHintStartScalePercent / 100f
val endScale = navbarConfig.lpnhHapticHintEndScalePercent / 100f
val scaleExponent = navbarConfig.lpnhHapticHintScaleExponent
val iterations = navbarConfig.lpnhHapticHintIterations
val delayMs = navbarConfig.lpnhHapticHintDelay
val composition = VibrationEffect.startComposition()
for (i in 0 until iterations) {
val t = i / (iterations - 1f)
val scale =
((1 - t) * startScale + t * endScale)
.toDouble()
.pow(scaleExponent.toDouble())
.toFloat()
if (i == 0) {
// Adds a delay before the ramp starts
composition.addPrimitive(Composition.PRIMITIVE_LOW_TICK, scale, delayMs)
} else {
composition.addPrimitive(Composition.PRIMITIVE_LOW_TICK, scale)
}
}
VibratorWrapper.INSTANCE[context].vibrate(composition.compose())
}
}
}