Skip to content

Commit

Permalink
Fixes Bugs, Add Ability to reset playlist
Browse files Browse the repository at this point in the history
- Fix Player channel metadata not loading/changing
- Fix crash when icon is null
- Channel changer in player now follows playlist state
- Misc ui changes
  • Loading branch information
khaled-0 committed Apr 3, 2024
1 parent dedab3e commit 54ef84f
Show file tree
Hide file tree
Showing 6 changed files with 54 additions and 49 deletions.
8 changes: 4 additions & 4 deletions app/src/main/java/dev/khaled/leanstream/Navigator.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import androidx.navigation.navArgument
import dev.khaled.leanstream.channels.Channel
import dev.khaled.leanstream.channels.ChannelPicker
import dev.khaled.leanstream.channels.ChannelPickerScreen
import dev.khaled.leanstream.channels.ChannelViewModel
import dev.khaled.leanstream.player.Player
import dev.khaled.leanstream.player.PlayerScreen
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.cbor.Cbor
import kotlinx.serialization.decodeFromHexString
Expand All @@ -30,7 +30,7 @@ fun Navigator() {
) {

composable(route = Route.ChannelPicker.route) {
ChannelPicker(channelViewModel) { channel ->
ChannelPickerScreen(channelViewModel) { channel ->
navController.navigateSingleTop(Route.Player.launch(channel))
}
}
Expand All @@ -41,7 +41,7 @@ fun Navigator() {
) {
val serializedChannel = it.arguments?.getString("channel") ?: return@composable
val channel = Cbor.decodeFromHexString<Channel>(serializedChannel)
Player(channel = channel, channelViewModel.channels) {
PlayerScreen(channel = channel, channelViewModel.channels) {
navController.popBackStack()
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package dev.khaled.leanstream.channels

import android.app.AlertDialog
import android.content.Context
import android.widget.Toast
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
Expand Down Expand Up @@ -38,12 +41,13 @@ import dev.khaled.leanstream.channels.item.ChannelsGrid
import dev.khaled.leanstream.isRunningOnTV
import dev.khaled.leanstream.playSoundEffectOnFocus
import dev.khaled.leanstream.ui.Branding
import kotlinx.serialization.ExperimentalSerializationApi


@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ChannelPicker(
viewModel: ChannelViewModel = viewModel(),
openChannel: (channel: Channel) -> Unit
fun ChannelPickerScreen(
viewModel: ChannelViewModel = viewModel(), openChannel: (channel: Channel) -> Unit
) {
val context = LocalContext.current
val compactAppBar = remember { isRunningOnTV(context) }
Expand Down Expand Up @@ -106,7 +110,7 @@ fun ChannelPicker(
})

IconButton(onClick = {

resetChannelsListConfirmation(context, viewModel)
}, content = {
Icon(Icons.Outlined.Settings, null)
})
Expand All @@ -127,4 +131,14 @@ fun ChannelPicker(
openChannel(it)
}
}
}

@OptIn(ExperimentalSerializationApi::class)
fun resetChannelsListConfirmation(context: Context, viewModel: ChannelViewModel) {
AlertDialog.Builder(context).setTitle("Reset Channels")
.setMessage("Do you really want to remove imported channels?")
.setPositiveButton("Yes") { _, _ ->
viewModel.savePlaylistToDisk(context, emptyList());
Toast.makeText(context, "Restart Application To Take Effect", Toast.LENGTH_SHORT).show()
}.setNegativeButton("No", null).show()
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ package dev.khaled.leanstream.channels.importer
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.VideoLibrary
import androidx.compose.material3.Button
import androidx.compose.material3.Icon
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
Expand All @@ -24,15 +25,17 @@ fun ImportPlaylistPrompt(result: () -> Unit) {
var showDialog by remember { mutableStateOf(false) }

Column(
horizontalAlignment = Alignment.CenterHorizontally
modifier = Modifier.padding(16.dp), horizontalAlignment = Alignment.CenterHorizontally
) {
Icon(
Icons.Rounded.VideoLibrary, modifier = Modifier.size(64.dp), contentDescription = null
)
Spacer(modifier = Modifier.height(8.dp))
Text(text = "No entries found. Import a playlist (.m3u8) below")
Spacer(modifier = Modifier.height(8.dp))
Button(onClick = { showDialog = true }, modifier = Modifier.playSoundEffectOnFocus()) {
OutlinedButton(
onClick = { showDialog = true }, modifier = Modifier.playSoundEffectOnFocus()
) {
Text(text = "Import")
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ fun GridItem(
val context = LocalContext.current
val focusRequester = remember { FocusRequester() }
val isTouchScreen = remember { !isRunningOnTV(context) }
val showChannelTitle = isTouchScreen //TODO
val showChannelTitle = isTouchScreen || channel.icon.isNullOrBlank() //TODO

Card(
onClick = { onClick.invoke(channel) },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,15 @@ import dev.khaled.leanstream.player.controller.PlayerController


@Composable
fun Player(channel: Channel, playlist: List<Channel>, navigateBack: () -> Unit) {
fun PlayerScreen(channel: Channel, playlist: List<Channel>, navigateBack: () -> Unit) {
val exoPlayer = rememberExoPlayer(LocalContext.current)

LaunchedEffect(Unit) {
playlist.forEach {
exoPlayer.addMediaItem(
MediaItem.Builder().setUri(it.url).setMediaMetadata(
MediaMetadata.Builder().setDisplayTitle(it.title)
.setArtworkUri(channel.icon.let { i -> Uri.parse(i) }).build()
.setArtworkUri(it.icon?.let { i -> Uri.parse(i) }).build()
).build()
)
}
Expand Down Expand Up @@ -58,24 +58,3 @@ private fun rememberExoPlayer(context: Context) = remember {
playWhenReady = true
}
}

//private fun Modifier.dPadEvents(
// exoPlayer: ExoPlayer,
// videoPlayerState: VideoPlayerState,
// pulseState: VideoPlayerPulseState
//): Modifier = this.handleDPadKeyEvents(
// onLeft = {
// exoPlayer.seekBack()
// pulseState.setType(BACK)
// },
// onRight = {
// exoPlayer.seekForward()
// pulseState.setType(FORWARD)
// },
// onUp = { videoPlayerState.showControls() },
// onDown = { videoPlayerState.showControls() },
// onEnter = {
// exoPlayer.pause()
// videoPlayerState.showControls()
// }
//)
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import androidx.compose.ui.focus.onFocusEvent
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import androidx.media3.common.MediaItem
import androidx.media3.common.PlaybackException
import androidx.media3.common.Player
import androidx.media3.common.Player.STATE_BUFFERING
Expand All @@ -50,19 +51,20 @@ fun PlayerController(
var controllerVisible by remember { mutableStateOf(true) }
var isButtonFocused by remember { mutableStateOf(true) }

var mediaMetadata by remember { mutableStateOf(player.mediaMetadata) }

var isPlaying by remember { mutableStateOf(true) }
var isBuffering by remember { mutableStateOf(true) }
var error by remember { mutableStateOf<String?>(null) }


val handler = remember { Handler(Looper.getMainLooper()) }
val controllerVisibilityRunnable =
remember {
Runnable {
if (isButtonFocused) return@Runnable
if (controllerVisible) controllerVisible = false
}
val controllerVisibilityRunnable = remember {
Runnable {
if (isButtonFocused) return@Runnable
if (controllerVisible) controllerVisible = false
}
}

fun triggerHideController() = run {
handler.removeCallbacks(controllerVisibilityRunnable)
Expand All @@ -88,13 +90,17 @@ fun PlayerController(
super.onPlayerError(exception)
error = exception.message
controllerVisible = true
player.prepare() //TODO Auto Retry Toggle
}

override fun onMediaItemTransition(mediaItem: MediaItem?, reason: Int) {
super.onMediaItemTransition(mediaItem, reason)
mediaItem?.let { mediaMetadata = it.mediaMetadata }
}
}

player.addListener(listener)
onDispose {
player.removeListener(listener)
}
onDispose { player.removeListener(listener) }
}


Expand Down Expand Up @@ -142,23 +148,26 @@ fun PlayerController(
.onFocusEvent {
isButtonFocused = it.hasFocus
if (!it.hasFocus) triggerHideController()
},
verticalAlignment = Alignment.CenterVertically
}, verticalAlignment = Alignment.CenterVertically
) {
if (!isRunningOnTV) ExtraControls(backHandler)

Spacer(modifier = Modifier.weight(1f))

FilledTonalIconButton(onClick = { player.seekToPreviousMediaItem() }) {
FilledTonalIconButton(enabled = player.hasPreviousMediaItem(),
onClick = { player.seekToPreviousMediaItem() }) {
Icon(Icons.Rounded.SkipPrevious, contentDescription = null)
}

ChannelInfo(player.mediaMetadata)
ChannelInfo(mediaMetadata)

FilledTonalIconButton(onClick = { player.seekToNextMediaItem() }) {
FilledTonalIconButton(enabled = player.hasNextMediaItem(),
onClick = { player.seekToNextMediaItem() }) {
Icon(Icons.Rounded.SkipNext, contentDescription = null)
}
}


}
}
}
Expand Down

0 comments on commit 54ef84f

Please sign in to comment.