Skip to content

Commit 0182acc

Browse files
committed
Adds snippets for Audio page
1 parent c4096d2 commit 0182acc

File tree

5 files changed

+152
-3
lines changed

5 files changed

+152
-3
lines changed

gradle/libs.versions.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,9 @@ validatorPush = "1.0.0-alpha08"
7373
version-catalog-update = "1.0.0"
7474
watchfaceComplicationsDataSourceKtx = "1.2.1"
7575
wear = "1.3.0"
76-
wearComposeFoundation = "1.5.0-rc02"
77-
wearComposeMaterial = "1.5.0-rc02"
78-
wearComposeMaterial3 = "1.5.0-rc02"
76+
wearComposeFoundation = "1.5.2"
77+
wearComposeMaterial = "1.5.2"
78+
wearComposeMaterial3 = "1.5.2"
7979
wearOngoing = "1.1.0"
8080
wearToolingPreview = "1.0.0"
8181
webkit = "1.14.0"

wear/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ android {
5353

5454
dependencies {
5555
implementation(libs.androidx.core.ktx)
56+
implementation(libs.androidx.media3.exoplayer)
5657
val composeBom = platform(libs.androidx.compose.bom)
5758
implementation(composeBom)
5859
androidTestImplementation(composeBom)

wear/src/main/AndroidManifest.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,11 @@
7272
</intent-filter>
7373
</activity>
7474

75+
<activity android:name=".snippets.audio.AudioActivity"
76+
android:exported="true"
77+
android:taskAffinity=""
78+
android:theme="@android:style/Theme.DeviceDefault"/>
79+
7580
<!-- [START android_wear_tile_manifest] -->
7681
<service
7782
android:name=".snippets.tile.MyTileService"
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
/*
2+
* Copyright 2025 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.example.wear.snippets.audio
18+
19+
import android.content.Context
20+
import android.content.pm.PackageManager
21+
import android.media.AudioDeviceCallback
22+
import android.media.AudioDeviceInfo
23+
import android.media.AudioManager
24+
import androidx.activity.ComponentActivity
25+
import androidx.annotation.OptIn
26+
import androidx.media3.common.AudioAttributes
27+
import androidx.media3.common.util.UnstableApi
28+
import androidx.media3.exoplayer.ExoPlayer
29+
import androidx.media3.ui.WearUnsuitableOutputPlaybackSuppressionResolverListener
30+
31+
class AudioActivity : ComponentActivity() {
32+
33+
// [START android_wear_audio_detect_devices]
34+
private val audioManager: AudioManager by lazy {
35+
getSystemService(AUDIO_SERVICE) as AudioManager
36+
}
37+
38+
fun audioOutputAvailable(type: Int): Boolean {
39+
if (!packageManager.hasSystemFeature(PackageManager.FEATURE_AUDIO_OUTPUT)) {
40+
return false
41+
}
42+
return audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS).any { it.type == type }
43+
}
44+
// [END android_wear_audio_detect_devices]
45+
46+
@OptIn(UnstableApi::class)
47+
fun buildExoPlayer(context: Context): ExoPlayer {
48+
// [START android_wear_exoplayer_audio_output_suppression]
49+
val exoPlayer = ExoPlayer.Builder(context)
50+
.setAudioAttributes(AudioAttributes.DEFAULT, true)
51+
.setSuppressPlaybackOnUnsuitableOutput(true)
52+
.build()
53+
// [END android_wear_exoplayer_audio_output_suppression]
54+
// [START android_wear_exoplayer_audio_output_suppression_listener]
55+
exoPlayer.addListener(WearUnsuitableOutputPlaybackSuppressionResolverListener(context))
56+
// [END android_wear_exoplayer_audio_output_suppression_listener]
57+
return exoPlayer
58+
}
59+
60+
override fun onCreate(savedInstanceState: android.os.Bundle?) {
61+
super.onCreate(savedInstanceState)
62+
63+
// [START android_wear_audio_detect_devices_sample]
64+
val hasSpeaker = audioOutputAvailable(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER)
65+
val hasBluetoothHeadset = audioOutputAvailable(AudioDeviceInfo.TYPE_BLUETOOTH_A2DP)
66+
val hasBLEBroadcast = audioOutputAvailable(AudioDeviceInfo.TYPE_BLE_BROADCAST)
67+
val hasBLEHeadset = audioOutputAvailable(AudioDeviceInfo.TYPE_BLE_HEADSET)
68+
val hasBLESpeaker = audioOutputAvailable(AudioDeviceInfo.TYPE_BLE_SPEAKER)
69+
// [END android_wear_audio_detect_devices_sample]
70+
println("Has speaker: $hasSpeaker")
71+
println("Has Bluetooth headset: $hasBluetoothHeadset")
72+
println("Has BLE broadcast: $hasBLEBroadcast")
73+
println("Has BLE headset: $hasBLEHeadset")
74+
println("Has BLE speaker: $hasBLESpeaker")
75+
76+
// [START android_wear_audio_register_callback]
77+
val audioDeviceCallback =
78+
object : AudioDeviceCallback() {
79+
override fun onAudioDevicesAdded(addedDevices: Array<out AudioDeviceInfo>?) {
80+
super.onAudioDevicesAdded(addedDevices)
81+
if (audioOutputAvailable(AudioDeviceInfo.TYPE_BLUETOOTH_A2DP) ||
82+
audioOutputAvailable(AudioDeviceInfo.TYPE_BLE_BROADCAST) ||
83+
audioOutputAvailable(AudioDeviceInfo.TYPE_BLE_HEADSET) ||
84+
audioOutputAvailable(AudioDeviceInfo.TYPE_BLE_SPEAKER)
85+
) {
86+
// A Bluetooth or BLE device is connected and available for playback.
87+
}
88+
}
89+
override fun onAudioDevicesRemoved(removedDevices: Array<out AudioDeviceInfo>?) {
90+
super.onAudioDevicesRemoved(removedDevices)
91+
if (!(audioOutputAvailable(AudioDeviceInfo.TYPE_BLUETOOTH_A2DP)) &&
92+
!(audioOutputAvailable(AudioDeviceInfo.TYPE_BLE_BROADCAST)) &&
93+
!(audioOutputAvailable(AudioDeviceInfo.TYPE_BLE_HEADSET)) &&
94+
!(audioOutputAvailable(AudioDeviceInfo.TYPE_BLE_SPEAKER))
95+
) {
96+
// No Bluetooth or BLE devices are connected anymore.
97+
}
98+
}
99+
}
100+
101+
audioManager.registerAudioDeviceCallback(audioDeviceCallback, /*handler=*/ null)
102+
// [END android_wear_audio_register_callback]
103+
}
104+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* Copyright 2025 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.example.wear.snippets.audio
18+
19+
import android.content.Context
20+
import android.content.Intent
21+
import android.provider.Settings
22+
23+
object BluetoothSettings {
24+
// [START android_wear_bluetooth_settings]
25+
fun Context.launchBluetoothSettings(closeOnConnect: Boolean = true) {
26+
val intent = with(Intent(Settings.ACTION_BLUETOOTH_SETTINGS)) {
27+
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
28+
putExtra("EXTRA_CONNECTION_ONLY", true)
29+
if (closeOnConnect) {
30+
putExtra("EXTRA_CLOSE_ON_CONNECT", true)
31+
}
32+
putExtra("android.bluetooth.devicepicker.extra.FILTER_TYPE", FILTER_TYPE_AUDIO)
33+
}
34+
startActivity(intent)
35+
}
36+
37+
internal const val FILTER_TYPE_AUDIO = 1
38+
// [END android_wear_bluetooth_settings]
39+
}

0 commit comments

Comments
 (0)