263 lines
7.9 KiB
Kotlin
263 lines
7.9 KiB
Kotlin
/*
|
|
* haptic-profiles.kt - Haptic UI Profiles for Every Interaction
|
|
*
|
|
* Production-ready example demonstrating:
|
|
* - Tactile feedback patterns for game UI
|
|
* - Haptic intensity scaling with DDA 3.0
|
|
* - Accessibility: haptic redundancy for visual impairments
|
|
* - Rest-State Mechanics: calm haptic profiles
|
|
* - Ethical engagement: non-addictive feedback loops
|
|
*
|
|
* © 2026 - Agent Guardrails Template
|
|
*/
|
|
|
|
package com.agentguardrails.composeui
|
|
|
|
import androidx.compose.haptic.HapticFeedback
|
|
import androidx.compose.haptic.HapticPattern
|
|
import androidx.compose.runtime.Composable
|
|
import androidx.compose.runtime.remember
|
|
import androidx.game.core.DDAManager
|
|
import androidx.game.ethical.EthicalEngagement
|
|
|
|
/**
|
|
* Haptic profile definitions for game UI interactions
|
|
* Each profile maps to specific game events with intensity scaling
|
|
*/
|
|
enum class HapticProfile {
|
|
// Core Loop: Action Phase
|
|
COMBAT_TRIGGER, // Enemy engagement
|
|
COMBAT_SUCCESS, // Enemy defeated
|
|
COMBAT_FAILURE, // Player damaged
|
|
|
|
// Core Loop: Reward Phase
|
|
LOFT_REVEAL, // Loot box open
|
|
LOFT_LEGENDARY, // Legendary item drop
|
|
LOFT_COMMON, // Common item drop
|
|
|
|
// Core Loop: Upgrade Phase
|
|
SKILL_UNLOCK, // New skill acquired
|
|
LEVEL_UP, // Character level increase
|
|
STAT_INCREASE, // Attribute point added
|
|
|
|
// Navigation
|
|
MENU_OPEN, // Menu expansion
|
|
MENU_SELECT, // Option selection
|
|
MENU_CLOSE, // Menu dismissal
|
|
|
|
// Ethical Engagement
|
|
REST_STATE_ENTER, // Calm mode activation
|
|
REST_STATE_EXIT, // Session resume
|
|
|
|
// Accessibility
|
|
ERROR_ALERT, // Non-visual error notification
|
|
SUCCESS_CONFIRMATION, // Non-visual success confirmation
|
|
|
|
// Spatial Computing (XR)
|
|
XR_GRAB, // Virtual object grab
|
|
XR_RELEASE, // Virtual object release
|
|
XR_COLLISION, // Virtual collision detection
|
|
}
|
|
|
|
/**
|
|
* Haptic intensity levels scale with DDA 3.0 tier
|
|
* Prevents overstimulation for stressed players
|
|
*/
|
|
enum class HapticIntensity {
|
|
LIGHT, // 20% intensity - Rest State
|
|
MODERATE, // 50% intensity - Normal play
|
|
STRONG, // 80% intensity - High engagement
|
|
MAXIMUM // 100% intensity - Critical moments
|
|
}
|
|
|
|
/**
|
|
* Haptic feedback manager with DDA 3.0 adaptation
|
|
*/
|
|
object HapticProfiles {
|
|
|
|
private val ddaManager = DDAManager()
|
|
private val ethicalEngagement = EthicalEngagement()
|
|
|
|
/**
|
|
* Applies haptic profile with intensity scaling
|
|
* DDA 3.0: Reduces intensity for stressed players
|
|
*/
|
|
fun applyProfile(profile: HapticProfile) {
|
|
val intensity = calculateIntensity(profile)
|
|
val pattern = getPattern(profile, intensity)
|
|
|
|
HapticFeedback.perform(pattern)
|
|
}
|
|
|
|
/**
|
|
* Calculates haptic intensity based on DDA tier and game state
|
|
*/
|
|
private fun calculateIntensity(profile: HapticProfile): HapticIntensity {
|
|
val ddaTier = ddaManager.currentTier
|
|
|
|
return when (ddaTier) {
|
|
DDAManager.Tier.DIFFICULT -> {
|
|
// Stressed players: reduce intensity
|
|
HapticIntensity.LIGHT
|
|
}
|
|
DDAManager.Tier.NORMAL -> {
|
|
// Standard intensity
|
|
HapticIntensity.MODERATE
|
|
}
|
|
DDAManager.Tier.RELAXED -> {
|
|
// Engaged players: moderate-strong intensity
|
|
when (profile) {
|
|
HapticProfile.LOFT_LEGENDARY, HapticProfile.LEVEL_UP ->
|
|
HapticIntensity.STRONG
|
|
else -> HapticIntensity.MODERATE
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Maps profile to haptic pattern with intensity
|
|
*/
|
|
private fun getPattern(profile: HapticProfile, intensity: HapticIntensity): HapticPattern {
|
|
return when (profile) {
|
|
HapticProfile.COMBAT_TRIGGER -> {
|
|
HapticPattern(
|
|
amplitude = intensity.amplitudeFactor,
|
|
duration = 50,
|
|
frequency = 150
|
|
)
|
|
}
|
|
HapticProfile.COMBAT_SUCCESS -> {
|
|
HapticPattern(
|
|
amplitude = intensity.amplitudeFactor,
|
|
duration = 100,
|
|
frequency = 200,
|
|
pattern = HapticPattern.Pattern.PULSE
|
|
)
|
|
}
|
|
HapticProfile.LOFT_LEGENDARY -> {
|
|
HapticPattern(
|
|
amplitude = intensity.amplitudeFactor,
|
|
duration = 300,
|
|
frequency = 250,
|
|
pattern = HapticPattern.Pattern.CASCADE
|
|
)
|
|
}
|
|
HapticProfile.LEVEL_UP -> {
|
|
HapticPattern(
|
|
amplitude = intensity.amplitudeFactor,
|
|
duration = 500,
|
|
frequency = 300,
|
|
pattern = HapticPattern.Pattern.CELEBRATION
|
|
)
|
|
}
|
|
HapticProfile.REST_STATE_ENTER -> {
|
|
HapticPattern(
|
|
amplitude = 0.2f, // Fixed light intensity
|
|
duration = 200,
|
|
frequency = 80,
|
|
pattern = HapticPattern.Pattern.GENTLE
|
|
)
|
|
}
|
|
HapticProfile.ERROR_ALERT -> {
|
|
// Accessibility: distinct pattern for error states
|
|
HapticPattern(
|
|
amplitude = 0.6f,
|
|
duration = 150,
|
|
frequency = 100,
|
|
pattern = HapticPattern.Pattern.WARNING
|
|
)
|
|
}
|
|
else -> {
|
|
HapticPattern.DEFAULT
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Rest-State haptic profile
|
|
* Ethical engagement: calming feedback after intense sessions
|
|
*/
|
|
@Composable
|
|
fun RestStateHapticFeedback(sessionDuration: Int) {
|
|
val restStateTimer = remember { mutableStateOf(0) }
|
|
|
|
if (sessionDuration > 45) {
|
|
// Gentle haptic reminder for rest state
|
|
applyProfile(HapticProfile.REST_STATE_ENTER)
|
|
restStateTimer.value++
|
|
|
|
if (restStateTimer.value >= 5) {
|
|
ethicalEngagement.enterRestState()
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Accessibility haptic redundancy
|
|
* Non-visual confirmation for impaired players
|
|
*/
|
|
fun AccessibilityHapticConfirmation(success: Boolean) {
|
|
val profile = if (success)
|
|
HapticProfile.SUCCESS_CONFIRMATION
|
|
else HapticProfile.ERROR_ALERT
|
|
|
|
applyProfile(profile)
|
|
}
|
|
|
|
/**
|
|
* XR haptic feedback for spatial computing
|
|
* Z-depth collision detection through tactile feedback
|
|
*/
|
|
fun XRHapticFeedback(event: XREvent, depth: Float) {
|
|
val intensity = when {
|
|
depth > 0.7f -> HapticIntensity.STRONG // Close collision
|
|
depth > 0.3f -> HapticIntensity.MODERATE // Mid-range
|
|
else -> HapticIntensity.LIGHT // Distant
|
|
}
|
|
|
|
val profile = when (event) {
|
|
XREvent.GRAB -> HapticProfile.XR_GRAB
|
|
XREvent.RELEASE -> HapticProfile.XR_RELEASE
|
|
XREvent.COLLISION -> HapticProfile.XR_COLLISION
|
|
}
|
|
|
|
applyProfile(profile)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Haptic pattern definition with amplitude, duration, frequency
|
|
*/
|
|
data class HapticPattern(
|
|
val amplitude: Float,
|
|
val duration: Int,
|
|
val frequency: Int,
|
|
val pattern: Pattern = Pattern.SIMPLE
|
|
) {
|
|
enum class Pattern {
|
|
SIMPLE, PULSE, CASCADE, CELEBRATION, GENTLE, WARNING
|
|
}
|
|
|
|
companion object {
|
|
val DEFAULT = HapticPattern(0.5f, 100, 150)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Intensity amplitude factor mapping
|
|
*/
|
|
val HapticIntensity.amplitudeFactor: Float
|
|
get() = when (this) {
|
|
HapticIntensity.LIGHT -> 0.2f
|
|
HapticIntensity.MODERATE -> 0.5f
|
|
HapticIntensity.STRONG -> 0.8f
|
|
HapticIntensity.MAXIMUM -> 1.0f
|
|
}
|
|
|
|
/**
|
|
* XR event types for spatial computing
|
|
*/
|
|
enum class XREvent {
|
|
GRAB, RELEASE, COLLISION
|
|
} |