Offline voice assistant for Android: wake word, open apps, directions, calls, texts, and hand-off to the Claude app. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MQRbidocjywGyo13fqxmUJ
50 lines
1.4 KiB
Kotlin
Executable File
50 lines
1.4 KiB
Kotlin
Executable File
package space.bloggin.heyclaude
|
|
|
|
import android.content.Context
|
|
import android.media.AudioManager
|
|
import android.media.ToneGenerator
|
|
import android.os.Handler
|
|
import android.os.Looper
|
|
import android.os.VibrationEffect
|
|
import android.os.Vibrator
|
|
import android.os.VibratorManager
|
|
|
|
object Sounds {
|
|
private val main = Handler(Looper.getMainLooper())
|
|
|
|
private fun tone(type: Int, ms: Int) {
|
|
try {
|
|
val tg = ToneGenerator(AudioManager.STREAM_NOTIFICATION, 85)
|
|
tg.startTone(type, ms)
|
|
main.postDelayed({ tg.release() }, ms + 300L)
|
|
} catch (_: Exception) {
|
|
}
|
|
}
|
|
|
|
private fun buzz(ctx: Context, ms: Long) {
|
|
try {
|
|
val v = (ctx.getSystemService(Context.VIBRATOR_MANAGER_SERVICE) as? VibratorManager)?.defaultVibrator
|
|
?: @Suppress("DEPRECATION") (ctx.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator)
|
|
v.vibrate(VibrationEffect.createOneShot(ms, VibrationEffect.DEFAULT_AMPLITUDE))
|
|
} catch (_: Exception) {
|
|
}
|
|
}
|
|
|
|
/** "I'm listening" */
|
|
fun wake(ctx: Context) {
|
|
if (Prefs.beep(ctx)) tone(ToneGenerator.TONE_PROP_BEEP, 140)
|
|
buzz(ctx, 40)
|
|
}
|
|
|
|
/** Got it */
|
|
fun done(ctx: Context) {
|
|
if (Prefs.beep(ctx)) tone(ToneGenerator.TONE_PROP_ACK, 120)
|
|
}
|
|
|
|
/** Didn't work */
|
|
fun error(ctx: Context) {
|
|
if (Prefs.beep(ctx)) tone(ToneGenerator.TONE_PROP_NACK, 250)
|
|
buzz(ctx, 120)
|
|
}
|
|
}
|