Refactor video testapp.

This commit is contained in:
Nicholas Tinsley 2024-01-10 11:50:54 -05:00
parent 3673fa4908
commit 8b24498fa7
9 changed files with 168 additions and 91 deletions

View file

@ -1,5 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright 2023 Signal Messenger, LLC
~ SPDX-License-Identifier: AGPL-3.0-only
-->
@ -16,7 +15,6 @@
<activity
android:name=".MainActivity"
android:exported="true"
android:label="@string/app_name"
android:theme="@style/Theme.Signal">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@ -24,6 +22,14 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".transcode.TranscodeTestActivity"
android:exported="false"
android:theme="@style/Theme.Signal" />
<activity
android:name=".playback.PlaybackTestActivity"
android:exported="false"
android:theme="@style/Theme.Signal" />
</application>
</manifest>

View file

@ -5,43 +5,36 @@
package org.thoughtcrime.video.app
import android.content.Intent
import android.content.pm.ActivityInfo
import android.content.pm.PackageManager
import android.content.pm.PackageManager.NameNotFoundException
import android.os.Bundle
import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.result.PickVisualMediaRequest
import androidx.activity.result.contract.ActivityResultContracts
import androidx.activity.viewModels
import androidx.annotation.OptIn
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.Button
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.viewinterop.AndroidView
import androidx.media3.common.util.UnstableApi
import androidx.media3.exoplayer.ExoPlayer
import androidx.media3.exoplayer.source.MediaSource
import androidx.media3.ui.PlayerView
import org.thoughtcrime.video.app.playback.PlaybackTestActivity
import org.thoughtcrime.video.app.transcode.TranscodeTestActivity
import org.thoughtcrime.video.app.ui.composables.LabeledButton
import org.thoughtcrime.video.app.ui.theme.SignalTheme
/**
* Main activity for this sample app.
*/
class MainActivity : ComponentActivity() {
private val viewModel: MainScreenViewModel by viewModels()
private lateinit var exoPlayer: ExoPlayer
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel.initialize(this)
exoPlayer = ExoPlayer.Builder(this).build()
val startPlaybackScreen = Intent(this, PlaybackTestActivity::class.java)
val startTranscodeScreen = Intent(this, TranscodeTestActivity::class.java)
setContent {
SignalTheme {
Surface(modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background) {
@ -49,70 +42,11 @@ class MainActivity : ComponentActivity() {
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
val videoUri = viewModel.selectedVideo
if (videoUri == null) {
LabeledButton("Select Video") { pickMedia.launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.VideoOnly)) }
} else {
LabeledButton("Play Video") { viewModel.updateMediaSource(this@MainActivity) }
LabeledButton("Play Video with slow download") { viewModel.updateMediaSourceTrickle(this@MainActivity) }
ExoVideoView(source = viewModel.mediaSource, exoPlayer = exoPlayer)
}
LabeledButton("Test Playback") { startActivity(startPlaybackScreen) }
LabeledButton("Test Transcode") { startActivity(startTranscodeScreen) }
}
}
}
}
}
override fun onPause() {
super.onPause()
exoPlayer.pause()
}
override fun onDestroy() {
super.onDestroy()
viewModel.releaseCache()
exoPlayer.stop()
exoPlayer.release()
}
/**
* This launches the system media picker and stores the resulting URI.
*/
private val pickMedia = registerForActivityResult(ActivityResultContracts.PickVisualMedia()) { uri ->
if (uri != null) {
Log.d("PhotoPicker", "Selected URI: $uri")
viewModel.selectedVideo = uri
viewModel.updateMediaSource(this)
} else {
Log.d("PhotoPicker", "No media selected")
}
}
}
@Composable
fun LabeledButton(buttonLabel: String, modifier: Modifier = Modifier, onClick: () -> Unit) {
Button(onClick = onClick, modifier = modifier) {
Text(buttonLabel)
}
}
@OptIn(UnstableApi::class)
@Composable
fun ExoVideoView(source: MediaSource, exoPlayer: ExoPlayer, modifier: Modifier = Modifier) {
exoPlayer.playWhenReady = false
exoPlayer.setMediaSource(source)
exoPlayer.prepare()
AndroidView(factory = { context ->
PlayerView(context).apply {
player = exoPlayer
}
}, modifier = modifier)
}
@Preview(showBackground = true)
@Composable
fun GreetingPreview() {
SignalTheme {
LabeledButton("Preview Render") {}
}
}
}

View file

@ -0,0 +1,108 @@
/*
* Copyright 2024 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.thoughtcrime.video.app.playback
import android.os.Bundle
import android.util.Log
import androidx.activity.compose.setContent
import androidx.activity.result.PickVisualMediaRequest
import androidx.activity.result.contract.ActivityResultContracts
import androidx.activity.viewModels
import androidx.annotation.OptIn
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.viewinterop.AndroidView
import androidx.media3.common.util.UnstableApi
import androidx.media3.exoplayer.ExoPlayer
import androidx.media3.exoplayer.source.MediaSource
import androidx.media3.ui.PlayerView
import org.thoughtcrime.video.app.ui.composables.LabeledButton
import org.thoughtcrime.video.app.ui.theme.SignalTheme
class PlaybackTestActivity : AppCompatActivity() {
private val viewModel: PlaybackTestViewModel by viewModels()
private lateinit var exoPlayer: ExoPlayer
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel.initialize(this)
exoPlayer = ExoPlayer.Builder(this).build()
setContent {
SignalTheme {
Surface(modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background) {
Column(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
val videoUri = viewModel.selectedVideo
if (videoUri == null) {
LabeledButton("Select Video") { pickMedia.launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.VideoOnly)) }
} else {
LabeledButton("Play Video") { viewModel.updateMediaSource(this@PlaybackTestActivity) }
LabeledButton("Play Video with slow download") { viewModel.updateMediaSourceTrickle(this@PlaybackTestActivity) }
ExoVideoView(source = viewModel.mediaSource, exoPlayer = exoPlayer)
}
}
}
}
}
}
override fun onPause() {
super.onPause()
exoPlayer.pause()
}
override fun onDestroy() {
super.onDestroy()
viewModel.releaseCache()
exoPlayer.stop()
exoPlayer.release()
}
/**
* This launches the system media picker and stores the resulting URI.
*/
private val pickMedia = registerForActivityResult(ActivityResultContracts.PickVisualMedia()) { uri ->
if (uri != null) {
Log.d("PhotoPicker", "Selected URI: $uri")
viewModel.selectedVideo = uri
viewModel.updateMediaSource(this)
} else {
Log.d("PhotoPicker", "No media selected")
}
}
}
@OptIn(UnstableApi::class)
@Composable
fun ExoVideoView(source: MediaSource, exoPlayer: ExoPlayer, modifier: Modifier = Modifier) {
exoPlayer.playWhenReady = false
exoPlayer.setMediaSource(source)
exoPlayer.prepare()
AndroidView(factory = { context ->
PlayerView(context).apply {
player = exoPlayer
}
}, modifier = modifier)
}
@Preview(showBackground = true)
@Composable
fun GreetingPreview() {
SignalTheme {
LabeledButton("Preview Render") {}
}
}

View file

@ -1,9 +1,9 @@
/*
* Copyright 2023 Signal Messenger, LLC
* Copyright 2024 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.thoughtcrime.video.app
package org.thoughtcrime.video.app.playback
import android.content.Context
import android.net.Uri
@ -28,7 +28,7 @@ import java.io.File
* Main screen view model for the video sample app.
*/
@OptIn(UnstableApi::class)
class MainScreenViewModel : ViewModel() {
class PlaybackTestViewModel : ViewModel() {
// Initialize an silent media source before the user selects a video. This is the closest I could find to an "empty" media source while still being nullsafe.
private val value by lazy {
val factory = SilenceMediaSource.Factory()

View file

@ -1,9 +1,9 @@
/*
* Copyright 2023 Signal Messenger, LLC
* Copyright 2024 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.thoughtcrime.video.app
package org.thoughtcrime.video.app.playback
import android.content.Context
import android.net.Uri

View file

@ -0,0 +1,11 @@
/*
* Copyright 2024 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.thoughtcrime.video.app.transcode
import androidx.appcompat.app.AppCompatActivity
class TranscodeTestActivity : AppCompatActivity() {
}

View file

@ -0,0 +1,18 @@
/*
* Copyright 2024 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.thoughtcrime.video.app.ui.composables
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
@Composable
fun LabeledButton(buttonLabel: String, modifier: Modifier = Modifier, onClick: () -> Unit) {
Button(onClick = onClick, modifier = modifier) {
Text(buttonLabel)
}
}

View file

@ -4,5 +4,5 @@
-->
<resources>
<string name="app_name">Video Player</string>
<string name="app_name">Video Framework Tester</string>
</resources>

View file

@ -6,5 +6,5 @@
<resources>
<style name="Theme.Signal" parent="android:Theme.Material.Light.NoActionBar" />
<style name="Theme.Signal" parent="Theme.AppCompat.DayNight" />
</resources>