在穿戴式裝置上播放音訊

本指南說明如何使用慣用的 Android API,在 Wear OS 應用程式中播放音訊。

偵測音訊裝置

Wear OS 應用程式必須先偵測穿戴式裝置是否有合適的音訊輸出裝置。穿戴式裝置通常至少有下列其中一種音訊輸出裝置:

  • AudioDeviceInfo.TYPE_BUILTIN_SPEAKER:適用於內建喇叭的裝置。
  • AudioDeviceInfo.TYPE_BLUETOOTH_A2DP:表示已配對且連接藍牙耳機。
  • AudioDeviceInfo.TYPE_BLE_BROADCAST:表示已與藍牙低功耗 (BLE) 廣播群組裝置配對及連線。
  • AudioDeviceInfo.TYPE_BLE_HEADSET:表示已與 BLE 耳機配對及連線。
  • AudioDeviceInfo.TYPE_BLE_SPEAKER:表示已與 BLE 音箱配對及連線。

以下範例使用 getDevices() 方法和 FEATURE_AUDIO_OUTPUT 值,檢查音訊輸出類型是否可用。

private val audioManager: AudioManager by lazy {
    getSystemService(AUDIO_SERVICE) as AudioManager
}

fun audioOutputAvailable(type: Int): Boolean {
    if (!packageManager.hasSystemFeature(PackageManager.FEATURE_AUDIO_OUTPUT)) {
        return false
    }
    return audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS).any { it.type == type }
}

接著,您可以使用這個方法檢查音訊輸出類型是否可用。

val hasSpeaker = audioOutputAvailable(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER)
val hasBluetoothHeadset = audioOutputAvailable(AudioDeviceInfo.TYPE_BLUETOOTH_A2DP)
val hasBLEBroadcast = audioOutputAvailable(AudioDeviceInfo.TYPE_BLE_BROADCAST)
val hasBLEHeadset = audioOutputAvailable(AudioDeviceInfo.TYPE_BLE_HEADSET)
val hasBLESpeaker = audioOutputAvailable(AudioDeviceInfo.TYPE_BLE_SPEAKER)

為提供最佳使用者體驗,請只在藍牙耳機或音箱與手錶連線時播放媒體。

選擇偏好的音訊輸出裝置

根據應用程式的用途,以及音訊對應用程式核心體驗的重要性,選擇使用者與應用程式音訊輸出內容的互動方式。

允許使用者選擇媒體輸出裝置

從 Wear OS 5 開始,系統會提供 UI,讓使用者選擇要透過哪部裝置播放媒體,並顯示目前播放的媒體內容資訊。

如果應用程式在需要透過搭載 Wear OS 5 以上版本的裝置播放音訊時,偵測到沒有已連線的藍牙耳機,建議您直接將使用者帶往媒體輸出切換器,如果裝置不支援媒體輸出切換器,請叫用 ACTION_BLUETOOTH_SETTINGS intent 動作,將使用者帶往系統設定中的藍牙頁面。

launchOutputSelection() 方法 (GitHub 上的 Horologist 程式庫的一部分) 示範如何讓使用者選擇媒體輸出裝置。

藍牙耳機

與裝置上隨時可用的內建喇叭不同,藍牙耳機在應用程式執行期間可能會是配對或取消配對狀態。如果應用程式需要耳機才能繼續操作,請註冊回呼,使用 registerAudioDeviceCallback 偵測使用者的藍牙耳機連線狀態:

val audioDeviceCallback =
    object : AudioDeviceCallback() {
        override fun onAudioDevicesAdded(addedDevices: Array<out AudioDeviceInfo>?) {
            super.onAudioDevicesAdded(addedDevices)
            if (audioOutputAvailable(AudioDeviceInfo.TYPE_BLUETOOTH_A2DP) ||
                audioOutputAvailable(AudioDeviceInfo.TYPE_BLE_BROADCAST) ||
                audioOutputAvailable(AudioDeviceInfo.TYPE_BLE_HEADSET) ||
                audioOutputAvailable(AudioDeviceInfo.TYPE_BLE_SPEAKER)
            ) {
                // A Bluetooth or BLE device is connected and available for playback.
            }
        }
        override fun onAudioDevicesRemoved(removedDevices: Array<out AudioDeviceInfo>?) {
            super.onAudioDevicesRemoved(removedDevices)
            if (!(audioOutputAvailable(AudioDeviceInfo.TYPE_BLUETOOTH_A2DP)) &&
                !(audioOutputAvailable(AudioDeviceInfo.TYPE_BLE_BROADCAST)) &&
                !(audioOutputAvailable(AudioDeviceInfo.TYPE_BLE_HEADSET)) &&
                !(audioOutputAvailable(AudioDeviceInfo.TYPE_BLE_SPEAKER))
            ) {
                // No Bluetooth or BLE devices are connected anymore.
            }
        }
    }

audioManager.registerAudioDeviceCallback(audioDeviceCallback, /*handler=*/ null)

如果應用程式在需要輸出音訊時偵測到沒有已連線的藍牙耳機,建議您不要顯示錯誤訊息,而是直接將使用者帶往藍牙設定,方便使用者連接耳機。傳送具有 ACTION_BLUETOOTH_SETTINGS 的意圖即可達到此效果:

fun Context.launchBluetoothSettings(closeOnConnect: Boolean = true) {
    val intent = with(Intent(Settings.ACTION_BLUETOOTH_SETTINGS)) {
        addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
        putExtra("EXTRA_CONNECTION_ONLY", true)
        if (closeOnConnect) {
            putExtra("EXTRA_CLOSE_ON_CONNECT", true)
        }
        putExtra("android.bluetooth.devicepicker.extra.FILTER_TYPE", FILTER_TYPE_AUDIO)
    }
    startActivity(intent)
}

internal const val FILTER_TYPE_AUDIO = 1

內建喇叭

大多數 Wear OS 裝置都內建喇叭。如果應用程式提供整合音效的非媒體用途,建議使用喇叭提供其他面向的互動體驗。舉例來說,配備喇叭的 Wear OS 裝置可能會觸發時鐘或計時器鬧鐘,並在完成時發出語音通知,而健身應用程式則可利用喇叭提供運動相關指示。

詳情請參閱 WearSpeakerSample

播放音訊

偵測到並選擇合適的音訊輸出裝置後,Wear OS 播放音訊的程序和行動裝置及其他裝置相同。詳情請參閱「MediaPlayer 總覽」一文。如要以更簡單的方式存取進階功能,例如串流及下載媒體,請使用 ExoPlayer。遵循音訊應用程式的最佳做法,例如管理音訊焦點

防止意外透過內建喇叭播放媒體

媒體應用程式可以按照這項指引,防止應用程式意外透過手錶內建喇叭播放媒體。指引內容會因應用程式使用的播放器而有所不同。

ExoPlayer

如果應用程式使用 ExoPlayer:

  1. 建構 ExoPlayer 例項時,呼叫 setSuppressPlaybackOnUnsuitableOutput(true) 方法

val exoPlayer = ExoPlayer.Builder(context)
    .setAudioAttributes(AudioAttributes.DEFAULT, true)
    .setSuppressPlaybackOnUnsuitableOutput(true)
    .build()

  1. 註冊 WearUnsuitableOutputPlaybackSuppressionResolverListener listener 做為 ExoPlayer 例項的事件監聽器,因應播放抑制事件:

exoPlayer.addListener(WearUnsuitableOutputPlaybackSuppressionResolverListener(context))

Horologist Media Toolkit

Horologist Media Toolkit 已包含相關邏輯,可防止透過手錶內建喇叭意外播放媒體。

其他媒體播放器