Skip to content

Commit eeb2b2f

Browse files
author
sentinelweb
committed
#487 start exo
1 parent 6aff73b commit eeb2b2f

File tree

7 files changed

+212
-20
lines changed

7 files changed

+212
-20
lines changed

app/build.gradle.kts

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ dependencies {
147147
implementation(libs.composeUiToolingPreview)
148148
implementation(libs.composeUiTooling)
149149
implementation(libs.androidx.palette.ktx)
150+
implementation(libs.androidx.activity.compose)
150151

151152
// mvikotlin
152153
debugImplementation(libs.mvikotlinAndroidDebug)
@@ -174,21 +175,16 @@ dependencies {
174175
implementation(libs.mediarouter)
175176
implementation(libs.media)
176177

178+
// exoplayer
179+
implementation(libs.exoplayer)
180+
177181
// firebase
178182
// implementation(platform(libs.firebaseBom))
179-
// implementation(libs.firebaseStorageKtx)
180-
// implementation(libs.firebaseCrashlyticsKtx)
181-
// implementation(libs.firebaseAnalyticsKtx)
182-
// implementation(libs.firebaseUiStorage)
183-
// FIREBASE
184-
// Import the BoM for the Firebase platform
185-
implementation(platform("com.google.firebase:firebase-bom:$ver_firebase_bom"))
186-
// Declare the dependency for the Cloud Storage library
187-
// When using the BoM, you don't specify versions in Firebase library dependencies
188-
implementation("com.google.firebase:firebase-storage-ktx")
189-
implementation("com.google.firebase:firebase-crashlytics-ktx")
190-
implementation("com.google.firebase:firebase-analytics-ktx")
191-
implementation("com.firebaseui:firebase-ui-storage:$ver_firebase_ui")
183+
implementation(project.dependencies.enforcedPlatform(libs.firebaseBom.get()))
184+
implementation(libs.firebaseStorageKtx)
185+
implementation(libs.firebaseCrashlyticsKtx)
186+
implementation(libs.firebaseAnalyticsKtx)
187+
implementation(libs.firebaseUiStorage)
192188

193189
// serialization
194190
implementation(libs.kotlinxSerializationCore)

app/src/main/AndroidManifest.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,12 @@
151151
android:screenOrientation="landscape"
152152
tools:ignore="LockedOrientationActivity" />
153153

154+
<activity
155+
android:name="uk.co.sentinelweb.cuer.app.ui.exoplayer.ExoPlayerActivity"
156+
android:label="@string/title_activity_fullscreen"
157+
android:screenOrientation="landscape"
158+
tools:ignore="LockedOrientationActivity" />
159+
154160
<service
155161
android:name="uk.co.sentinelweb.cuer.app.service.cast.CastService"
156162
android:exported="false">
Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
package uk.co.sentinelweb.cuer.app.ui.exoplayer
2+
3+
import android.net.Uri
4+
import android.os.Bundle
5+
import androidx.activity.ComponentActivity
6+
import androidx.activity.compose.setContent
7+
import androidx.compose.foundation.background
8+
import androidx.compose.foundation.layout.*
9+
import androidx.compose.material.Button
10+
import androidx.compose.material.Slider
11+
import androidx.compose.material.SliderDefaults
12+
import androidx.compose.material.Text
13+
import androidx.compose.runtime.*
14+
import androidx.compose.ui.Alignment.Companion.BottomCenter
15+
import androidx.compose.ui.Alignment.Companion.CenterHorizontally
16+
import androidx.compose.ui.Alignment.Companion.CenterVertically
17+
import androidx.compose.ui.Modifier
18+
import androidx.compose.ui.graphics.Color
19+
import androidx.compose.ui.platform.LocalContext
20+
import androidx.compose.ui.unit.dp
21+
import androidx.compose.ui.viewinterop.AndroidView
22+
import com.google.android.exoplayer2.ExoPlayer
23+
import com.google.android.exoplayer2.MediaItem
24+
import com.google.android.exoplayer2.ui.PlayerView
25+
import uk.co.sentinelweb.cuer.app.ui.common.compose.CuerSharedTheme
26+
27+
class ExoPlayerActivity : ComponentActivity() {
28+
override fun onCreate(savedInstanceState: Bundle?) {
29+
super.onCreate(savedInstanceState)
30+
setContent {
31+
CuerSharedTheme {
32+
ExoPlayerScreen()
33+
}
34+
}
35+
}
36+
}
37+
38+
@Composable
39+
fun ExoPlayerScreen() {
40+
var player by remember { mutableStateOf<ExoPlayer?>(null) }
41+
val context = LocalContext.current
42+
43+
DisposableEffect(Unit) {
44+
val exoPlayer = ExoPlayer.Builder(context).build().apply {
45+
val mediaItem =
46+
MediaItem.fromUri(Uri.parse("https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4"))
47+
setMediaItem(mediaItem)
48+
prepare()
49+
playWhenReady = true
50+
}
51+
player = exoPlayer
52+
onDispose {
53+
exoPlayer.release()
54+
}
55+
}
56+
57+
Box(
58+
modifier = Modifier
59+
.fillMaxSize()
60+
.background(Color.Black)
61+
) {
62+
if (player != null) {
63+
AndroidView(
64+
factory = {
65+
PlayerView(context).apply {
66+
this.player = player
67+
this.useController = false
68+
}
69+
},
70+
modifier = Modifier
71+
.fillMaxWidth()
72+
.aspectRatio(16 / 9f)
73+
)
74+
PlayerControls(player!!)
75+
}
76+
val title = "Video Title" // Replace with actual title if available
77+
// Text(
78+
// text = title,
79+
// color = Color.White,
80+
// modifier = Modifier
81+
// .padding(16.dp)
82+
// .height(20.dp)
83+
// .background(Color.Transparent)
84+
// )
85+
}
86+
}
87+
88+
@Composable
89+
fun BoxScope.PlayerControls(player: ExoPlayer) {
90+
var playWhenReady by remember { mutableStateOf(player.playWhenReady) }
91+
var playbackPosition by remember { mutableStateOf(player.currentPosition) }
92+
var bufferedPosition by remember { mutableStateOf(player.bufferedPosition) }
93+
var duration by remember { mutableStateOf(player.duration) }
94+
95+
LaunchedEffect(player) {
96+
while (true) {
97+
playbackPosition = player.currentPosition
98+
bufferedPosition = player.bufferedPosition
99+
duration = player.duration
100+
playWhenReady = player.isPlaying
101+
kotlinx.coroutines.delay(1000)
102+
}
103+
}
104+
105+
Column(
106+
horizontalAlignment = CenterHorizontally,
107+
modifier = Modifier
108+
.fillMaxWidth()
109+
.height(80.dp)
110+
// .background(Color.DarkGray)
111+
.align(BottomCenter)
112+
) {
113+
Row(
114+
horizontalArrangement = Arrangement.SpaceEvenly,
115+
verticalAlignment = CenterVertically,
116+
modifier = Modifier
117+
.fillMaxWidth()
118+
.height(60.dp)
119+
.padding(16.dp)
120+
) {
121+
Button(
122+
onClick = { player.seekBack() },
123+
modifier = Modifier.height(40.dp)
124+
) { Text("<< 10s") }
125+
126+
Button(
127+
onClick = {
128+
playWhenReady = !playWhenReady
129+
player.playWhenReady = playWhenReady
130+
},
131+
modifier = Modifier.height(40.dp)
132+
) { Text(if (playWhenReady) "Pause" else "Play") }
133+
134+
Button(onClick = { player.seekForward() }) {
135+
Text("10s >>")
136+
}
137+
138+
Button(
139+
onClick = {
140+
player.stop()
141+
player.seekTo(0)
142+
playWhenReady = false
143+
},
144+
modifier = Modifier.height(40.dp)
145+
) { Text("Stop") }
146+
}
147+
148+
Slider(
149+
value = playbackPosition.toFloat(),
150+
onValueChange = { value ->
151+
player.seekTo(value.toLong())
152+
playbackPosition = value.toLong()
153+
},
154+
valueRange = 0f..duration.toFloat(),
155+
modifier = Modifier
156+
.fillMaxWidth()
157+
.height(20.dp)
158+
.padding(horizontal = 16.dp),
159+
colors = SliderDefaults.colors(
160+
thumbColor = Color.White,
161+
inactiveTrackColor = Color.Gray,
162+
activeTrackColor = Color.White
163+
)
164+
)
165+
166+
Text(
167+
text = "${formatTime(playbackPosition)} / ${formatTime(duration)}",
168+
color = Color.White,
169+
modifier = Modifier.padding(8.dp)
170+
)
171+
}
172+
}
173+
174+
fun formatTime(ms: Long): String {
175+
val totalSeconds = (ms / 1000).toInt()
176+
val seconds = totalSeconds % 60
177+
val minutes = (totalSeconds / 60) % 60
178+
val hours = totalSeconds / 3600
179+
return if (hours > 0) {
180+
String.format("%d:%02d:%02d", hours, minutes, seconds)
181+
} else {
182+
String.format("%02d:%02d", minutes, seconds)
183+
}
184+
}

app/src/main/java/uk/co/sentinelweb/cuer/app/ui/filebrowser/FileBrowserFragment.kt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package uk.co.sentinelweb.cuer.app.ui.filebrowser
22

33
import android.content.Context
4+
import android.content.Intent
45
import android.os.Bundle
56
import android.view.LayoutInflater
67
import android.view.View
@@ -22,6 +23,7 @@ import uk.co.sentinelweb.cuer.app.ui.common.navigation.NavigationModel.Target.NA
2223
import uk.co.sentinelweb.cuer.app.ui.common.navigation.NavigationRouter
2324
import uk.co.sentinelweb.cuer.app.ui.common.navigation.getString
2425
import uk.co.sentinelweb.cuer.app.ui.common.navigation.navigationRouter
26+
import uk.co.sentinelweb.cuer.app.ui.exoplayer.ExoPlayerActivity
2527
import uk.co.sentinelweb.cuer.app.ui.filebrowser.FilesContract.Label
2628
import uk.co.sentinelweb.cuer.app.ui.play_control.CompactPlayerScroll
2729
import uk.co.sentinelweb.cuer.app.util.extension.fragmentScopeWithSource
@@ -150,7 +152,11 @@ class FileBrowserFragment : Fragment(), AndroidScopeComponent {
150152
scoped { navigationRouter(true, this.getFragmentActivity()) }
151153
scoped<PlayerLaunchHost> {
152154
object : PlayerLaunchHost {
153-
override fun launchVideo(item: PlaylistItemDomain, screenIndex: Int?) = Unit
155+
override fun launchVideo(item: PlaylistItemDomain, screenIndex: Int?) {
156+
val activity = get<FileBrowserFragment>().activity
157+
Intent(activity, ExoPlayerActivity::class.java)
158+
.let { activity!!.startActivity(it) }
159+
}
154160
}
155161
}
156162
}

gradle/libs.versions.toml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -88,14 +88,14 @@ jna = "5.14.0"
8888
paletteKtx = "1.0.0"
8989
uiAndroid = "1.7.4"
9090
coil-kmp = "3.0.0-rc01"
91+
exoplayer = "2.19.1"
92+
androidx-activity-compose = "1.3.1"
9193

9294
[libraries]
9395
kotlinStdLib = { module = "org.jetbrains.kotlin:kotlin-stdlib-jdk7", version.ref = "kotlin" }
9496
junit = { group = "junit", name = "junit", version.ref = "junit" }
9597
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "androidx-core-ktx" }
9698
androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "androidx-appcompat" }
97-
androidx-material = { group = "com.google.android.material", name = "material", version.ref = "androidx-material" }
98-
androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "androidx-constraintlayout" }
9999
androidx-fragment = { group = "androidx.fragment", name = "fragment", version.ref = "androidx-fragment-ktx" }
100100
androidx-fragment-ktx = { group = "androidx.fragment", name = "fragment-ktx", version.ref = "androidx-fragment-ktx" }
101101
androidx-annotation = { module = "androidx.annotation:annotation", version.ref = "androidx-annotation" }
@@ -139,13 +139,13 @@ coilCompose = { module = "io.coil-kt:coil-compose", version.ref = "coil" }
139139
coreSplashscreen = { module = "androidx.core:core-splashscreen", version.ref = "splash" }
140140
androidx-palette-ktx = { group = "androidx.palette", name = "palette-ktx", version.ref = "paletteKtx" }
141141
androidx-lifecycle-viewmodel = { group = "org.jetbrains.androidx.lifecycle", name = "lifecycle-viewmodel", version.ref = "androidx-lifecycle" }
142-
#androidx-lifecycle-viewmodel-desktop = { group = "org.jetbrains.androidx.lifecycle", name = "lifecycle-viewmodel-desktop", version.ref = "androidx-lifecycle" }
143142
androidx-lifecycle-runtime-compose = { group = "org.jetbrains.androidx.lifecycle", name = "lifecycle-runtime-compose", version.ref = "androidx-lifecycle" }
144143
androidx-lifecycle-viewmodel-compose = { group = "org.jetbrains.androidx.lifecycle", name = "lifecycle-viewmodel-compose", version.ref = "androidx-lifecycle" }
145144
androidx-lifecycle-viewmodel-compose-desktop = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-compose-desktop", version.ref = "androidx-lifecycle" }
146-
#androidx-lifecycle-viewmodel-desktop = { group = "org.jetbrains.androidx.lifecycle", name = "lifecycle-viewmodel-desktop", version.ref = "androidx-lifecycle" }
147145
coil-kmp = {group="io.coil-kt.coil3", name="coil-compose", version.ref = "coil-kmp"}
148146
kotlinx-coroutines-swing = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-swing", version.ref = "coroutines" }
147+
exoplayer = { group = "com.google.android.exoplayer", name = "exoplayer", version.ref = "exoplayer" }
148+
androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "androidx-activity-compose" }
149149

150150
# testing
151151
kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" }

shared/src/commonMain/kotlin/uk/co/sentinelweb/cuer/app/ui/common/compose/SharedTheme.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ val colorDelete = Color( 0xFFe53935)
4141
val colorEdit = Color( 0xFF43a047)
4242
val colorMove = Color( 0xFF1e88e5)
4343
val colorTransparentBlack = Color( 0x44000000)
44-
val colorTransparentYellow = Color( 0x22ffff00)
44+
val colorTransparentYellow = Color( 0x66888800)
4545

4646
@Composable
4747
fun CuerSharedTheme(

shared/src/commonMain/kotlin/uk/co/sentinelweb/cuer/app/ui/remotes/RemotesComposables.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ object RemotesComposables {
5050
Surface {
5151
Box(contentAlignment = Alignment.TopStart) {
5252
CuerSharedAppBar(
53-
title = stringResource(Res.string.rm_title),// + ": " + model.value.title,
53+
title = stringResource(Res.string.rm_title),
5454
backgroundColor = colorTransparentBlack,
5555
contentColor = Color.White,
5656
modifier = Modifier

0 commit comments

Comments
 (0)