This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

tools.hermes-voice: headset button starts Assist listening

The Android app now handles VOICE_COMMAND (HFP AT+BVRA from headsets),
shows over the lock screen and asks to unlock before opening the page.
It opens /?listen, which starts the call without pressing Talk.

+60 -10
+10
tools/hermes-voice/android/AndroidManifest.xml
··· 12 12 android:label="Assist" 13 13 android:icon="@mipmap/ic_launcher" 14 14 android:allowBackup="false"> 15 + <!-- Over the lock screen and waking the display: a headset button 16 + (VOICE_COMMAND) usually arrives while the phone is locked; the 17 + activity then asks to unlock before it opens the page. --> 15 18 <activity 16 19 android:name=".AssistActivity" 17 20 android:exported="true" 21 + android:showWhenLocked="true" 22 + android:turnScreenOn="true" 18 23 android:theme="@android:style/Theme.Translucent.NoTitleBar"> 19 24 <intent-filter> 20 25 <action android:name="android.intent.action.MAIN" /> ··· 22 27 </intent-filter> 23 28 <intent-filter> 24 29 <action android:name="android.intent.action.ASSIST" /> 30 + <category android:name="android.intent.category.DEFAULT" /> 31 + </intent-filter> 32 + <!-- Bluetooth headsets: HFP AT+BVRA=1 makes HeadsetService start it. --> 33 + <intent-filter> 34 + <action android:name="android.intent.action.VOICE_COMMAND" /> 25 35 <category android:name="android.intent.category.DEFAULT" /> 26 36 </intent-filter> 27 37 </activity>
+19 -8
tools/hermes-voice/static/app.js
··· 590 590 } 591 591 } 592 592 593 - talk.addEventListener("click", async () => { 594 - if (call) { 595 - talk.disabled = true; 596 - setStatus("Hanging up…"); 597 - call.close(); 598 - return; 599 - } 593 + async function startCall() { 600 594 call = new Call(); 601 595 talk.disabled = true; 602 596 talk.classList.add("live"); 603 597 talk.textContent = "Hang up"; 604 598 setStatus("Connecting…"); 605 599 // Must run inside the click handler: mobile browsers only allow audio 606 - // playback started by a user gesture. 600 + // playback started by a user gesture. Started without one (?listen), the 601 + // track handler's play() runs while the mic records, which Chrome allows. 607 602 voice.play().catch(() => {}); 608 603 try { 609 604 await call.start(); ··· 612 607 addLine("error", String(error.message || error)); 613 608 call.finish("failed to start"); 614 609 } 610 + } 611 + 612 + talk.addEventListener("click", () => { 613 + if (call) { 614 + talk.disabled = true; 615 + setStatus("Hanging up…"); 616 + call.close(); 617 + return; 618 + } 619 + startCall(); 615 620 }); 616 621 617 622 window.addEventListener("pagehide", () => call?.finish("page closed")); ··· 655 660 ), 656 661 ); 657 662 voiceSelect.value = saved in config.voices ? saved : config.voice; 663 + // The Android app (and the headset button) opens /?listen: talk right away. 664 + // Dropped from the URL once used, so a reload does not call again. 665 + if (new URLSearchParams(location.search).has("listen")) { 666 + history.replaceState(null, "", location.pathname); 667 + startCall(); 668 + } 658 669 } 659 670 660 671 loadConfig();
+31 -2
tools/hermes-voice/android/src/us/lassul/assist/AssistActivity.java
··· 1 1 package us.lassul.assist; 2 2 3 3 import android.app.Activity; 4 + import android.app.KeyguardManager; 4 5 import android.content.ComponentName; 5 6 import android.content.Intent; 6 7 import android.content.pm.PackageManager; ··· 14 15 import androidx.browser.customtabs.CustomTabsSession; 15 16 import androidx.browser.trusted.TrustedWebActivityIntentBuilder; 16 17 17 - /** Opens the voice page as a Trusted Web Activity, else as a Custom Tab or in the browser. */ 18 + /** 19 + * Opens the voice page as a Trusted Web Activity, else as a Custom Tab or in the browser. The 20 + * page starts listening at once (?listen). 21 + */ 18 22 public class AssistActivity extends Activity { 19 - private static final Uri URL = Uri.parse("https://assist.lassul.us/"); 23 + private static final Uri URL = Uri.parse("https://assist.lassul.us/?listen"); 20 24 21 25 private CustomTabsServiceConnection connection; 22 26 private boolean launched; ··· 24 28 @Override 25 29 protected void onCreate(Bundle state) { 26 30 super.onCreate(state); 31 + KeyguardManager keyguard = getSystemService(KeyguardManager.class); 32 + if (!keyguard.isKeyguardLocked()) { 33 + open(); 34 + return; 35 + } 36 + // Started over the lock screen (showWhenLocked): the browser cannot show there. 37 + keyguard.requestDismissKeyguard(this, new KeyguardManager.KeyguardDismissCallback() { 38 + @Override 39 + public void onDismissSucceeded() { 40 + open(); 41 + } 42 + 43 + @Override 44 + public void onDismissCancelled() { 45 + finish(); 46 + } 47 + 48 + @Override 49 + public void onDismissError() { 50 + finish(); 51 + } 52 + }); 53 + } 54 + 55 + private void open() { 27 56 String provider = twaProvider(); 28 57 if (provider == null) { 29 58 openCustomTab();