Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions app/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Demo app

This app demonstrates the Open Video UI for Android, with a phone/tablet UI in [`MainActivity`](src/main/java/com/theoplayer/android/ui/demo/MainActivity.kt) and an Android TV UI in [`TvActivity`](src/main/java/com/theoplayer/android/ui/demo/tv/TvActivity.kt).

## Credits

The Android TV home screen uses the following images:

* Bip Bop: ["Oscilloscope sine square.jpg"](https://commons.wikimedia.org/wiki/File:Oscilloscope_sine_square.jpg) by Xato, Public domain.
* Elephant's Dream: ["Elephants Dream s5 both.jpg"](https://commons.wikimedia.org/wiki/File:Elephants_Dream_s5_both.jpg) by © Blender Foundation / Ton Roosendaal et al., [CC BY 2.5](https://creativecommons.org/licenses/by/2.5).
* Sintel: ["Sintel-screenshot-1.jpg"](https://commons.wikimedia.org/wiki/File:Sintel-screenshot-1.jpg) by © Blender Foundation (www.sintel.org), [CC BY 3.0](https://creativecommons.org/licenses/by/3.0).
* DASH-IF Livesim and Test card: Frames of the DASH-IF Livesim test stream, captured in the demo app.
13 changes: 12 additions & 1 deletion app/src/main/java/com/theoplayer/android/ui/demo/Streams.kt
Original file line number Diff line number Diff line change
@@ -1,18 +1,25 @@
package com.theoplayer.android.ui.demo

import androidx.annotation.DrawableRes
import androidx.compose.runtime.saveable.Saver
import androidx.compose.runtime.saveable.SaverScope
import com.theoplayer.android.api.source.SourceDescription
import com.theoplayer.android.api.source.TypedSource
import com.theoplayer.android.api.source.addescription.GoogleImaAdDescription

data class Stream(val title: String, val description: String = "", val source: SourceDescription)
data class Stream(
val title: String,
val description: String = "",
@DrawableRes val image: Int,
val source: SourceDescription,
)

val streams by lazy {
listOf(
Stream(
title = "Bip Bop (HLS)",
description = "HLS · VOD",
image = R.drawable.stream_bipbop,
source =
SourceDescription.Builder(
TypedSource.Builder(
Expand All @@ -25,6 +32,7 @@ val streams by lazy {
Stream(
title = "Elephant's Dream (HLS)",
description = "HLS · VOD",
image = R.drawable.stream_elephants,
source =
SourceDescription.Builder(
TypedSource.Builder(
Expand All @@ -37,6 +45,7 @@ val streams by lazy {
Stream(
title = "Sintel (DASH) with preroll ad",
description = "DASH · VOD · Preroll ad",
image = R.drawable.stream_sintel,
source =
SourceDescription.Builder(
TypedSource.Builder(
Expand All @@ -56,6 +65,7 @@ val streams by lazy {
Stream(
title = "DASH-IF Livesim",
description = "DASH · Live",
image = R.drawable.stream_livesim,
source =
SourceDescription.Builder(
TypedSource.Builder(
Expand All @@ -68,6 +78,7 @@ val streams by lazy {
Stream(
title = "Test card (with CEA tracks)",
description = "DASH · VOD · CEA-608 captions",
image = R.drawable.stream_testcard,
source =
SourceDescription.Builder(
TypedSource.Builder("https://livesim2.dashif.org/vod/testpic_2s/cea608.mpd")
Expand Down
50 changes: 35 additions & 15 deletions app/src/main/java/com/theoplayer/android/ui/demo/tv/TvHomeScreen.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.theoplayer.android.ui.demo.tv

import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.focusGroup
import androidx.compose.foundation.layout.Arrangement
Expand Down Expand Up @@ -28,6 +29,8 @@ import androidx.compose.ui.focus.focusProperties
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.tv.material3.Button
Expand Down Expand Up @@ -99,7 +102,6 @@ private fun StreamCarousel(
) { index ->
StreamSlide(
stream = streams[index],
background = slideBackgrounds[index % slideBackgrounds.size],
onPlay = { onSelectStream(streams[index]) },
)
}
Expand All @@ -125,10 +127,19 @@ private fun Modifier.keepHorizontalFocusInside(): Modifier =
}
.focusGroup()

/** A single carousel slide, with the stream's title, description and a play button. */
/**
* A single carousel slide, with the stream's background image, title, description and play button.
*/
@Composable
private fun StreamSlide(stream: Stream, background: Brush, onPlay: () -> Unit) {
Box(modifier = Modifier.fillMaxSize().background(background)) {
private fun StreamSlide(stream: Stream, onPlay: () -> Unit) {
Box(modifier = Modifier.fillMaxSize()) {
Image(
painter = painterResource(stream.image),
contentDescription = null,
contentScale = ContentScale.Crop,
modifier = Modifier.fillMaxSize(),
)
SlideScrim()
Column(modifier = Modifier.align(Alignment.BottomStart).padding(32.dp)) {
SlideText(stream)
Spacer(Modifier.height(16.dp))
Expand All @@ -141,6 +152,26 @@ private fun StreamSlide(stream: Stream, background: Brush, onPlay: () -> Unit) {
}
}

/** Dark gradients keep the white slide text readable. */
@Composable
private fun SlideScrim() {
Box(
Modifier.fillMaxSize()
.background(
Brush.horizontalGradient(
0f to Color.Black.copy(alpha = 0.8f),
0.6f to Color.Transparent,
)
)
.background(
Brush.verticalGradient(
0.4f to Color.Transparent,
1f to Color.Black.copy(alpha = 0.6f),
)
)
)
}

/** The title and description of a carousel slide. */
@Composable
private fun ColumnScope.SlideText(stream: Stream) {
Expand Down Expand Up @@ -177,17 +208,6 @@ private fun ThemeRow(currentTheme: TvPlayerTheme, onSelectTheme: (TvPlayerTheme)
}
}

// Dark gradients, so the white slide text stays readable.
private val slideBackgrounds =
listOf(
Color(0xFF1A237E) to Color(0xFF4A148C),
Color(0xFF004D40) to Color(0xFF01579B),
Color(0xFFB71C1C) to Color(0xFF4E342E),
Color(0xFF263238) to Color(0xFF1B5E20),
Color(0xFF311B92) to Color(0xFF880E4F),
)
.map { (start, end) -> Brush.linearGradient(listOf(start, end)) }

// Keep content inside the overscan-safe area of TVs.
private val safeAreaHorizontalPadding = 48.dp
private val safeAreaVerticalPadding = 27.dp
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/src/main/res/drawable-nodpi/stream_sintel.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/assets/tv-demo-home.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed docs/assets/tv-demo-home.png
Binary file not shown.
2 changes: 1 addition & 1 deletion docs/guides/android-tv/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,6 @@ The TV UI has no Chromecast, fullscreen, picture-in-picture, or mute controls. T

The demo app's [`TvActivity`](https://github.com/THEOplayer/android-ui/blob/main/app/src/main/java/com/theoplayer/android/ui/demo/tv/TvActivity.kt) opens a home screen with a stream carousel and theme chips.

![Android TV demo home screen](../../assets/tv-demo-home.png)
![Android TV demo home screen](../../assets/tv-demo-home.jpg)

To run it, create a **Television (1080p)** Android Virtual Device, run the `app` module, and open the app from the TV launcher. See the [TV demo source](https://github.com/THEOplayer/android-ui/tree/main/app/src/main/java/com/theoplayer/android/ui/demo/tv) for the full example.
Loading