diff --git a/.idea/gradle.xml b/.idea/gradle.xml
index 305fce5..541be3a 100644
--- a/.idea/gradle.xml
+++ b/.idea/gradle.xml
@@ -14,6 +14,7 @@
+
diff --git a/app/src/main/java/com/techlads/composetv/features/wiw/WhoIsWatching.kt b/app/src/main/java/com/techlads/composetv/features/wiw/WhoIsWatching.kt
new file mode 100644
index 0000000..8cca173
--- /dev/null
+++ b/app/src/main/java/com/techlads/composetv/features/wiw/WhoIsWatching.kt
@@ -0,0 +1,9 @@
+package com.techlads.composetv.features.wiw
+
+import androidx.compose.runtime.Composable
+import com.techlads.composetv.features.wiw.data.Avatar
+
+@Composable
+fun WhoIsWatchingScreen(onProfileSelection: (avatar: Avatar) -> Unit) {
+ WhoIsWatchingContent(onProfileSelection)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/techlads/composetv/features/wiw/WhoIsWatchingContent.kt b/app/src/main/java/com/techlads/composetv/features/wiw/WhoIsWatchingContent.kt
new file mode 100644
index 0000000..a31609a
--- /dev/null
+++ b/app/src/main/java/com/techlads/composetv/features/wiw/WhoIsWatchingContent.kt
@@ -0,0 +1,241 @@
+@file:OptIn(ExperimentalTvMaterial3Api::class)
+
+package com.techlads.composetv.features.wiw
+
+import androidx.annotation.DrawableRes
+import androidx.compose.animation.AnimatedContent
+import androidx.compose.animation.ExperimentalAnimationApi
+import androidx.compose.animation.SizeTransform
+import androidx.compose.animation.fadeIn
+import androidx.compose.animation.fadeOut
+import androidx.compose.animation.slideInVertically
+import androidx.compose.animation.slideOutVertically
+import androidx.compose.animation.with
+import androidx.compose.foundation.BorderStroke
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.aspectRatio
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.focus.FocusRequester
+import androidx.compose.ui.focus.focusRequester
+import androidx.compose.ui.focus.onFocusChanged
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.layout.ContentScale
+import androidx.compose.ui.layout.onGloballyPositioned
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.tooling.preview.Devices
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import androidx.tv.foundation.PivotOffsets
+import androidx.tv.foundation.lazy.list.TvLazyRow
+import androidx.tv.material3.Border
+import androidx.tv.material3.ClickableSurfaceDefaults
+import androidx.tv.material3.ExperimentalTvMaterial3Api
+import androidx.tv.material3.Icon
+import androidx.tv.material3.IconButton
+import androidx.tv.material3.MaterialTheme
+import androidx.tv.material3.Surface
+import androidx.tv.material3.Text
+import com.techlads.composetv.R
+import com.techlads.composetv.features.wiw.data.Avatar
+import kotlinx.coroutines.delay
+
+private val avatarList = listOf(
+ Avatar(
+ title = "Jack",
+ image = R.drawable.boy
+ ), Avatar(
+ title = "Alice",
+ image = R.drawable.girl
+ ), Avatar(
+ title = "Archit",
+ image = R.drawable.old
+ )
+)
+
+@Composable
+fun WhoIsWatchingContent(onProfileSelection: (avatar: Avatar) -> Unit) {
+
+ //initial height set at 0.dp
+ var containerWidth by remember { mutableStateOf(0.dp) }
+ // get local density from composable
+ val density = LocalDensity.current
+
+
+ Box(
+ modifier = Modifier
+ .fillMaxSize()
+ .onGloballyPositioned {
+ containerWidth = with(density) {
+ it.size.width.toDp()
+ }
+ }, contentAlignment = Alignment.Center
+ ) {
+
+ Column(horizontalAlignment = Alignment.CenterHorizontally) {
+
+ var selectedAvatar by remember {
+ mutableStateOf("")
+ }
+
+ var isLeft by remember {
+ mutableStateOf(false)
+ }
+
+ var lastPosition by remember {
+ mutableStateOf(0)
+ }
+
+ val requester = remember { FocusRequester() }
+
+ LaunchedEffect(Unit) {
+ delay(10)
+ requester.requestFocus()
+ }
+
+ Text(
+ text = "Who's Watching?",
+ style = MaterialTheme.typography.titleLarge.copy(fontSize = 38.sp),
+ modifier = Modifier.padding(top = 32.dp)
+ )
+
+ Spacer(modifier = Modifier.size(68.dp))
+
+ TvLazyRow(
+ contentPadding = PaddingValues(horizontal = containerWidth / 2),
+ horizontalArrangement = Arrangement.Center,
+ pivotOffsets = PivotOffsets(0.5f, 0.5f),
+ modifier = Modifier.fillMaxWidth()
+ ) {
+ items(avatarList.size) {
+ val item = avatarList[it]
+ val itemIndex = it
+ ScaleAbleAvatar(
+ avatarRes = item.image,
+ modifier = Modifier
+ .then(if (it == 1) Modifier.focusRequester(requester) else Modifier)
+ .onFocusChanged {
+ if (it.isFocused) {
+ isLeft = lastPosition > itemIndex
+ lastPosition = itemIndex
+ }
+ selectedAvatar = item.title
+ },
+ onProfileSelection = onProfileSelection
+ )
+ }
+ }
+
+ if (selectedAvatar.isNotEmpty()) {
+ Spacer(modifier = Modifier.padding(top = 48.dp))
+ ProfileName(name = selectedAvatar, isLeft)
+ }
+
+ Spacer(modifier = Modifier.size(38.dp))
+
+ IconButton(onClick = { /*TODO*/ }) {
+ Icon(
+ painter = painterResource(id = R.drawable.ic_settings),
+ contentDescription = "Settings"
+ )
+ }
+ }
+ }
+}
+
+@OptIn(ExperimentalAnimationApi::class)
+@Composable
+fun ProfileName(name: String, scaleUp: Boolean) {
+ AnimatedContent(
+ targetState = name,
+ transitionSpec = {
+ // Compare the incoming number with the previous number.
+ if (scaleUp) {
+ // If the target number is larger, it slides up and fades in
+ // while the initial (smaller) number slides up and fades out.
+ slideInVertically { height -> height } + fadeIn() with
+ slideOutVertically { height -> -height } + fadeOut()
+ } else {
+ // If the target number is smaller, it slides down and fades in
+ // while the initial number slides down and fades out.
+ slideInVertically { height -> -height } + fadeIn() with
+ slideOutVertically { height -> height } + fadeOut()
+ }.using(
+ // Disable clipping since the faded slide-in/out should
+ // be displayed out of bounds.
+ SizeTransform(clip = false)
+ )
+ }, label = ""
+ ) { text ->
+ Text(
+ text = text,
+ style = MaterialTheme.typography.titleSmall.copy(fontSize = 24.sp),
+ modifier = Modifier.padding(top = 32.dp)
+ )
+ }
+}
+
+@Composable
+fun ScaleAbleAvatar(
+ modifier: Modifier,
+ avatarRes: Int,
+ onProfileSelection: (avatar: Avatar) -> Unit
+) {
+ Surface(
+ onClick = {
+ onProfileSelection(avatarList[avatarRes])
+ },
+ modifier = modifier.padding(horizontal = 32.dp),
+ border = ClickableSurfaceDefaults.border(
+ border = Border(
+ border = BorderStroke(
+ 4.dp, Color.Transparent
+ ), shape = CircleShape
+ ), focusedBorder = Border(
+ border = BorderStroke(
+ 4.dp, Color.White
+ ), shape = CircleShape
+ )
+ ),
+ shape = ClickableSurfaceDefaults.shape(shape = CircleShape),
+ scale = ClickableSurfaceDefaults.scale(focusedScale = 1.5f)
+ ) {
+ AvatarIcon(avatarRes = avatarRes, modifier = Modifier.size(120.dp))
+ }
+}
+
+@Composable
+fun AvatarIcon(modifier: Modifier, @DrawableRes avatarRes: Int, description: String? = null) {
+ Image(
+ painter = painterResource(id = avatarRes),
+ contentDescription = description,
+ contentScale = ContentScale.Crop,
+ modifier = modifier.aspectRatio(1f)
+ )
+}
+
+@Preview(device = Devices.TV_1080p, showBackground = true)
+@Composable
+private fun WhoIsWatchingPreview() {
+ WhoIsWatchingContent {
+
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/techlads/composetv/features/wiw/data/Avatar.kt b/app/src/main/java/com/techlads/composetv/features/wiw/data/Avatar.kt
new file mode 100644
index 0000000..6a332e4
--- /dev/null
+++ b/app/src/main/java/com/techlads/composetv/features/wiw/data/Avatar.kt
@@ -0,0 +1,5 @@
+package com.techlads.composetv.features.wiw.data
+
+import androidx.annotation.DrawableRes
+
+data class Avatar(val title: String, @DrawableRes val image: Int)
diff --git a/app/src/main/java/com/techlads/composetv/navigation/AppNavigation.kt b/app/src/main/java/com/techlads/composetv/navigation/AppNavigation.kt
index 6153c1e..4ff01c9 100644
--- a/app/src/main/java/com/techlads/composetv/navigation/AppNavigation.kt
+++ b/app/src/main/java/com/techlads/composetv/navigation/AppNavigation.kt
@@ -8,14 +8,15 @@ import androidx.compose.animation.fadeOut
import androidx.compose.runtime.Composable
import androidx.navigation.NavGraph.Companion.findStartDestination
import androidx.navigation.NavHostController
-import com.google.accompanist.navigation.animation.composable
import com.google.accompanist.navigation.animation.AnimatedNavHost
+import com.google.accompanist.navigation.animation.composable
import com.techlads.composetv.features.details.ProductDetailsScreen
import com.techlads.composetv.features.home.HomeScreen
import com.techlads.composetv.features.home.HomeViewModel
-import com.techlads.composetv.features.login.withToken.DeviceTokenAuthenticationScreen
import com.techlads.composetv.features.login.withEmailPassword.LoginScreen
+import com.techlads.composetv.features.login.withToken.DeviceTokenAuthenticationScreen
import com.techlads.composetv.features.player.PlayerScreen
+import com.techlads.composetv.features.wiw.WhoIsWatchingScreen
@OptIn(ExperimentalAnimationApi::class)
@Composable
@@ -27,7 +28,7 @@ fun AppNavigation(navController: NavHostController, viewModel: HomeViewModel) {
enterTransition = { tabEnterTransition() },
exitTransition = { tabExitTransition() }) {
LoginScreen {
- navController.navigateSingleTopTo(Screens.Home.title)
+ navController.navigateSingleTopTo(Screens.WhoIsWatching.title)
}
}
@@ -40,6 +41,15 @@ fun AppNavigation(navController: NavHostController, viewModel: HomeViewModel) {
}
}
+ composable(
+ Screens.WhoIsWatching.title,
+ enterTransition = { tabEnterTransition() },
+ exitTransition = { tabExitTransition() }) {
+ WhoIsWatchingScreen {
+ navController.navigateSingleTopTo(Screens.Home.title)
+ }
+ }
+
composable(
Screens.Home.title,
enterTransition = { tabEnterTransition() },
diff --git a/app/src/main/java/com/techlads/composetv/navigation/Screens.kt b/app/src/main/java/com/techlads/composetv/navigation/Screens.kt
index 6f2cf5b..93ef284 100644
--- a/app/src/main/java/com/techlads/composetv/navigation/Screens.kt
+++ b/app/src/main/java/com/techlads/composetv/navigation/Screens.kt
@@ -6,4 +6,5 @@ sealed class Screens(val title: String) {
object Home : Screens("home_screen")
object Player : Screens("player_screen")
object ProductDetail : Screens("product_detail")
+ object WhoIsWatching : Screens("who_is_watching")
}
\ No newline at end of file
diff --git a/app/src/main/res/drawable/boy.png b/app/src/main/res/drawable/boy.png
new file mode 100644
index 0000000..aa134ff
Binary files /dev/null and b/app/src/main/res/drawable/boy.png differ
diff --git a/app/src/main/res/drawable/boy3.png b/app/src/main/res/drawable/boy3.png
new file mode 100644
index 0000000..cd6f26f
Binary files /dev/null and b/app/src/main/res/drawable/boy3.png differ
diff --git a/app/src/main/res/drawable/girl.png b/app/src/main/res/drawable/girl.png
new file mode 100644
index 0000000..7accc02
Binary files /dev/null and b/app/src/main/res/drawable/girl.png differ
diff --git a/app/src/main/res/drawable/men.jpeg b/app/src/main/res/drawable/men.jpeg
new file mode 100644
index 0000000..34f3c55
Binary files /dev/null and b/app/src/main/res/drawable/men.jpeg differ
diff --git a/app/src/main/res/drawable/old.png b/app/src/main/res/drawable/old.png
new file mode 100644
index 0000000..12bd832
Binary files /dev/null and b/app/src/main/res/drawable/old.png differ