This commit is contained in:
greenart7c3
2023-09-27 10:48:53 -03:00
11 changed files with 152 additions and 90 deletions
@@ -12,7 +12,7 @@ import java.io.File
@UnstableApi object VideoCache {
var exoPlayerCacheSize: Long = 90 * 1024 * 1024 // 90MB
var exoPlayerCacheSize: Long = 150 * 1024 * 1024 // 90MB
var leastRecentlyUsedCacheEvictor = LeastRecentlyUsedCacheEvictor(exoPlayerCacheSize)
@@ -214,7 +214,10 @@ fun NewPostView(
PostButton(
onPost = {
postViewModel.sendPost(relayList = relayList)
onClose()
scope.launch {
delay(100)
onClose()
}
},
isActive = postViewModel.canPost()
)
@@ -224,7 +227,10 @@ fun NewPostView(
Spacer(modifier = StdHorzSpacer)
CloseButton(onPress = {
postViewModel.cancel()
onClose()
scope.launch {
delay(100)
onClose()
}
})
},
backgroundColor = MaterialTheme.colors.surface,
@@ -1220,9 +1226,7 @@ private fun MarkAsSensitive(
@Composable
fun CloseButton(onPress: () -> Unit) {
Button(
onClick = {
onPress()
},
onClick = onPress,
shape = ButtonBorder,
colors = ButtonDefaults
.buttonColors(
@@ -1,5 +1,6 @@
package com.vitorpamplona.amethyst.ui.components
import android.content.Context
import android.graphics.Rect
import android.graphics.drawable.Drawable
import android.net.Uri
@@ -206,34 +207,36 @@ fun VideoViewInner(
)
}
val mediaItem = remember(videoUri) {
MediaItem.Builder()
.setMediaId(videoUri)
.setUri(videoUri)
.setMediaMetadata(
MediaMetadata.Builder()
.setArtist(authorName?.ifBlank { null })
.setTitle(title?.ifBlank { null } ?: videoUri)
.setArtworkUri(
try {
if (artworkUri != null) {
Uri.parse(artworkUri)
} else {
null
}
} catch (e: Exception) {
null
}
)
.build()
)
.build()
}
if (!automaticallyStartPlayback.value) {
ImageUrlWithDownloadButton(url = videoUri, showImage = automaticallyStartPlayback)
} else {
VideoPlayerActiveMutex(videoUri) { activeOnScreen ->
val mediaItem = remember(videoUri) {
mutableStateOf(
MediaItem.Builder()
.setMediaId(videoUri)
.setUri(videoUri)
.setMediaMetadata(
MediaMetadata.Builder()
.setArtist(authorName?.ifBlank { null })
.setTitle(title?.ifBlank { null } ?: videoUri)
.setArtworkUri(
try {
if (artworkUri != null) {
Uri.parse(artworkUri)
} else {
null
}
} catch (e: Exception) {
null
}
)
.build()
)
.build()
)
}
GetVideoController(
mediaItem = mediaItem,
videoUri = videoUri,
@@ -258,7 +261,7 @@ fun VideoViewInner(
@Composable
@OptIn(UnstableApi::class)
fun GetVideoController(
mediaItem: MediaItem,
mediaItem: MutableState<MediaItem>,
videoUri: String,
defaultToStart: Boolean = false,
nostrUriCallback: String? = null,
@@ -288,14 +291,16 @@ fun GetVideoController(
DisposableEffect(key1 = videoUri) {
// If it is not null, the user might have come back from a playing video, like clicking on
// the notification of the video player.
scope.launch(Dispatchers.IO) {
if (controller.value == null) {
if (controller.value == null) {
scope.launch(Dispatchers.IO) {
PlaybackClientController.prepareController(
uid,
videoUri,
nostrUriCallback,
context
) {
// REQUIRED TO BE RUN IN THE MAIN THREAD
// checks again because of race conditions.
if (controller.value == null) { // still prone to race conditions.
controller.value = it
@@ -311,7 +316,7 @@ fun GetVideoController(
}
}
controller.value?.setMediaItem(mediaItem)
controller.value?.setMediaItem(mediaItem.value)
controller.value?.prepare()
} else if (controller.value != it) {
// discards the new controller because there is an existing one
@@ -329,27 +334,27 @@ fun GetVideoController(
it.volume = if (defaultToStart) 0f else 1f
}
it.setMediaItem(mediaItem)
it.setMediaItem(mediaItem.value)
it.prepare()
}
}
}
}
} else {
controller.value?.let {
if (it.playbackState == Player.STATE_IDLE || it.playbackState == Player.STATE_ENDED) {
if (it.isPlaying) {
// There is a video playing, start this one on mute.
it.volume = 0f
} else {
// There is no other video playing. Use the default mute state to
// decide if sound is on or not.
it.volume = if (defaultToStart) 0f else 1f
}
it.setMediaItem(mediaItem)
it.prepare()
}
} else {
controller.value?.let {
if (it.playbackState == Player.STATE_IDLE || it.playbackState == Player.STATE_ENDED) {
if (it.isPlaying) {
// There is a video playing, start this one on mute.
it.volume = 0f
} else {
// There is no other video playing. Use the default mute state to
// decide if sound is on or not.
it.volume = if (defaultToStart) 0f else 1f
}
it.setMediaItem(mediaItem.value)
it.prepare()
}
}
}
@@ -379,6 +384,8 @@ fun GetVideoController(
nostrUriCallback,
context
) {
// REQUIRED TO BE RUN IN THE MAIN THREAD
// checks again to make sure no other thread has created a controller.
if (controller.value == null) {
controller.value = it
@@ -394,7 +401,7 @@ fun GetVideoController(
}
}
controller.value?.setMediaItem(mediaItem)
controller.value?.setMediaItem(mediaItem.value)
controller.value?.prepare()
} else if (controller.value != it) {
// discards the new controller because there is an existing one
@@ -460,7 +467,7 @@ fun VideoPlayerActiveMutex(videoUri: String, inner: @Composable (MutableState<Bo
}
}
Box(
val myModifier = remember(videoUri) {
Modifier
.fillMaxWidth()
.defaultMinSize(minHeight = 70.dp)
@@ -489,7 +496,9 @@ fun VideoPlayerActiveMutex(videoUri: String, inner: @Composable (MutableState<Bo
}
}
}
) {
}
Box(modifier = myModifier) {
inner(active)
}
}
@@ -511,8 +520,6 @@ private fun RenderVideoPlayer(
activeOnScreen: MutableState<Boolean>,
onDialog: ((Boolean) -> Unit)?
) {
val context = LocalContext.current
ControlWhenPlayerIsActive(controller, keepPlaying, automaticallyStartPlayback, activeOnScreen)
val controllerVisible = remember(controller) {
@@ -520,9 +527,11 @@ private fun RenderVideoPlayer(
}
BoxWithConstraints() {
AndroidView(
modifier = if (roundedCorner) {
MaterialTheme.colors.imageModifier
val borders = MaterialTheme.colors.imageModifier
val myModifier = remember {
if (roundedCorner) {
borders
.defaultMinSize(minHeight = 100.dp)
.align(Alignment.Center)
} else {
@@ -530,8 +539,11 @@ private fun RenderVideoPlayer(
.fillMaxWidth()
.defaultMinSize(minHeight = 100.dp)
.align(Alignment.Center)
},
factory = {
}
}
val factory = remember(controller) {
{ context: Context ->
PlayerView(context).apply {
player = controller
layoutParams = FrameLayout.LayoutParams(
@@ -556,10 +568,15 @@ private fun RenderVideoPlayer(
)
}
}
}
AndroidView(
modifier = myModifier,
factory = factory
)
waveform?.let {
Waveform(it, controller, Modifier.align(Alignment.Center))
Waveform(it, controller, remember { Modifier.align(Alignment.Center) })
}
val startingMuteState = remember(controller) {
@@ -580,7 +597,7 @@ private fun RenderVideoPlayer(
controller.volume = if (mute) 0f else 1f
}
KeepPlayingButton(keepPlaying, controllerVisible, Modifier.align(Alignment.TopEnd)) { newKeepPlaying: Boolean ->
KeepPlayingButton(keepPlaying, controllerVisible, remember { Modifier.align(Alignment.TopEnd) }) { newKeepPlaying: Boolean ->
// If something else is playing and the user marks this video to keep playing, stops the other one.
if (newKeepPlaying) {
if (keepPlayingMutex != null && keepPlayingMutex != controller) {
@@ -840,34 +840,17 @@ private fun CheckNewAndRenderNote(
val backgroundColor = remember { mutableStateOf<Color>(defaultBackgroundColor) }
LaunchedEffect(key1 = routeForLastRead, key2 = parentBackgroundColor?.value) {
launch(Dispatchers.IO) {
routeForLastRead?.let {
val lastTime = accountViewModel.account.loadLastRead(it)
val createdAt = baseNote.createdAt()
if (createdAt != null) {
accountViewModel.account.markAsRead(it, createdAt)
val isNew = createdAt > lastTime
val newBackgroundColor = if (isNew) {
if (parentBackgroundColor != null) {
newItemColor.compositeOver(parentBackgroundColor.value)
} else {
newItemColor.compositeOver(defaultBackgroundColor)
}
routeForLastRead?.let {
accountViewModel.loadAndMarkAsRead(it, baseNote.createdAt()) { isNew ->
val newBackgroundColor = if (isNew) {
if (parentBackgroundColor != null) {
newItemColor.compositeOver(parentBackgroundColor.value)
} else {
parentBackgroundColor?.value ?: defaultBackgroundColor
}
if (newBackgroundColor != backgroundColor.value) {
launch(Dispatchers.Main) {
backgroundColor.value = newBackgroundColor
}
newItemColor.compositeOver(defaultBackgroundColor)
}
} else {
parentBackgroundColor?.value ?: defaultBackgroundColor
}
} ?: run {
val newBackgroundColor = parentBackgroundColor?.value ?: defaultBackgroundColor
if (newBackgroundColor != backgroundColor.value) {
launch(Dispatchers.Main) {
@@ -875,6 +858,14 @@ private fun CheckNewAndRenderNote(
}
}
}
} ?: run {
val newBackgroundColor = parentBackgroundColor?.value ?: defaultBackgroundColor
if (newBackgroundColor != backgroundColor.value) {
launch(Dispatchers.Main) {
backgroundColor.value = newBackgroundColor
}
}
}
}
@@ -63,6 +63,7 @@ class NostrChatroomFeedViewModel(val user: ChatroomKey, val account: Account) :
}
}
@Stable
class NostrVideoFeedViewModel(val account: Account) : FeedViewModel(VideoFeedFilter(account)) {
class Factory(val account: Account) : ViewModelProvider.Factory {
override fun <NostrVideoFeedViewModel : ViewModel> create(modelClass: Class<NostrVideoFeedViewModel>): NostrVideoFeedViewModel {
@@ -726,6 +726,19 @@ class AccountViewModel(val account: Account) : ViewModel(), Dao {
}
}
fun loadAndMarkAsRead(routeForLastRead: String, baseNoteCreatedAt: Long?, onIsNew: (Boolean) -> Unit) {
viewModelScope.launch(Dispatchers.IO) {
val lastTime = account.loadLastRead(routeForLastRead)
if (baseNoteCreatedAt != null) {
account.markAsRead(routeForLastRead, baseNoteCreatedAt)
onIsNew(baseNoteCreatedAt > lastTime)
} else {
onIsNew(false)
}
}
}
class Factory(val account: Account) : ViewModelProvider.Factory {
override fun <AccountViewModel : ViewModel> create(modelClass: Class<AccountViewModel>): AccountViewModel {
return AccountViewModel(account) as AccountViewModel
@@ -229,7 +229,7 @@ fun SlidingCarousel(
VerticalPager(
state = pagerState,
beyondBoundsPageCount = 1,
modifier = Modifier.fillMaxSize(1f),
modifier = Modifier.fillMaxSize(),
key = { index ->
feed.value.getOrNull(index)?.idHex ?: "$index"
}
@@ -246,7 +246,7 @@ fun LoadedVideoCompose(
accountViewModel: AccountViewModel,
nav: (String) -> Unit
) {
var state by remember {
var state by remember(note) {
mutableStateOf(
AccountViewModel.NoteComposeReportState()
)