1+ package com.engineer.compose.ui
2+
3+ import android.media.MediaPlayer
4+ import android.net.Uri
5+ import android.os.Bundle
6+ import android.view.SurfaceHolder
7+ import android.view.SurfaceView
8+ import android.view.WindowManager
9+ import androidx.activity.ComponentActivity
10+ import androidx.activity.compose.setContent
11+ import androidx.compose.foundation.background
12+ import androidx.compose.foundation.layout.Arrangement
13+ import androidx.compose.foundation.layout.Box
14+ import androidx.compose.foundation.layout.Column
15+ import androidx.compose.foundation.layout.Row
16+ import androidx.compose.foundation.layout.aspectRatio
17+ import androidx.compose.foundation.layout.fillMaxSize
18+ import androidx.compose.foundation.layout.fillMaxWidth
19+ import androidx.compose.foundation.layout.padding
20+ import androidx.compose.material.icons.Icons
21+ import androidx.compose.material.icons.filled.Pause
22+ import androidx.compose.material.icons.filled.PlayArrow
23+ import androidx.compose.material3.Icon
24+ import androidx.compose.material3.IconButton
25+ import androidx.compose.material3.Slider
26+ import androidx.compose.material3.Text
27+ import androidx.compose.runtime.Composable
28+ import androidx.compose.runtime.DisposableEffect
29+ import androidx.compose.runtime.LaunchedEffect
30+ import androidx.compose.runtime.getValue
31+ import androidx.compose.runtime.mutableIntStateOf
32+ import androidx.compose.runtime.mutableStateOf
33+ import androidx.compose.runtime.remember
34+ import androidx.compose.runtime.setValue
35+ import androidx.compose.ui.Alignment
36+ import androidx.compose.ui.Modifier
37+ import androidx.compose.ui.graphics.Color
38+ import androidx.compose.ui.platform.LocalContext
39+ import androidx.compose.ui.unit.IntSize
40+ import androidx.compose.ui.unit.dp
41+ import androidx.compose.ui.viewinterop.AndroidView
42+ import androidx.core.view.WindowCompat
43+ import androidx.core.view.WindowInsetsCompat
44+ import androidx.core.view.WindowInsetsControllerCompat
45+ import com.engineer.compose.ui.ui.theme.ComposeAppTheme
46+ import kotlinx.coroutines.delay
47+
48+ class VideoPlayerActivity : ComponentActivity () {
49+ override fun onCreate (savedInstanceState : Bundle ? ) {
50+ super .onCreate(savedInstanceState)
51+ val windowInsetsController = WindowCompat .getInsetsController(window, window.decorView)
52+ windowInsetsController.systemBarsBehavior =
53+ WindowInsetsControllerCompat .BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
54+ windowInsetsController.hide(WindowInsetsCompat .Type .systemBars())
55+
56+ val videoUri = intent.getStringExtra(" video_uri" )
57+ setContent {
58+ ComposeAppTheme {
59+ if (videoUri != null ) {
60+ VideoPlayer (Uri .parse(videoUri))
61+ }
62+ }
63+ }
64+ }
65+ }
66+
67+ @Composable
68+ fun VideoPlayer (videoUri : Uri ) {
69+ val context = LocalContext .current
70+ val mediaPlayer = remember { MediaPlayer () }
71+ var isPlaying by remember { mutableStateOf(false ) }
72+ var videoSize by remember { mutableStateOf(IntSize .Zero ) }
73+ var duration by remember { mutableIntStateOf(0 ) }
74+ var progress by remember { mutableIntStateOf(0 ) }
75+
76+ val activity = LocalContext .current as ? ComponentActivity
77+
78+ LaunchedEffect (isPlaying) {
79+ while (isPlaying) {
80+ progress = mediaPlayer.currentPosition
81+ delay(1000 )
82+ }
83+ }
84+
85+ Box (
86+ modifier = Modifier
87+ .fillMaxSize()
88+ .background(Color .Black ),
89+ contentAlignment = Alignment .Center
90+ ) {
91+ AndroidView (
92+ factory = {
93+ SurfaceView (context).apply {
94+ holder.addCallback(object : SurfaceHolder .Callback {
95+ override fun surfaceCreated (holder : SurfaceHolder ) {
96+ mediaPlayer.apply {
97+ setDataSource(context, videoUri)
98+ setDisplay(holder)
99+ prepareAsync()
100+ setOnPreparedListener {
101+ videoSize = IntSize (it.videoWidth, it.videoHeight)
102+ duration = it.duration
103+ isPlaying = true
104+ it.isLooping = true
105+ it.start()
106+ }
107+ setVideoScalingMode(MediaPlayer .VIDEO_SCALING_MODE_SCALE_TO_FIT )
108+ }
109+ }
110+
111+ override fun surfaceChanged (
112+ holder : SurfaceHolder ,
113+ format : Int ,
114+ width : Int ,
115+ height : Int
116+ ) {
117+ }
118+
119+ override fun surfaceDestroyed (holder : SurfaceHolder ) {
120+ mediaPlayer.stop()
121+ mediaPlayer.release()
122+ }
123+ })
124+ }
125+ },
126+ modifier = Modifier
127+ .fillMaxWidth()
128+ .aspectRatio(if (videoSize.height > 0 ) videoSize.width.toFloat() / videoSize.height.toFloat() else 1f )
129+ )
130+
131+ Column (
132+ modifier = Modifier
133+ .align(Alignment .BottomCenter )
134+ .padding(16 .dp)
135+ .fillMaxWidth()
136+ ) {
137+ Row (
138+ modifier = Modifier .fillMaxWidth(),
139+ horizontalArrangement = Arrangement .SpaceBetween ,
140+ verticalAlignment = Alignment .CenterVertically
141+ ) {
142+ Text (text = formatTime(progress), color = Color .White )
143+ IconButton (
144+ onClick = {
145+ if (isPlaying) {
146+ mediaPlayer.pause()
147+ } else {
148+ mediaPlayer.start()
149+ }
150+ isPlaying = ! isPlaying
151+ }
152+ ) {
153+ Icon (
154+ imageVector = if (isPlaying) Icons .Filled .Pause else Icons .Filled .PlayArrow ,
155+ contentDescription = if (isPlaying) " Pause" else " Play" ,
156+ tint = Color .White
157+ )
158+ }
159+ Text (text = formatTime(duration), color = Color .White )
160+ }
161+ }
162+ }
163+
164+ DisposableEffect (Unit ) {
165+ activity?.window?.addFlags(WindowManager .LayoutParams .FLAG_KEEP_SCREEN_ON )
166+ onDispose {
167+ mediaPlayer.release()
168+ activity?.window?.clearFlags(WindowManager .LayoutParams .FLAG_KEEP_SCREEN_ON )
169+ }
170+ }
171+ }
172+
173+ private fun formatTime (ms : Int ): String {
174+ val totalSeconds = ms / 1000
175+ val minutes = totalSeconds / 60
176+ val seconds = totalSeconds % 60
177+ return String .format(" %02d:%02d" , minutes, seconds)
178+ }
0 commit comments