Skip to content

Commit f42f392

Browse files
authored
CMM-972 authenticate media requests with wp.com credentials (#22359)
* Add support for fullscreen images * Supporting thumbnails authentication * Some refactoring to avoid propagate the accountStore or the token * Downloading and playing video * Removing VideoUrlResolver * Code simplification and refactor * Adding new tests * Detekt * Properly handling video download errors * Removing authHeader from function arguments * Caching filepaths instead of Files * Skipping multiple authHeader calls on recomposition * Avoid creating function multiple times in composition * detekt * Fixing sonarqube warning * Reduce header recomposition in imagepreview
1 parent 89633eb commit f42f392

File tree

9 files changed

+554
-129
lines changed

9 files changed

+554
-129
lines changed
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package org.wordpress.android.support.he.model
2+
3+
import java.io.File
4+
5+
sealed class VideoDownloadState {
6+
object Idle : VideoDownloadState()
7+
object Downloading : VideoDownloadState()
8+
object Error : VideoDownloadState()
9+
data class Success(val file: File) : VideoDownloadState()
10+
}

WordPress/src/main/java/org/wordpress/android/support/he/ui/AttachmentFullscreenImagePreview.kt

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,18 +40,23 @@ import androidx.compose.ui.window.DialogProperties
4040
import coil.compose.SubcomposeAsyncImage
4141
import coil.request.ImageRequest
4242
import org.wordpress.android.R
43+
import org.wordpress.android.support.he.ui.HESupportActivity.Companion.AUTHORIZATION_TAG
4344
import org.wordpress.android.ui.compose.theme.AppThemeM3
4445

4546
@Composable
4647
fun AttachmentFullscreenImagePreview(
4748
imageUrl: String,
49+
onGetAuthorizationHeaderArgument: () -> String,
4850
onDismiss: () -> Unit,
49-
onDownload: () -> Unit = {}
51+
onDownload: () -> Unit = {},
5052
) {
5153
var scale by remember { mutableFloatStateOf(1f) }
5254
var offsetX by remember { mutableFloatStateOf(0f) }
5355
var offsetY by remember { mutableFloatStateOf(0f) }
5456

57+
// Cache authorization header to avoid repeated function calls during composition
58+
val authorizationHeader = remember { onGetAuthorizationHeaderArgument() }
59+
5560
// Load semantics
5661
val loadingImageDescription = stringResource(R.string.he_support_loading_image)
5762
val attachmentImageDescription = stringResource(R.string.he_support_attachment_image)
@@ -90,6 +95,9 @@ fun AttachmentFullscreenImagePreview(
9095
model = ImageRequest.Builder(LocalContext.current)
9196
.data(imageUrl)
9297
.crossfade(true)
98+
.apply {
99+
addHeader(AUTHORIZATION_TAG, authorizationHeader)
100+
}
93101
.build(),
94102
contentDescription = attachmentImageDescription,
95103
modifier = Modifier
@@ -181,6 +189,7 @@ private fun AttachmentFullscreenImagePreviewPreview() {
181189
AppThemeM3(isDarkTheme = false) {
182190
AttachmentFullscreenImagePreview(
183191
imageUrl = "https://via.placeholder.com/800x600",
192+
onGetAuthorizationHeaderArgument = { "" },
184193
onDismiss = { },
185194
onDownload = { }
186195
)
@@ -193,6 +202,7 @@ private fun AttachmentFullscreenImagePreviewPreviewDark() {
193202
AppThemeM3(isDarkTheme = true) {
194203
AttachmentFullscreenImagePreview(
195204
imageUrl = "https://via.placeholder.com/800x600",
205+
onGetAuthorizationHeaderArgument = { "" },
196206
onDismiss = { },
197207
onDownload = { }
198208
)

WordPress/src/main/java/org/wordpress/android/support/he/ui/AttachmentFullscreenVideoPlayer.kt

Lines changed: 48 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package org.wordpress.android.support.he.ui
22

33
import android.view.ViewGroup
4-
import androidx.core.net.toUri
54
import android.widget.FrameLayout
65
import androidx.compose.foundation.background
76
import androidx.compose.foundation.layout.Arrangement
@@ -21,7 +20,7 @@ import androidx.compose.material3.Icon
2120
import androidx.compose.material3.IconButton
2221
import androidx.compose.material3.Text
2322
import androidx.compose.runtime.Composable
24-
import androidx.compose.runtime.DisposableEffect
23+
import androidx.compose.runtime.LaunchedEffect
2524
import androidx.compose.runtime.getValue
2625
import androidx.compose.runtime.mutableStateOf
2726
import androidx.compose.runtime.remember
@@ -37,67 +36,64 @@ import androidx.compose.ui.unit.dp
3736
import androidx.compose.ui.viewinterop.AndroidView
3837
import androidx.compose.ui.window.Dialog
3938
import androidx.compose.ui.window.DialogProperties
39+
import androidx.core.net.toUri
4040
import com.google.android.exoplayer2.MediaItem
4141
import com.google.android.exoplayer2.Player
4242
import com.google.android.exoplayer2.SimpleExoPlayer
4343
import com.google.android.exoplayer2.ui.PlayerView
4444
import org.wordpress.android.R
45-
import org.wordpress.android.support.he.util.VideoUrlResolver
45+
import org.wordpress.android.support.he.model.VideoDownloadState
46+
import org.wordpress.android.util.AppLog
47+
import java.io.File
4648

4749
@Composable
4850
fun AttachmentFullscreenVideoPlayer(
4951
videoUrl: String,
5052
onDismiss: () -> Unit,
5153
onDownload: () -> Unit = {},
52-
videoUrlResolver: VideoUrlResolver? = null
54+
downloadState: VideoDownloadState,
55+
onStartVideoDownload: (String) -> Unit,
56+
onResetVideoDownloadState: () -> Unit = {},
5357
) {
5458
val context = LocalContext.current
55-
var hasError by remember { mutableStateOf(false) }
56-
var resolvedUrl by remember { mutableStateOf<String?>(null) }
57-
var isResolving by remember { mutableStateOf(true) }
59+
var localVideoFile by remember { mutableStateOf<File?>(null) }
60+
61+
// Start download when composable is first launched
62+
LaunchedEffect(videoUrl) {
63+
onStartVideoDownload(videoUrl)
64+
}
5865

59-
// Resolve URL redirects before playing
60-
androidx.compose.runtime.LaunchedEffect(videoUrl) {
61-
if (videoUrlResolver != null) {
62-
resolvedUrl = videoUrlResolver.resolveUrl(videoUrl)
63-
} else {
64-
resolvedUrl = videoUrl
66+
// Update local file when download succeeds
67+
LaunchedEffect(downloadState) {
68+
if (downloadState is VideoDownloadState.Success) {
69+
localVideoFile = downloadState.file
6570
}
66-
isResolving = false
6771
}
6872

69-
val exoPlayer = remember(resolvedUrl) {
70-
// Don't create player until URL is resolved
71-
val url = resolvedUrl ?: return@remember null
73+
val exoPlayer = remember(localVideoFile) {
74+
// Don't create player until video is downloaded
75+
val file = localVideoFile ?: return@remember null
7276

7377
SimpleExoPlayer.Builder(context).build().apply {
74-
// Add error listener
78+
// Add error listener for logging
7579
addListener(object : Player.EventListener {
7680
override fun onPlayerError(error: com.google.android.exoplayer2.ExoPlaybackException) {
77-
hasError = true
81+
AppLog.e(AppLog.T.SUPPORT, "Video playback error", error)
7882
}
7983
})
8084

81-
// Simple configuration using MediaItem
82-
val mediaItem = MediaItem.fromUri(url.toUri())
85+
// Play from local file
86+
val mediaItem = MediaItem.fromUri(file.toUri())
8387
setMediaItem(mediaItem)
8488
prepare()
8589
playWhenReady = true
8690
repeatMode = Player.REPEAT_MODE_OFF
8791
}
8892
}
8993

90-
DisposableEffect(Unit) {
91-
onDispose {
92-
exoPlayer?.stop()
93-
exoPlayer?.release()
94-
}
95-
}
96-
9794
Dialog(
9895
onDismissRequest = {
99-
exoPlayer?.stop()
100-
onDismiss()
96+
closeFullScreen(exoPlayer, onDismiss, onResetVideoDownloadState)
10197
},
10298
properties = DialogProperties(
10399
usePlatformDefaultWidth = false,
@@ -110,15 +106,16 @@ fun AttachmentFullscreenVideoPlayer(
110106
.fillMaxSize()
111107
.background(Color.Black)
112108
) {
113-
when {
114-
isResolving -> {
115-
// Show loading indicator while resolving URL
109+
when (downloadState) {
110+
is VideoDownloadState.Idle,
111+
is VideoDownloadState.Downloading -> {
112+
// Show loading indicator while downloading video
116113
CircularProgressIndicator(
117114
modifier = Modifier.align(Alignment.Center),
118115
color = Color.White
119116
)
120117
}
121-
hasError -> {
118+
is VideoDownloadState.Error -> {
122119
// Show error message when video fails to load
123120
Column(
124121
modifier = Modifier
@@ -146,17 +143,16 @@ fun AttachmentFullscreenVideoPlayer(
146143
)
147144
Button(
148145
onClick = {
149-
exoPlayer?.stop()
150146
onDownload()
151-
onDismiss()
147+
closeFullScreen(exoPlayer, onDismiss, onResetVideoDownloadState)
152148
}
153149
) {
154150
Text(stringResource(R.string.he_support_download_video_button))
155151
}
156152
}
157153
}
158-
else -> {
159-
// Show video player when URL is resolved and no error
154+
is VideoDownloadState.Success -> {
155+
// Show video player when video is downloaded successfully
160156
exoPlayer?.let { player ->
161157
AndroidView(
162158
factory = { ctx ->
@@ -190,9 +186,8 @@ fun AttachmentFullscreenVideoPlayer(
190186
// Download button
191187
IconButton(
192188
onClick = {
193-
exoPlayer?.stop()
194-
onDownload.invoke()
195-
onDismiss.invoke()
189+
onDownload()
190+
closeFullScreen(exoPlayer, onDismiss, onResetVideoDownloadState)
196191
}
197192
) {
198193
Icon(
@@ -206,8 +201,7 @@ fun AttachmentFullscreenVideoPlayer(
206201
// Close button
207202
IconButton(
208203
onClick = {
209-
exoPlayer?.stop()
210-
onDismiss()
204+
closeFullScreen(exoPlayer, onDismiss, onResetVideoDownloadState)
211205
}
212206
) {
213207
Icon(
@@ -221,3 +215,14 @@ fun AttachmentFullscreenVideoPlayer(
221215
}
222216
}
223217
}
218+
219+
fun closeFullScreen(
220+
exoPlayer: SimpleExoPlayer?,
221+
onDismiss: () -> Unit,
222+
onResetVideoDownloadState: () -> Unit,
223+
) {
224+
exoPlayer?.stop()
225+
exoPlayer?.release()
226+
onDismiss()
227+
onResetVideoDownloadState()
228+
}

0 commit comments

Comments
 (0)