I can’t understand why the view is not updated
@Composable
@androidx.annotation.OptIn(androidx.media3.common.util.UnstableApi::class)
fun VideoPlayer(
screenHeight: Dp, screenWidth: Dp, attachment: MediaAttachment, feedViewModel: FeedViewModel
) {
val context = LocalContext.current
val currentSource by feedViewModel.currentSource.observeAsState(attachment.Video)
Toast.makeText(context, currentSource.toString(), Toast.LENGTH_SHORT).show()
var exoPlayer = remember(currentSource) {
ExoPlayer.Builder(context).build().apply {
val defaultDataSourceFactory = DefaultDataSource.Factory(context)
val dataSourceFactory: DataSource.Factory = DefaultDataSource.Factory(
context, defaultDataSourceFactory
)
val source = ProgressiveMediaSource.Factory(dataSourceFactory)
.createMediaSource(MediaItem.fromUri("https://senses.team/$currentSource"))
setMediaSource(source)
prepare()
}
}
exoPlayer.playWhenReady = true
exoPlayer.videoScalingMode = C.VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING
exoPlayer.repeatMode = Player.REPEAT_MODE_ONE
DisposableEffect(
AndroidView(modifier = Modifier
.height(screenHeight)
.width(screenWidth)
.background(Color.Black),
factory = {
CustomExoPlayerView(context).apply {
hideController()
useController = false
resizeMode = AspectRatioFrameLayout.RESIZE_MODE_FIT
player = exoPlayer
layoutParams = FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)
}
})
) {
onDispose { exoPlayer.release() }
}
}
@UnstableApi
internal class CustomExoPlayerView(
context: Context, attributeSet: AttributeSet? = null,
) : PlayerView(context, attributeSet) {
@SuppressLint("ClickableViewAccessibility")
override fun onTouchEvent(event: MotionEvent): Boolean {
when (event.action) {
MotionEvent.ACTION_DOWN -\> {
showController()
}
}
return false
}
}
my ViewModel for storage current video source
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.android.senses.structs.Sense
import com.android.senses.ui.navigation.screens.home.ListOfSenses
class FeedViewModel : ViewModel() {
private val _listOfSenses = MutableLiveData<List<Sense>>(ListOfSenses)
val listOfSenses: MutableLiveData<List<Sense>> = _listOfSenses
private val _currentSource = MutableLiveData<String>()
val currentSource: LiveData<String> = _currentSource
fun setCurrentSource(source: String) {
_currentSource.value = source
}
}
and there is a lazy list that, when clicked, changes the link in the viewmodel
package com.android.senses.ui.navigation.screens.home
import FeedViewModel
import android.annotation.SuppressLint
import android.content.Context
import android.widget.Toast
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
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.draw.alpha
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.constraintlayout.compose.ConstraintLayout
import androidx.navigation.NavController
import com.android.senses.R
import com.android.senses.structs.Sense
import com.android.senses.ui.LoadImageWithGlide
import com.android.senses.ui.PreloadImageWithGlide
import com.android.senses.ui.VideoPlayer
@SuppressLint(
"UnusedMaterial3ScaffoldPaddingParameter", "CoroutineCreationDuringComposition",
"UnrememberedMutableState"
)
@Composable
fun HomeScreen(
navController: NavController,
navControllerMain: NavController
) {
val screenHeight = LocalConfiguration.current.screenHeightDp.dp
val screenWidth = LocalConfiguration.current.screenWidthDp.dp
val feedViewModel = FeedViewModel()
val listOfSenses by feedViewModel.listOfSenses.observeAsState(emptyList())
var currentSense by remember { mutableStateOf(listOfSenses.get(0)) }
var currentAttachmentIndex by remember { mutableStateOf(0) }
val scrollState = rememberLazyListState()
LazyColumn(
state = scrollState,
modifier = Modifier
.fillMaxSize()
) {
itemsIndexed(items = listOfSenses, key = { _, currentSense -> currentSense.ID })
{ index, sense ->
PostCard(
context = LocalContext.current,
screenHeight = screenHeight,
screenWidth = screenWidth,
viewModel = feedViewModel,
sense = sense,
feedViewModel= feedViewModel
)
}
}
}
@androidx.annotation.OptIn(androidx.media3.common.util.UnstableApi::class)
@Composable
fun PostCard(
context: Context,
screenHeight: Dp,
screenWidth: Dp,
viewModel: FeedViewModel,
sense: Sense,
feedViewModel: FeedViewModel
) {
var currentAttachmentIndex by remember { mutableStateOf(0) }
val gradientColors = listOf(
Color(0xFF000000),
Color.Transparent,
)
val gradientBrush = Brush.verticalGradient(gradientColors)
val reverseGradientBrush = Brush.verticalGradient(gradientColors.reversed())
ConstraintLayout(
modifier = Modifier.fillMaxSize()
) {
val (
containerGradient1,
containerGradient2,
commentContainer,
reactionContainer
) = createRefs()
Box(
modifier = Modifier
.fillMaxSize()
.clickable {
currentAttachmentIndex = (currentAttachmentIndex + 1) % sense.Attachments.size
feedViewModel.setCurrentSource(sense.Attachments.get(currentAttachmentIndex).Video)
Toast
.makeText(context, currentAttachmentIndex.toString(), Toast.LENGTH_SHORT)
.show()
}
) {
if (sense.Attachments.get(currentAttachmentIndex)?.Type != 1) {
LoadImageWithGlide(
imageUrl = sense.Attachments.get(currentAttachmentIndex).Photo,
screenHeight = screenHeight,
screenWidth = screenWidth,
viewModel = viewModel
)
VideoPlayer(
screenHeight = screenHeight,
screenWidth = screenWidth,
attachment = sense.Attachments.get(currentAttachmentIndex),
feedViewModel= feedViewModel
)
} else {
LoadImageWithGlide(
imageUrl = sense.Attachments.get(currentAttachmentIndex).Photo,
screenHeight = screenHeight,
screenWidth = screenWidth,
viewModel = viewModel
)
}
sense.Attachments.forEach {
PreloadImageWithGlide(imageUrl = it.Photo, context = LocalContext.current)
}
}
//gradient top section
Column(
modifier = Modifier
.constrainAs(containerGradient1) {
top.linkTo(parent.top)
}
.height(200.dp)
.fillMaxWidth()
.background(brush = gradientBrush)
.alpha(0.2f)
) {}
// head section (title, create sense, messanger and profile image)
Column(
modifier = Modifier.fillMaxSize(),
) {
Row(
modifier = Modifier
.fillMaxWidth()
.fillMaxHeight(0.08f)
.padding(start = 10.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceAround,
) {
Text(
text = LocalContext.current.getString(R.string.home_screen_title),
color = Color.White,
fontSize = 30.sp,
style = MaterialTheme.typography.titleLarge
)
Spacer(modifier = Modifier.width(150.dp))
Row(
horizontalArrangement = Arrangement.SpaceBetween
) {
IconButton(onClick = { /*TODO*/ }) {
Icon(
painter = painterResource(id = R.drawable.add_icon),
contentDescription = "add",
tint = Color.White,
)
}
IconButton(
onClick = { /*TODO*/ },
) {
Icon(
painter = painterResource(id = R.drawable.send_message_icon),
contentDescription = "send message",
tint = Color.White,
)
}
}
}
Row(
modifier = Modifier.padding(start = 15.dp, top = 10.dp),
horizontalArrangement = Arrangement.SpaceBetween
) {
Box(
modifier = Modifier
.size(40.dp)
.clip(CircleShape)
) {
LoadImageWithGlide(
sense.Author.avatarUrl,
screenHeight,
screenWidth,
viewModel
)
}
}
}
//reactions section
Column(
modifier = Modifier
.constrainAs(reactionContainer) {
bottom.linkTo(commentContainer.top, margin = 70.dp)
end.linkTo(parent.end, margin = 10.dp)
}
.padding(start = 10.dp)
) {
IconButton(onClick = { /*TODO*/ }) {
Icon(
painter = painterResource(id = R.drawable.comment_icon),
contentDescription = "comment",
tint = Color.White
)
}
IconButton(onClick = { /*TODO*/ }) {
Column(
horizontalAlignment = Alignment.CenterHorizontally
) {
Icon(
painter = painterResource(id = R.drawable.like_icon),
contentDescription = "like",
tint = if (sense.HasLike) Color.Red else Color.White
)
Text(
text = sense.Likes.toString(),
color = Color.White,
fontSize = 12.sp,
)
}
}
}
//gradient bottom
Column(
modifier = Modifier
.constrainAs(containerGradient2) {
bottom.linkTo(parent.bottom)
}
.height(200.dp)
.fillMaxWidth()
.background(brush = reverseGradientBrush)
.alpha(0.2f)
) {
}
//comment section
Row(
modifier = Modifier
.constrainAs(commentContainer) {
bottom.linkTo(parent.bottom)
}
.padding(start = 10.dp, end = 10.dp)
.clickable { }
) {
Column(
modifier = Modifier
.fillMaxWidth(0.9f)
.height(150.dp)
.clickable { }
) {
Text(
text = sense.Text,
color = Color.White,
fontSize = 15.sp,
maxLines = 3
)
}
Column(
modifier = Modifier
.fillMaxWidth()
.size(15.dp)
.clickable { }
) {
IconButton(onClick = { /*TODO*/ }) {
Icon(
painter = painterResource(
id = R.drawable.dots_icon
),
contentDescription = "more",
tint = Color.White,
)
}
}
}
}
}
I tried to use remember state, LaunchedEffect, recreate the player, but the view did not change
I still can’t figure out how to create buttons, for example, for pause and process its clicks, because the player intercepts clicks from the main view
Please help