Skip to content

Commit 9b01942

Browse files
committed
新增移动版的播放器
1 parent f711594 commit 9b01942

File tree

21 files changed

+726
-5
lines changed

21 files changed

+726
-5
lines changed

app/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,7 @@ dependencies {
208208
implementation(project(mapOf("path" to ":bili-api")))
209209
implementation(project(mapOf("path" to ":bili-subtitle")))
210210
implementation(project(mapOf("path" to ":bv-player")))
211+
implementation(project(mapOf("path" to ":bv-player-mobile")))
211212
testImplementation(androidx.room.testing)
212213
testImplementation(libs.kotlin.test)
213214
androidTestImplementation(androidx.compose.ui.test.junit4)

app/src/main/AndroidManifest.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,11 @@
141141
android:name="android.support.FILE_PROVIDER_PATHS"
142142
android:resource="@xml/provider_paths" />
143143
</provider>
144+
<activity
145+
android:name=".mobile.activities.VideoPlayerActivity"
146+
android:exported="true"
147+
android:label="@string/title_mobile_activity_video_player"
148+
android:theme="@style/Theme.BVMobile" />
144149
</application>
145150

146151
</manifest>

app/src/main/kotlin/dev/aaa1115910/bv/BVApp.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import com.google.firebase.analytics.ktx.analytics
1111
import com.google.firebase.ktx.Firebase
1212
import de.schnettler.datastore.manager.DataStoreManager
1313
import dev.aaa1115910.bv.dao.AppDatabase
14+
import dev.aaa1115910.bv.mobile.activities.MobileVideoPlayerViewModel
1415
import dev.aaa1115910.bv.repository.UserRepository
1516
import dev.aaa1115910.bv.repository.VideoInfoRepository
1617
import dev.aaa1115910.bv.viewmodel.LoginViewModel
@@ -78,6 +79,7 @@ val appModule = module {
7879
viewModel { FollowingSeasonViewModel() }
7980
viewModel { TagViewModel() }
8081
viewModel { VideoPlayerV3ViewModel(get()) }
82+
viewModel { MobileVideoPlayerViewModel() }
8183
}
8284

8385
val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "Settings")
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
package dev.aaa1115910.bv.mobile.activities
2+
3+
import android.annotation.SuppressLint
4+
import android.app.Activity
5+
import android.content.pm.ActivityInfo
6+
import android.content.res.Configuration
7+
import android.os.Bundle
8+
import androidx.activity.ComponentActivity
9+
import androidx.activity.compose.setContent
10+
import androidx.compose.foundation.layout.aspectRatio
11+
import androidx.compose.foundation.layout.fillMaxSize
12+
import androidx.compose.foundation.layout.fillMaxWidth
13+
import androidx.compose.foundation.layout.padding
14+
import androidx.compose.material3.ExperimentalMaterial3Api
15+
import androidx.compose.material3.LargeTopAppBar
16+
import androidx.compose.material3.Scaffold
17+
import androidx.compose.material3.Text
18+
import androidx.compose.runtime.Composable
19+
import androidx.compose.runtime.LaunchedEffect
20+
import androidx.compose.runtime.SideEffect
21+
import androidx.compose.runtime.movableContentOf
22+
import androidx.compose.runtime.remember
23+
import androidx.compose.ui.Modifier
24+
import androidx.compose.ui.platform.LocalConfiguration
25+
import androidx.compose.ui.platform.LocalContext
26+
import androidx.compose.ui.zIndex
27+
import androidx.lifecycle.ViewModel
28+
import androidx.media3.exoplayer.ExoPlayer
29+
import com.google.accompanist.systemuicontroller.rememberSystemUiController
30+
import dev.aaa1115910.bv.mobile.theme.BVMobileTheme
31+
import dev.aaa1115910.bv.player.mobile.component.BvPlayer
32+
import dev.aaa1115910.bv.player.mobile.component.playUrl
33+
import org.koin.androidx.compose.koinViewModel
34+
import org.koin.androidx.viewmodel.ext.android.viewModel
35+
36+
class VideoPlayerActivity : ComponentActivity() {
37+
private val playerViewModel: MobileVideoPlayerViewModel by viewModel()
38+
override fun onCreate(savedInstanceState: Bundle?) {
39+
super.onCreate(savedInstanceState)
40+
initVideoPlayer()
41+
setContent {
42+
BVMobileTheme {
43+
VideoPlayerScreen()
44+
}
45+
}
46+
}
47+
48+
fun initVideoPlayer() {
49+
if (playerViewModel.videoPlayer != null) return
50+
val videoUrl =
51+
"https://storage.googleapis.com/downloads.webmproject.org/av1/exoplayer/bbb-av1-480p.mp4"
52+
val audioUrl = null
53+
playerViewModel.videoPlayer = ExoPlayer.Builder(this).build()
54+
playerViewModel.videoPlayer?.playUrl(videoUrl, audioUrl)
55+
playerViewModel.videoPlayer?.prepare()
56+
}
57+
}
58+
59+
@OptIn(ExperimentalMaterial3Api::class)
60+
@Composable
61+
fun VideoPlayerScreen(
62+
playerViewModel: MobileVideoPlayerViewModel = koinViewModel()
63+
) {
64+
val context = LocalContext.current
65+
66+
val configuration = LocalConfiguration.current
67+
val isLandscape = configuration.orientation == Configuration.ORIENTATION_LANDSCAPE
68+
69+
val systemUiController = rememberSystemUiController()
70+
SideEffect {
71+
systemUiController.isStatusBarVisible = !isLandscape
72+
systemUiController.isNavigationBarVisible = !isLandscape
73+
}
74+
75+
LaunchedEffect(Unit) {
76+
}
77+
78+
val bvPlayerContent = remember {
79+
// TODO movableContentOf here doesn't avoid Media from recreating its surface view when
80+
// screen rotation changed. Seems like a bug of Compose.
81+
// see: https://kotlinlang.slack.com/archives/CJLTWPH7S/p1654734644676989
82+
movableContentOf { isLandscape: Boolean, modifier: Modifier ->
83+
BvPlayer(
84+
modifier = modifier,
85+
isLandscape = isLandscape,
86+
videoPlayer = playerViewModel.videoPlayer!!,
87+
onEnterFullScreen = {
88+
(context as Activity).requestedOrientation =
89+
ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE
90+
},
91+
onExitFullScreen = {
92+
@SuppressLint("SourceLockedOrientationActivity")
93+
(context as Activity).requestedOrientation =
94+
ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT
95+
}
96+
)
97+
}
98+
}
99+
100+
Scaffold(
101+
topBar = {
102+
LargeTopAppBar(title = { Text(text = "title") })
103+
}
104+
) { innerPadding ->
105+
if (playerViewModel.videoPlayer != null) {
106+
if (!isLandscape) {
107+
bvPlayerContent(
108+
false,
109+
Modifier
110+
.padding(innerPadding)
111+
.fillMaxWidth()
112+
.aspectRatio(16f / 9f)
113+
)
114+
} else {
115+
if (playerViewModel.videoPlayer != null)
116+
bvPlayerContent(
117+
true,
118+
Modifier
119+
.fillMaxSize()
120+
.zIndex(1f)
121+
)
122+
}
123+
}
124+
125+
}
126+
127+
SideEffect {
128+
if (isLandscape) {
129+
if ((context as Activity).requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_USER) {
130+
(context as Activity).requestedOrientation =
131+
ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE
132+
}
133+
} else {
134+
(context as Activity).requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_USER
135+
}
136+
}
137+
}
138+
139+
140+
class MobileVideoPlayerViewModel : ViewModel() {
141+
var videoPlayer: ExoPlayer? = null
142+
}

app/src/main/kotlin/dev/aaa1115910/bv/mobile/componment/home/SearchBar.kt renamed to app/src/main/kotlin/dev/aaa1115910/bv/mobile/component/home/SearchBar.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package dev.aaa1115910.bv.mobile.componment.home
1+
package dev.aaa1115910.bv.mobile.component.home
22

33
import androidx.compose.foundation.background
44
import androidx.compose.foundation.layout.Arrangement

app/src/main/kotlin/dev/aaa1115910/bv/mobile/componment/videocard/SmallVideoCard.kt renamed to app/src/main/kotlin/dev/aaa1115910/bv/mobile/component/videocard/SmallVideoCard.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package dev.aaa1115910.bv.mobile.componment.videocard
1+
package dev.aaa1115910.bv.mobile.component.videocard
22

33
import androidx.compose.foundation.background
44
import androidx.compose.foundation.layout.Arrangement

app/src/main/kotlin/dev/aaa1115910/bv/mobile/screen/home/HomeScreen.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@ import androidx.compose.runtime.setValue
2323
import androidx.compose.ui.Modifier
2424
import androidx.compose.ui.unit.dp
2525
import dev.aaa1115910.bv.entity.carddata.VideoCardData
26-
import dev.aaa1115910.bv.mobile.componment.home.HomeSearchTopBarCompact
27-
import dev.aaa1115910.bv.mobile.componment.home.HomeSearchTopBarExpanded
28-
import dev.aaa1115910.bv.mobile.componment.videocard.SmallVideoCard
26+
import dev.aaa1115910.bv.mobile.component.home.HomeSearchTopBarCompact
27+
import dev.aaa1115910.bv.mobile.component.home.HomeSearchTopBarExpanded
28+
import dev.aaa1115910.bv.mobile.component.videocard.SmallVideoCard
2929
import dev.aaa1115910.bv.viewmodel.home.PopularViewModel
3030
import kotlinx.coroutines.launch
3131
import org.koin.androidx.compose.koinViewModel

app/src/main/res/values/strings.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,7 @@
193193
<string name="title_activity_video_info">视频信息</string>
194194
<string name="title_activity_video_player">视频播放</string>
195195
<string name="title_activity_video_player_v3">视频播放</string>
196+
<string name="title_mobile_activity_video_player">视频播放</string>
196197

197198
<string name="top_nav_item_anime">番剧</string>
198199
<string name="top_nav_item_dynamics">动态</string>

bv-player-mobile/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/build

bv-player-mobile/build.gradle.kts

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
@file:Suppress("UnstableApiUsage")
2+
3+
plugins {
4+
alias(gradleLibs.plugins.android.library)
5+
alias(gradleLibs.plugins.kotlin.android)
6+
}
7+
8+
android {
9+
namespace = "${AppConfiguration.appId}.player.mobile"
10+
compileSdk = AppConfiguration.compileSdk
11+
12+
defaultConfig {
13+
minSdk = AppConfiguration.minSdk
14+
15+
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
16+
consumerProguardFiles("consumer-rules.pro")
17+
}
18+
19+
buildTypes {
20+
release {
21+
isMinifyEnabled = true
22+
proguardFiles(
23+
getDefaultProguardFile("proguard-android-optimize.txt"),
24+
"proguard-rules.pro"
25+
)
26+
}
27+
create("r8Test") {
28+
isMinifyEnabled = true
29+
proguardFiles(
30+
getDefaultProguardFile("proguard-android-optimize.txt"),
31+
"proguard-rules.pro"
32+
)
33+
}
34+
create("alpha") {
35+
isMinifyEnabled = true
36+
proguardFiles(
37+
getDefaultProguardFile("proguard-android-optimize.txt"),
38+
"proguard-rules.pro"
39+
)
40+
}
41+
}
42+
43+
compileOptions {
44+
sourceCompatibility = JavaVersion.VERSION_11
45+
targetCompatibility = JavaVersion.VERSION_11
46+
}
47+
buildFeatures {
48+
compose = true
49+
}
50+
composeOptions {
51+
kotlinCompilerExtensionVersion = androidx.compose.compiler.get().version
52+
}
53+
}
54+
55+
java {
56+
toolchain {
57+
languageVersion.set(JavaLanguageVersion.of(11))
58+
}
59+
}
60+
61+
dependencies {
62+
63+
implementation(androidx.activity.compose)
64+
implementation(androidx.core.ktx)
65+
implementation(androidx.compose.ui)
66+
implementation(androidx.compose.ui.util)
67+
implementation(androidx.compose.ui.tooling.preview)
68+
implementation(androidx.compose.tv.foundation)
69+
implementation(androidx.compose.tv.material)
70+
implementation(androidx.compose.material)
71+
implementation(androidx.compose.material3)
72+
implementation(androidx.media3.common)
73+
implementation(androidx.media3.decoder)
74+
implementation(androidx.media3.exoplayer)
75+
implementation(androidx.media3.exoplayer.dash)
76+
implementation(androidx.media3.exoplayer.hls)
77+
implementation(androidx.media3.ui)
78+
implementation(libs.material)
79+
testImplementation(libs.kotlin.test)
80+
androidTestImplementation(androidx.compose.ui.test.junit4)
81+
debugImplementation(androidx.compose.ui.test.manifest)
82+
debugImplementation(androidx.compose.ui.tooling)
83+
}

bv-player-mobile/consumer-rules.pro

Whitespace-only changes.

bv-player-mobile/proguard-rules.pro

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Add project specific ProGuard rules here.
2+
# You can control the set of applied configuration files using the
3+
# proguardFiles setting in build.gradle.
4+
#
5+
# For more details, see
6+
# http://developer.android.com/guide/developing/tools/proguard.html
7+
8+
# If your project uses WebView with JS, uncomment the following
9+
# and specify the fully qualified class name to the JavaScript interface
10+
# class:
11+
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12+
# public *;
13+
#}
14+
15+
# Uncomment this to preserve the line number information for
16+
# debugging stack traces.
17+
#-keepattributes SourceFile,LineNumberTable
18+
19+
# If you keep the line number information, uncomment this to
20+
# hide the original source file name.
21+
#-renamesourcefileattribute SourceFile
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package dev.aaa1115910.bv.player.mobile
2+
3+
import androidx.test.ext.junit.runners.AndroidJUnit4
4+
import androidx.test.platform.app.InstrumentationRegistry
5+
import org.junit.Assert.*
6+
import org.junit.Test
7+
import org.junit.runner.RunWith
8+
9+
/**
10+
* Instrumented test, which will execute on an Android device.
11+
*
12+
* See [testing documentation](http://d.android.com/tools/testing).
13+
*/
14+
@RunWith(AndroidJUnit4::class)
15+
class ExampleInstrumentedTest {
16+
@Test
17+
fun useAppContext() {
18+
// Context of the app under test.
19+
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
20+
assertEquals("dev.aaa1115910.bv.player.mobile.test", appContext.packageName)
21+
}
22+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
3+
4+
</manifest>
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package dev.aaa1115910.bv.player.mobile.component
2+
3+
import androidx.compose.runtime.Composable
4+
import androidx.compose.ui.Modifier
5+
import androidx.media3.exoplayer.ExoPlayer
6+
import dev.aaa1115910.bv.player.mobile.component.controller.BvPlayerController
7+
8+
@Composable
9+
fun BvPlayer(
10+
modifier: Modifier = Modifier,
11+
isLandscape: Boolean,
12+
onEnterFullScreen: () -> Unit,
13+
onExitFullScreen: () -> Unit,
14+
videoPlayer: ExoPlayer
15+
) {
16+
BvPlayerController(
17+
modifier = modifier,
18+
isLandscape = isLandscape,
19+
onEnterFullScreen = onEnterFullScreen,
20+
onExitFullScreen = onExitFullScreen
21+
) {
22+
BvVideoPlayer(videoPlayer = videoPlayer)
23+
}
24+
}

0 commit comments

Comments
 (0)