Skip to content
Open
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
2 changes: 2 additions & 0 deletions app/src/main/kotlin/com/wisp/app/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ class MainActivity : FragmentActivity() {
mutableStateOf(MediaSettings(
autoLoadMedia = interfacePrefs.isAutoLoadMedia(),
videoAutoPlay = interfacePrefs.isVideoAutoPlay(),
videoLoop = interfacePrefs.isVideoLoop(),
mediaLayoutStyle = interfacePrefs.getMediaLayoutStyle()
))
}
Expand Down Expand Up @@ -97,6 +98,7 @@ class MainActivity : FragmentActivity() {
mediaSettings = MediaSettings(
autoLoadMedia = interfacePrefs.isAutoLoadMedia(),
videoAutoPlay = interfacePrefs.isVideoAutoPlay(),
videoLoop = interfacePrefs.isVideoLoop(),
mediaLayoutStyle = interfacePrefs.getMediaLayoutStyle()
)
}
Expand Down
5 changes: 5 additions & 0 deletions app/src/main/kotlin/com/wisp/app/repo/InterfacePreferences.kt
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ class InterfacePreferences(context: Context) {
fun isVideoAutoPlay(): Boolean = prefs.getBoolean("video_auto_play", true)
fun setVideoAutoPlay(enabled: Boolean) = prefs.edit().putBoolean("video_auto_play", enabled).apply()

// Loop videos in the timeline and full-screen gallery. Not yet
// round-tripped over NIP-78 to match iOS, which carries the same TODO.
fun isVideoLoop(): Boolean = prefs.getBoolean("video_loop", true)
fun setVideoLoop(enabled: Boolean) = prefs.edit().putBoolean("video_loop", enabled).apply()

fun getMediaLayoutStyle(): MediaLayoutStyle =
MediaLayoutStyle.fromKey(prefs.getString("media_layout_style", null))
fun setMediaLayoutStyle(style: MediaLayoutStyle) =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,13 @@ import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat
import androidx.media3.common.MediaItem
import androidx.media3.common.Player
import androidx.media3.common.util.UnstableApi
import androidx.media3.exoplayer.ExoPlayer
import androidx.media3.ui.PlayerView
import com.wisp.app.R
import com.wisp.app.relay.HttpClientFactory
import com.wisp.app.repo.InterfacePreferences
import com.wisp.app.util.MediaDownloader
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
Expand Down Expand Up @@ -64,12 +66,15 @@ fun FullScreenVideoPlayer(
DisposableEffect(videoUrl) {
val activity = context.findActivity() ?: return@DisposableEffect onDispose {}

val loopEnabled = InterfacePreferences(context).isVideoLoop()
val ownsPlayer = existingPlayer == null
val exoPlayer = existingPlayer ?: HttpClientFactory.createExoPlayer(context).apply {
val exoPlayer = (existingPlayer ?: HttpClientFactory.createExoPlayer(context).apply {
setMediaItem(MediaItem.fromUri(Uri.parse(videoUrl)))
prepare()
seekTo(startPositionMs)
playWhenReady = true
}).apply {
repeatMode = if (loopEnabled) Player.REPEAT_MODE_ONE else Player.REPEAT_MODE_OFF
}

var minimizedToPip = false
Expand Down
15 changes: 13 additions & 2 deletions app/src/main/kotlin/com/wisp/app/ui/component/RichContent.kt
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ private const val INLINE_CONTENT_TAG = "androidx.compose.foundation.text.inlineC
data class MediaSettings(
val autoLoadMedia: Boolean = true,
val videoAutoPlay: Boolean = true,
val videoLoop: Boolean = true,
val mediaLayoutStyle: com.wisp.app.repo.InterfacePreferences.MediaLayoutStyle =
com.wisp.app.repo.InterfacePreferences.MediaLayoutStyle.GALLERY
)
Expand Down Expand Up @@ -2310,7 +2311,7 @@ internal fun InlineVideoPlayerWithFullscreen(meta: MediaMeta, onFullScreen: (pos
volume = if (globalMuted.value) 0f else 1f
playWhenReady = false
}).apply {
repeatMode = Player.REPEAT_MODE_ONE
repeatMode = if (mediaSettings.videoLoop) Player.REPEAT_MODE_ONE else Player.REPEAT_MODE_OFF
}
}

Expand All @@ -2319,6 +2320,11 @@ internal fun InlineVideoPlayerWithFullscreen(meta: MediaMeta, onFullScreen: (pos
exoPlayer.volume = if (isMuted) 0f else 1f
}

LaunchedEffect(mediaSettings.videoLoop) {
exoPlayer.repeatMode =
if (mediaSettings.videoLoop) Player.REPEAT_MODE_ONE else Player.REPEAT_MODE_OFF
}

DisposableEffect(url) {
val listener = object : Player.Listener {
override fun onVideoSizeChanged(videoSize: VideoSize) {
Expand Down Expand Up @@ -2589,14 +2595,19 @@ private fun InlineVideoPlayer(url: String, modifier: Modifier = Modifier) {
volume = if (globalMuted.value) 0f else 1f
playWhenReady = false
}).apply {
repeatMode = Player.REPEAT_MODE_ONE
repeatMode = if (mediaSettings.videoLoop) Player.REPEAT_MODE_ONE else Player.REPEAT_MODE_OFF
}
}

LaunchedEffect(isMuted) {
exoPlayer.volume = if (isMuted) 0f else 1f
}

LaunchedEffect(mediaSettings.videoLoop) {
exoPlayer.repeatMode =
if (mediaSettings.videoLoop) Player.REPEAT_MODE_ONE else Player.REPEAT_MODE_OFF
}

DisposableEffect(url) {
val listener = object : Player.Listener {
override fun onVideoSizeChanged(videoSize: VideoSize) {
Expand Down
24 changes: 24 additions & 0 deletions app/src/main/kotlin/com/wisp/app/ui/screen/InterfaceScreen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ fun InterfaceScreen(
var clientTagEnabled by remember { mutableStateOf(interfacePrefs.isClientTagEnabled()) }
var autoLoadMedia by remember { mutableStateOf(interfacePrefs.isAutoLoadMedia()) }
var videoAutoPlay by remember { mutableStateOf(interfacePrefs.isVideoAutoPlay()) }
var videoLoop by remember { mutableStateOf(interfacePrefs.isVideoLoop()) }
var mediaLayout by remember { mutableStateOf(interfacePrefs.getMediaLayoutStyle()) }
var liveStreamsHidden by remember { mutableStateOf(interfacePrefs.isLiveStreamsHidden()) }
var autoTranslate by remember { mutableStateOf(interfacePrefs.isAutoTranslate()) }
Expand Down Expand Up @@ -463,6 +464,29 @@ fun InterfaceScreen(
)
}
Spacer(Modifier.height(12.dp))
Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically
) {
Column(modifier = Modifier.weight(1f)) {
Text(stringResource(R.string.settings_video_loop), style = MaterialTheme.typography.bodyMedium)
Text(
stringResource(R.string.settings_video_loop_description),
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
Switch(
checked = videoLoop,
onCheckedChange = {
videoLoop = it
interfacePrefs.setVideoLoop(it)
onChanged()
},
colors = wispSwitchColors()
)
}
Spacer(Modifier.height(12.dp))
Column(modifier = Modifier.fillMaxWidth()) {
Text(stringResource(R.string.settings_media_layout), style = MaterialTheme.typography.bodyMedium)
Text(
Expand Down
2 changes: 2 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -533,6 +533,8 @@
<string name="settings_auto_load_description">Automatically download images and videos in notes. When off, tap to load.</string>
<string name="settings_video_autoplay">Video autoplay</string>
<string name="settings_video_autoplay_description">Automatically play videos when they scroll into view</string>
<string name="settings_video_loop">Loop videos</string>
<string name="settings_video_loop_description">Replays timeline and gallery videos from the beginning when they finish.</string>
<string name="settings_media_layout">Multi-image layout</string>
<string name="settings_media_layout_description">Gallery: horizontal swipe through every photo and video. Stack: each item full-width below the next.</string>
<string name="settings_media_layout_gallery">Gallery</string>
Expand Down