Skip to content
Closed
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
16 changes: 7 additions & 9 deletions tv/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@ plugins {

android {
namespace = "com.example.tv"
compileSdk = 37
compileSdk = libs.versions.compileSdk.get().toInt()

defaultConfig {
applicationId = "com.example.tv"
minSdk = 28
targetSdk = 37
minSdk = libs.versions.minSdk.get().toInt()
targetSdk = libs.versions.targetSdk.get().toInt()
versionCode = 1
versionName = "1.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}

buildTypes {
Expand All @@ -25,14 +26,11 @@ android {
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
kotlin {
jvmToolchain(21)
compilerOptions {
jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_11)
}
jvmToolchain(17)
}

buildFeatures {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* Copyright 2026 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.example.tv.playback

import android.content.Context
import android.media.MediaCodec
import android.media.MediaFormat
import android.media.quality.MediaQualityManager
import android.media.quality.PictureProfile
import android.os.Build
import android.os.Bundle
import androidx.annotation.RequiresApi

// [START android_tv_playback_adjust_display_settings_profile_constants]
const val NAME_STANDARD: String = "standard"
const val NAME_VIVID: String = "vivid"
const val NAME_SPORTS: String = "sports"
const val NAME_GAME: String = "game"
const val NAME_MOVIE: String = "movie"
const val NAME_ENERGY_SAVING: String = "energy_saving"
const val NAME_USER: String = "user"
// [END android_tv_playback_adjust_display_settings_profile_constants]

@RequiresApi(36)
class AdjustDisplaySettings {

fun queryAvailableProfiles(context: Context, mediaCodec: MediaCodec) {
// [START android_tv_playback_adjust_display_settings_available_profiles]
if (Build.VERSION.SDK_INT >= 36) {
val mediaQualityManager: MediaQualityManager =
context.getSystemService(MediaQualityManager::class.java)
val profiles = mediaQualityManager.getAvailablePictureProfiles(null)
for (profile in profiles) {
// If we have a system sports profile, apply it to our media codec
if (profile.profileType == PictureProfile.TYPE_SYSTEM &&
profile.name == NAME_SPORTS
) {
val bundle = Bundle().apply {
putParcelable(MediaFormat.KEY_PICTURE_PROFILE_INSTANCE, profile)
}
mediaCodec.setParameters(bundle)
}
}
}
// [END android_tv_playback_adjust_display_settings_available_profiles]
}

fun getProfileByName(mediaQualityManager: MediaQualityManager) {
// [START android_tv_playback_adjust_display_settings_get_profile]
if (Build.VERSION.SDK_INT >= 36) {
val profile = mediaQualityManager.getPictureProfile(
PictureProfile.TYPE_SYSTEM, NAME_SPORTS, null
)
}
// [END android_tv_playback_adjust_display_settings_get_profile]
}
}
35 changes: 35 additions & 0 deletions tv/src/main/java/com/example/tv/playback/AmbientModeActivity.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Copyright 2026 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.example.tv.playback

import android.view.WindowManager
import androidx.activity.ComponentActivity

class AmbientModeActivity : ComponentActivity() {

fun enableKeepScreenOn() {
// [START android_tv_playback_ambient_mode_add_flags]
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
// [END android_tv_playback_ambient_mode_add_flags]
}

fun disableKeepScreenOn() {
// [START android_tv_playback_ambient_mode_clear_flags]
window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
// [END android_tv_playback_ambient_mode_clear_flags]
}
}
83 changes: 66 additions & 17 deletions tv/src/main/java/com/example/tv/ui/TvMoviesActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,15 @@ package com.example.tv.ui
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.Card
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
Expand All @@ -37,6 +36,8 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import androidx.tv.foundation.lazy.list.TvLazyColumn
import androidx.tv.foundation.lazy.list.items
import androidx.tv.material3.Carousel
import androidx.tv.material3.ExperimentalTvMaterial3Api
import coil.compose.AsyncImage
Expand All @@ -62,7 +63,8 @@ class TvMoviesActivity : ComponentActivity() {
)
val SECTIONS = listOf(
Section(
"Favorites", listOf(
"Favorites",
listOf(
Movie(
"The Shawshank Redemption",
"Two imprisoned men bond over a number of years",
Expand Down Expand Up @@ -96,7 +98,8 @@ class TvMoviesActivity : ComponentActivity() {
)
),
Section(
"Comedy", listOf(
"Comedy",
listOf(
Movie(
"Role Models",
"Two salesmen have to undergo community service to mentor two boys",
Expand Down Expand Up @@ -126,22 +129,43 @@ class TvMoviesActivity : ComponentActivity() {
)
}

@OptIn(ExperimentalTvMaterial3Api::class)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
Scaffold(modifier = Modifier.fillMaxSize()) { padding ->
Column {
FeaturedCarousel(FEATURED)
CatalogBrowser(SECTIONS, Modifier.padding(padding))
CatalogBrowserWithCarousel.CatalogBrowser(
FEATURED,
SECTIONS,
Modifier.padding(padding)
)
}
}
}
}
}

private object CatalogBrowserStub {
// [START android_compose_tv_catalog_browser_stub]
@Composable
fun CatalogBrowser(
featuredContentList: List<Movie>,
sectionList: List<Section>,
modifier: Modifier = Modifier,
onItemSelected: (Movie) -> Unit = {},
) {
// ToDo: add implementation
}
// [END android_compose_tv_catalog_browser_stub]
}

private object CatalogBrowserLazyColumn {
// [START android_compose_tv_catalog_browser]
@Composable
fun CatalogBrowser(
featuredContentList: List<Movie>,
sectionList: List<Section>,
modifier: Modifier = Modifier,
onItemSelected: (Movie) -> Unit = {},
Expand All @@ -150,12 +174,14 @@ fun CatalogBrowser(
modifier = modifier.fillMaxSize(),
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
items(sectionList) { section ->
items(sectionList.size) { index ->
val section = sectionList[index]
SectionRow(section, onItemSelected = onItemSelected)
}
}
}
// [END android_compose_tv_catalog_browser]
}

// [START android_compose_tv_catalog_browser_section]
@Composable
Expand Down Expand Up @@ -187,7 +213,8 @@ fun MovieCatalog(
modifier = modifier,
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
items(movies) { movie ->
items(movies.size) { index ->
val movie = movies[index]
MovieCard(
movie = movie,
onClick = { onClick(movie) }
Expand All @@ -198,15 +225,17 @@ fun MovieCatalog(
// [END android_compose_tv_lazyrow]

// [START android_compose_tv_movie_card]
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MovieCard(
movie: Movie,
modifier: Modifier = Modifier,
onClick: (Movie) -> Unit = {},
onClick: () -> Unit = {}
) {
Card(
modifier = modifier,
onClick = { onClick(movie) }) {
onClick = onClick
) {
AsyncImage(
model = movie.thumbnailUrl,
contentDescription = movie.title,
Expand Down Expand Up @@ -237,15 +266,35 @@ fun FeaturedCarousel(
contentScale = ContentScale.Crop,
modifier = Modifier.fillMaxSize()
)
Text(
text = content.title,
style = MaterialTheme.typography.headlineMedium.copy(color = androidx.compose.ui.graphics.Color.White),
modifier = Modifier
.padding(16.dp)
.background(androidx.compose.ui.graphics.Color.Black.copy(alpha = 0.5f))
.padding(8.dp)
)
Text(text = content.title)
}
}
}
// [END android_compose_tv_featured_carousel]

private object CatalogBrowserWithCarousel {
// [START android_compose_tv_catalog_browser_with_carousel]
@OptIn(ExperimentalTvMaterial3Api::class)
@Composable
fun CatalogBrowser(
featuredContentList: List<Movie>,
sectionList: List<Section>,
modifier: Modifier = Modifier,
onItemSelected: (Movie) -> Unit = {},
) {
TvLazyColumn(
modifier = modifier.fillMaxSize(),
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
item {
FeaturedCarousel(featuredContentList)
}

items(sectionList.size) { index ->
val section = sectionList[index]
SectionRow(section, onItemSelected = onItemSelected)
}
}
}
// [END android_compose_tv_catalog_browser_with_carousel]
}
Loading