瀏覽代碼

feat: route hardware volume keys to the dashboard stream

Krasimir Kazakov 1 周之前
父節點
當前提交
e7d1d6ce34
共有 2 個文件被更改,包括 40 次插入 和 0 次删除
  1. 36 0
      android/app/src/main/kotlin/com/wasp/cardash_launcher/MainActivity.kt
  2. 4 0
      lib/app.dart

+ 36 - 0
android/app/src/main/kotlin/com/wasp/cardash_launcher/MainActivity.kt

@@ -1,5 +1,7 @@
 package com.wasp.cardash_launcher
 
+import android.media.AudioManager
+import android.view.KeyEvent
 import io.flutter.embedding.android.FlutterActivity
 import io.flutter.embedding.engine.FlutterEngine
 
@@ -18,6 +20,40 @@ class MainActivity : FlutterActivity() {
         }
     }
 
+    /**
+     * While the dashboard is visible, the hardware volume keys should change
+     * the same media stream as the on-screen control. Without this, Android
+     * falls back to the ring stream when nothing is playing and the dashboard
+     * and the keys disagree.
+     */
+    override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
+        if (keyCode == KeyEvent.KEYCODE_VOLUME_UP || keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
+            adjustMediaVolume(if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) 1 else -1)
+            return true
+        }
+        return super.onKeyDown(keyCode, event)
+    }
+
+    override fun onKeyUp(keyCode: Int, event: KeyEvent?): Boolean {
+        if (keyCode == KeyEvent.KEYCODE_VOLUME_UP || keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
+            return true
+        }
+        return super.onKeyUp(keyCode, event)
+    }
+
+    private fun adjustMediaVolume(direction: Int) {
+        try {
+            val audio = getSystemService(AudioManager::class.java)
+            audio.adjustStreamVolume(
+                AudioManager.STREAM_MUSIC,
+                if (direction > 0) AudioManager.ADJUST_RAISE else AudioManager.ADJUST_LOWER,
+                0,
+            )
+        } catch (_: Throwable) {
+            // Never let a volume-key edge case take down the launcher.
+        }
+    }
+
     override fun onDestroy() {
         launcherBridge?.dispose()
         systemBridge?.dispose()

+ 4 - 0
lib/app.dart

@@ -49,6 +49,10 @@ class _CarDashAppState extends State<CarDashApp> with WidgetsBindingObserver {
         system.start();
         context.read<LauncherController>().refresh();
       case AppLifecycleState.inactive:
+        // The system volume panel and permission dialogs can make the activity
+        // inactive without hiding it. Keep polling so the volume ring stays in
+        // sync while that panel is open.
+        break;
       case AppLifecycleState.hidden:
       case AppLifecycleState.paused:
       case AppLifecycleState.detached: