Closed sudomain closed 4 months ago
+1 I think this functionality would increase adoption quite a bit. Home Assistant does this. I'm not an Android developer but maybe this could help someone out:
<PreferenceCategory
android:title="@string/assist"
android:key="assist">
<SwitchPreference
android:key="assist_voice_command_intent"
android:icon="@drawable/ic_headphones_settings"
android:title="@string/open_with_headphone_button"
android:summary="@string/open_with_headphone_button_summary"
/>
</PreferenceCategory>
<activity-alias
android:name=".assist.VoiceCommandIntentActivity"
android:targetActivity=".assist.AssistActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VOICE_COMMAND" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity-alias>
@AndroidEntryPoint
class AssistActivity : BaseActivity() {
private val viewModel: AssistViewModel by viewModels()
private var contextIsLocked = true
companion object {
const val TAG = "AssistActivity"
private const val EXTRA_SERVER = "server"
private const val EXTRA_PIPELINE = "pipeline"
private const val EXTRA_START_LISTENING = "start_listening"
private const val EXTRA_FROM_FRONTEND = "from_frontend"
fun newInstance(
context: Context,
serverId: Int = -1,
pipelineId: String? = null,
startListening: Boolean = true,
fromFrontend: Boolean = true
): Intent {
return Intent(context, AssistActivity::class.java).apply {
putExtra(EXTRA_SERVER, serverId)
putExtra(EXTRA_PIPELINE, pipelineId)
putExtra(EXTRA_START_LISTENING, startListening)
putExtra(EXTRA_FROM_FRONTEND, fromFrontend)
}
}
}
private val requestPermission = registerForActivityResult(
ActivityResultContracts.RequestPermission(),
{ viewModel.onPermissionResult(it) }
)
override fun onCreate(savedInstanceState: Bundle?) {
enableEdgeToEdge(
navigationBarStyle = SystemBarStyle.auto(Color.TRANSPARENT, Color.TRANSPARENT)
)
super.onCreate(savedInstanceState)
updateShowWhenLocked()
if (savedInstanceState == null) {
viewModel.onCreate(
hasPermission = hasRecordingPermission(),
serverId = if (intent.hasExtra(EXTRA_SERVER)) {
intent.getIntExtra(EXTRA_SERVER, ServerManager.SERVER_ID_ACTIVE)
} else {
null
},
pipelineId = if (intent.hasExtra(EXTRA_PIPELINE)) {
intent.getStringExtra(EXTRA_PIPELINE) ?: AssistViewModelBase.PIPELINE_LAST_USED
} else {
AssistViewModelBase.PIPELINE_LAST_USED
},
startListening = if (intent.hasExtra(EXTRA_START_LISTENING)) {
intent.getBooleanExtra(EXTRA_START_LISTENING, true)
} else if (intent.action == Intent.ACTION_VOICE_COMMAND) {
// Always start listening if triggered via the voice command (e.g., from a BT headset).
true
} else {
null
}
)
}
val fromFrontend = intent.getBooleanExtra(EXTRA_FROM_FRONTEND, false)
setContent {
HomeAssistantAppTheme {
AssistSheetView(
conversation = viewModel.conversation,
pipelines = viewModel.pipelines,
inputMode = viewModel.inputMode,
fromFrontend = fromFrontend,
currentPipeline = viewModel.currentPipeline,
onSelectPipeline = viewModel::changePipeline,
onManagePipelines =
if (fromFrontend && viewModel.userCanManagePipelines()) {
{
startActivity(
WebViewActivity.newInstance(
this,
"config/voice-assistants/assistants"
).apply {
flags += Intent.FLAG_ACTIVITY_NEW_TASK // Delivers data in onNewIntent
}
)
finish()
}
} else {
null
},
onChangeInput = viewModel::onChangeInput,
onTextInput = viewModel::onTextInput,
onMicrophoneInput = viewModel::onMicrophoneInput,
onHide = { finish() }
)
}
}
}
override fun onResume() {
super.onResume()
viewModel.setPermissionInfo(hasRecordingPermission()) { requestPermission.launch(Manifest.permission.RECORD_AUDIO) }
}
override fun onPause() {
super.onPause()
viewModel.onPause()
}
override fun onDestroy() {
super.onDestroy()
viewModel.onDestroy()
}
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
this.intent = intent
val isLocked = getSystemService<KeyguardManager>()?.isKeyguardLocked ?: false
viewModel.onNewIntent(intent, contextIsLocked == isLocked)
updateShowWhenLocked(isLocked)
}
/** Set flags to show dialog when (un)locked, and prevent unlocked dialogs from resuming while locked **/
private fun updateShowWhenLocked(isLocked: Boolean? = null) {
val locked = isLocked ?: getSystemService<KeyguardManager>()?.isKeyguardLocked ?: false
contextIsLocked = locked
if (locked) {
window.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER)
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.O) {
@Suppress("DEPRECATION")
window.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED)
} else {
setShowWhenLocked(true)
setTurnScreenOn(true)
}
} else {
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.O) {
@Suppress("DEPRECATION")
window.clearFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED)
} else {
setShowWhenLocked(false)
}
}
}
private fun hasRecordingPermission() =
ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) == PackageManager.PERMISSION_GRANTED
}
Could you test if #228 works? A testing APK is linked there
That APK works great! Thank you!
Thank you for testing! I'm leaving this open until that PR is merged
Hello,
Can Dicio respond to the intent
ACTION_VOICE_COMMAND
so it can be a "voice assistant"?Dicio currently uses
ACTION_ASSIST
which is fordigital
ordevice
assistants. I'm not entirely sure about this terminology because it is different in different apps and my device settings.I bought a headset recently that claims it can start the "voice assistant" and from what I understand from my research so far, this means "ACTION_VOICE_COMMAND".