Build a simple media player app¶
This guide will walk you through on how to build a very simple media player app for Wear OS, capable of playing a media which is hosted on the internet.
This guide assumes that you are familiar with:
- How to create Wear OS projects in Android Studio;
- Kotlin programming language;
- Jetpack Compose;
Display a PlayerScreen¶
1 - Add dependency¶
Create a new project from Android Studio by choosing "Basic Wear App Without Associated Tiles" from
"Wear OS" templates. Add dependency on media-ui to your project’s build.gradle:
implementation "com.google.android.horologist:horologist-media-ui:$horologist_version"
2 - Add PlayerScreen¶
Add the following code to your Activity’s onCreate function:
setContent {
PlayerScreen(
mediaDisplay = {
TextMediaDisplay(
title = "Song name",
subtitle = "Artist name"
)
},
controlButtons = {
PodcastControlButtons(
onPlayButtonClick = { },
onPauseButtonClick = { },
playPauseButtonEnabled = true,
playing = false,
onSeekBackButtonClick = { },
seekBackButtonEnabled = true,
onSeekForwardButtonClick = { },
seekForwardButtonEnabled = true,
)
},
buttons = { }
)
}
This code is displaying PlayerScreen on the app. PlayerScreen is a full screen composable that
contains slots
parameters to pass the contents to be displayed for media display, control buttons and more.
In this sample, we are using the UI components TextMediaDisplay and PodcastControlButtons,
provided by the UI library, as values to parameters of PlayerScreen.
Result¶
Run the app and you should see the following screen:

None of the controls are working, as they were not implemented yet.
Make the screen functional¶
1 - Add dependencies¶
Add the following dependencies to your project’s build.gradle:
implementation "com.google.android.horologist:horologist-media-data:$horologist_version"
implementation "com.google.android.horologist:horologist-audio-ui:$horologist_version"
implementation("androidx.media3:media3-exoplayer:$media3_version")
2 - Add ViewModel¶
Add a ViewModel extending PlayerViewModel, providing an instance of PlayerRepositoryImpl:
class MyViewModel(
player: Player,
playerRepository: PlayerRepositoryImpl = PlayerRepositoryImpl()
) : PlayerViewModel(playerRepository) {}
3 - Add init block¶
Add the following init block to the ViewModel to connect the Player to the PlayerRepository,
set a media and update the position of the player every second:
init {
viewModelScope.launch {
playerRepository.connect(player) {}
playerRepository.setMedia(
Media(
id = "wake_up_02",
uri = "https://storage.googleapis.com/uamp/The_Kyoto_Connection_-_Wake_Up/02_-_Geisha.mp3",
title = "Geisha",
artist = "The Kyoto Connection"
)
)
}
}
4 - Create an instance of the ViewModel¶
Change your Activity’s onCreate function to:
@SuppressLint("UnsafeOptInUsageError")
val player = ExoPlayer.Builder(this)
.setSeekForwardIncrementMs(5000L)
.setSeekBackIncrementMs(5000L)
.build()
// ViewModels should NOT be created here like this
val viewModel = MyViewModel(player)
val volumeViewModel = createVolumeViewModel()
setContent {
PlayerScreen(
playerViewModel = viewModel,
volumeViewModel = volumeViewModel,
mediaDisplay = { playerUiState: PlayerUiState ->
DefaultMediaInfoDisplay(playerUiState)
},
controlButtons = { playerUIController: PlayerUiController,
playerUiState: PlayerUiState ->
PodcastControlButtons(
playerController = playerUIController,
playerUiState = playerUiState
)
},
buttons = { }
)
}
Add createVolumeViewModel function to create a VolumeViewModel:
fun createVolumeViewModel(): VolumeViewModel {
val audioRepository = SystemAudioRepository.fromContext(application)
val vibrator: Vibrator = application.getSystemService(Vibrator::class.java)
return VolumeViewModel(audioRepository, audioRepository, onCleared = {
audioRepository.close()
}, vibrator)
}
We are creating an instance of ExoPlayer, passing it to the ViewModel.
Then for the PlayerScreen slots we are using:
- the
DefaultMediaDisplaycomponent, which accepts aMediaUiModelinstance as parameter; - the stateful version of
PodcastControlButtons, which accepts instances ofPlayerViewModelandPlayerUiStateas parameters to hook the controls with theViewModel;
Result¶
Run the app again and this time, play with the screen controls as the app should be able to play, pause, and seek the media now:
