diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 3675825e..cd21467b 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -4,6 +4,7 @@ + , azimElev: Pair) { + val context = LocalContext.current + val view = LocalView.current val radarColor = Color(0xFFDCDCDC) val primaryColor = Color(0xFFFFE082) val secondaryColor = Color(0xFFDC0000) @@ -44,11 +59,77 @@ fun RadarViewCompose(item: OrbitalPos, items: List, azimElev: Pair(tween(1000)) val animScale = animTransition.animateFloat(16f, 64f, animSpec, label = "animScale") + val aimThreshold = 0.05f val measurer = rememberTextMeasurer() val sweepDegrees = remember { mutableFloatStateOf(0f) } val trackCreated = remember { mutableStateOf(false) } val trackPath = remember { mutableStateOf(Path()) } val trackEffect = remember { mutableStateOf(PathEffect.cornerPathEffect(0f)) } + val soundPool = remember { mutableStateOf(null) } + val beepSoundId = remember { mutableIntStateOf(0) } + val aimTargetDifference = remember { mutableFloatStateOf(0f) } + + LaunchedEffect(item.azimuth, item.elevation, azimElev.first, azimElev.second) { + val aimAzimuthRadians = azimElev.first.toDouble().toRadians() + val aimElevationRadians = abs(min(azimElev.second, 0f)).toDouble().toRadians() + // radius of 0.5 makes the aimTargetDifference range 0.0 to 1.0 + val radius = 0.5 + val aimX = sph2CartX(aimAzimuthRadians, aimElevationRadians, radius) + val aimY = sph2CartY(aimAzimuthRadians, aimElevationRadians, radius) + val satX = sph2CartX(item.azimuth, item.elevation, radius) + val satY = sph2CartY(item.azimuth, item.elevation, radius) + aimTargetDifference.floatValue = sqrt((satX - aimX).pow(2) + (satY - aimY).pow(2)) + + + val minPlaybackRate = 0.5f + val maxPlaybackRate = 2.0f + val playbackRate = + maxPlaybackRate - (aimTargetDifference.floatValue / (maxPlaybackRate - minPlaybackRate)) + + soundPool.value?.setRate(beepSoundId.intValue, playbackRate) + } + + LaunchedEffect(aimTargetDifference.floatValue < aimThreshold) { + if (aimTargetDifference.floatValue < aimThreshold) { + view.performHapticFeedback(HapticFeedbackConstantsCompat.VIRTUAL_KEY) + soundPool.value?.pause(beepSoundId.intValue) + } else { + soundPool.value?.resume(beepSoundId.intValue) + } + } + + LaunchedEffect(item.elevation > 0) { + if(item.elevation > 0) { + soundPool.value?.resume(beepSoundId.intValue) + } else { + soundPool.value?.pause(beepSoundId.intValue) + } + } + + DisposableEffect(Unit) { + val audioAttributes = AudioAttributes.Builder() + .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION) + .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) + .build() + + soundPool.value = SoundPool.Builder() + .setMaxStreams(1) + .setAudioAttributes(audioAttributes) + .build() + + beepSoundId.intValue = soundPool.value?.load(context, R.raw.beep, 1) ?: 0 + + soundPool.value?.setOnLoadCompleteListener { soundPool, _, status -> + if (status == 0) { + soundPool.play(beepSoundId.intValue, 0.5f, 0.5f, 0, -1, 1f) + } + } + + onDispose { + soundPool.value?.release() + } + } + Canvas(modifier = Modifier.fillMaxSize()) { val radius = (size.minDimension / 2f) * 0.95f if (!trackCreated.value) { diff --git a/app/src/main/res/raw/beep.mp3 b/app/src/main/res/raw/beep.mp3 new file mode 100644 index 00000000..ed6a42ad Binary files /dev/null and b/app/src/main/res/raw/beep.mp3 differ