feat: register as Android default digital assistant (#757) (#758)

* feat: register as Android default digital assistant

* fix: assistant role request fallback + always-attempt click

* remove: assistant role request row from Settings

The system-level assistant registration stays (VoiceInteractionService
+ ROLE_ASSISTANT, set via system Settings -> Default apps), but the
in-app Settings row is dropped: RequestRoleActivity fails to resolve
the caller on a plain startActivity ('Package name cannot be null or
empty'), so the button was a dead end. Removes the row, the
AssistantRole helper, and the now-unused strings.

---------

Co-authored-by: M57 <hy4ri@users.noreply.github.com>
This commit is contained in:
M57andM57 authored and GitHub committed 2026-08-01 18:33:33 +03:00
1 parent 63b966b133
commit ed48e10ca6
6 files changed
+140

No files matched your search

@@ -0,0 +1,21 @@
package com.m57.hermescontrol.assistant
import android.content.Intent
import android.speech.RecognitionService
/**
* Stub recognition service. The framework requires a recognition service to
* be declared in voice_interaction_service.xml for the assistant component to
* be valid (AOSP VoiceInteractionServiceInfo parse error otherwise), but v1
* has no speech input — long-press Home just opens the app.
*/
class AssistantRecognitionService : RecognitionService() {
override fun onStartListening(
intent: Intent,
callback: Callback,
) = Unit
override fun onStopListening(callback: Callback) = Unit
override fun onCancel(callback: Callback) = Unit
}
@@ -0,0 +1,10 @@
package com.m57.hermescontrol.assistant
import android.service.voice.VoiceInteractionService
/**
* Top-level digital-assistant service. The system keeps this running while
* Hermes Mobile is the default assistant; the actual trigger handling lives
* in [AssistantSessionService] / [AssistantSession].
*/
class AssistantService : VoiceInteractionService()
@@ -0,0 +1,60 @@
package com.m57.hermescontrol.assistant
import android.app.assist.AssistContent
import android.app.assist.AssistStructure
import android.content.Context
import android.content.Intent
import android.os.Build
import android.os.Bundle
import android.service.voice.VoiceInteractionSession
import androidx.annotation.RequiresApi
import com.m57.hermescontrol.MainActivity
/**
* Handles a single assistant invocation. There is no voice UI in v1 — every
* trigger (long-press Home / corner swipe) simply opens the app in the
* foreground, then closes the session.
*
* [onShow] fires for every invocation; [onHandleAssist] additionally receives
* the assist data. Both route to the same guarded launch so the app opens
* exactly once per session. The deprecated 3-arg [onHandleAssist] is kept so
* the launch works on every supported API level (the framework's
* [AssistState] default forwards to it below API 29).
*/
class AssistantSession(context: Context) : VoiceInteractionSession(context) {
private var launchHandled = false
override fun onShow(
args: Bundle?,
showFlags: Int,
) {
super.onShow(args, showFlags)
launchHermesAndFinish()
}
@Suppress("OVERRIDE_DEPRECATION")
override fun onHandleAssist(
data: Bundle?,
structure: AssistStructure?,
content: AssistContent?,
) {
launchHermesAndFinish()
}
@RequiresApi(Build.VERSION_CODES.Q)
override fun onHandleAssist(state: AssistState) {
launchHermesAndFinish()
}
private fun launchHermesAndFinish() {
if (launchHandled) return
launchHandled = true
val intent =
Intent(context, MainActivity::class.java)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP)
context.startActivity(intent)
finish()
}
}
@@ -0,0 +1,13 @@
package com.m57.hermescontrol.assistant
import android.os.Bundle
import android.service.voice.VoiceInteractionSession
import android.service.voice.VoiceInteractionSessionService
/**
* Spawns an [AssistantSession] each time the user invokes the assistant
* (long-press Home / corner swipe).
*/
class AssistantSessionService : VoiceInteractionSessionService() {
override fun onNewSession(args: Bundle?): VoiceInteractionSession = AssistantSession(this)
}