Skip to content

Commit d05d0a8

Browse files
committed
compose: R4 P5-1 remaining demos (ad + danmaku + exo + multi-window-parallel)
Adds the remaining 4 P5-1 demos that pair with the Java samples and close out R4 (8/8 P5-1 demos done; total Compose demo coverage 16/41). Demos: - AdInListComposeActivity (D3) - single controller setUp(ad) followed by events.AutoComplete edge then setUp(feature). Simplifies the Java GSYADVideoPlayer dual-player pattern using Compose reactive event chain. - DanmakuComposeActivity (D5) - pure Compose Canvas + rememberTextMeasurer drawing 12 mock danmakus across 3 lanes; uses controller.snapshot.currentPosition so seek auto-syncs without master.flame.danmaku SDK. - ExoSwitchSourceComposeActivity (D6) - PlayerFactory.setPlayManager(Exo2PlayerManager) in onCreate; 4 sources (MP4 / HLS / GSY default / IMG_0382) and 5 speed presets (0.75 / 1.0 / 1.25 / 1.5 / 2.0). Each source switch rebuilds GSYVideoOptionBuilder. - MultiWindowParallelComposeActivity (D7) - AndroidView wraps 3 MultiSampleVideo instances; each uses a unique setPlayTag + setPlayPosition so getKey() routes to its own CustomManager.getCustomManager(key). Demonstrates TRUE parallel playback (different from existing P1 MultiWindowActivity which is a mutex Native-controller demo). Wiring: - ComposeDemoListActivity entries 12 -> 16 - AndroidManifest registers 4 new exported=false activities Real-device deep verification (emulator-5554): - D6 logcat 'ExoPlayerImpl: Init [AndroidXMedia3/1.10.1]' confirms EXO kernel switch; HLS source-switch shows old instance Release + new instance Init + onPrepared; UI panel reads 'kernel: EXO2 (Exo2PlayerManager)'. - D5 plays the 5547s long sample with Playing 48s real progress + danmaku toggle + 1x speed; verifies snapshot sync. - D3 phase transitions ad -> feature confirmed via UI state (Playing 5022/10026 ms after AutoComplete). - D7 onPrepared + CURRENT_STATE_PLAYING + videoWidth 480x384 across multiple player hashes; multi-CustomManager class path active. - Monkey 100 events: 0 FATAL across the whole app. doc/COMPOSE_BACKLOG.md updated: R4 row 'half-done (4/8)' -> 'done (8/8)'; total demo coverage 12/41 -> 16/41 (~39%).
1 parent 47ce187 commit d05d0a8

7 files changed

Lines changed: 712 additions & 8 deletions

File tree

app/src/main/AndroidManifest.xml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,26 @@
312312
android:configChanges="keyboard|keyboardHidden|orientation|screenSize|screenLayout|smallestScreenSize|uiMode"
313313
android:exported="false"
314314
android:screenOrientation="portrait" />
315+
<activity
316+
android:name=".compose.host.AdInListComposeActivity"
317+
android:configChanges="keyboard|keyboardHidden|orientation|screenSize|screenLayout|smallestScreenSize|uiMode"
318+
android:exported="false"
319+
android:screenOrientation="portrait" />
320+
<activity
321+
android:name=".compose.host.DanmakuComposeActivity"
322+
android:configChanges="keyboard|keyboardHidden|orientation|screenSize|screenLayout|smallestScreenSize|uiMode"
323+
android:exported="false"
324+
android:screenOrientation="portrait" />
325+
<activity
326+
android:name=".compose.host.ExoSwitchSourceComposeActivity"
327+
android:configChanges="keyboard|keyboardHidden|orientation|screenSize|screenLayout|smallestScreenSize|uiMode"
328+
android:exported="false"
329+
android:screenOrientation="portrait" />
330+
<activity
331+
android:name=".compose.host.MultiWindowParallelComposeActivity"
332+
android:configChanges="keyboard|keyboardHidden|orientation|screenSize|screenLayout|smallestScreenSize|uiMode"
333+
android:exported="false"
334+
android:screenOrientation="portrait" />
315335

316336
<meta-data
317337
android:name="android.max_aspect"

app/src/main/java/com/example/gsyvideoplayer/compose/ComposeDemoListActivity.kt

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,19 @@ import androidx.compose.runtime.Composable
2424
import androidx.compose.ui.Modifier
2525
import androidx.compose.ui.platform.LocalContext
2626
import androidx.compose.ui.unit.dp
27+
import com.example.gsyvideoplayer.compose.host.AdInListComposeActivity
2728
import com.example.gsyvideoplayer.compose.host.AutoPlayListActivity
2829
import com.example.gsyvideoplayer.compose.host.BasicWrapperActivity
2930
import com.example.gsyvideoplayer.compose.host.CacheDownloadComposeActivity
31+
import com.example.gsyvideoplayer.compose.host.DanmakuComposeActivity
3032
import com.example.gsyvideoplayer.compose.host.DetailFilterComposeActivity
3133
import com.example.gsyvideoplayer.compose.host.DetailNativeActivity
34+
import com.example.gsyvideoplayer.compose.host.ExoSwitchSourceComposeActivity
3235
import com.example.gsyvideoplayer.compose.host.FullFeatureNativeActivity
3336
import com.example.gsyvideoplayer.compose.host.ListPlayNativeActivity
3437
import com.example.gsyvideoplayer.compose.host.ListWithFullscreenActivity
3538
import com.example.gsyvideoplayer.compose.host.MultiWindowActivity
39+
import com.example.gsyvideoplayer.compose.host.MultiWindowParallelComposeActivity
3640
import com.example.gsyvideoplayer.compose.host.SubtitleComposeActivity
3741
import com.example.gsyvideoplayer.compose.host.SwitchSeamlessComposeActivity
3842
import com.example.gsyvideoplayer.compose.host.SwitchUrlActivity
@@ -117,6 +121,26 @@ private val DEMO_ENTRIES = listOf(
117121
subtitle = "对齐 Java SwitchDetailActivity:同一 controller 跨 list/detail 复用,不重 setUp 不重拉流",
118122
target = SwitchSeamlessComposeActivity::class.java,
119123
),
124+
DemoEntry(
125+
title = "P5 · Native 前贴片广告",
126+
subtitle = "对齐 Java ListADVideoActivity:单 controller AD → AutoComplete → 切正片,演示 events 边沿事件链",
127+
target = AdInListComposeActivity::class.java,
128+
),
129+
DemoEntry(
130+
title = "P5 · Native Compose 自绘弹幕",
131+
subtitle = "对齐 Java DanmakuVideoPlayer 思路:Canvas + textMeasurer 与 snapshot.currentPosition 同步",
132+
target = DanmakuComposeActivity::class.java,
133+
),
134+
DemoEntry(
135+
title = "P5 · Native EXO 多源切换",
136+
subtitle = "对齐 Java DetailExoListPlayer:PlayerFactory.setPlayManager(Exo2PlayerManager) + MP4/HLS + 5 档倍速",
137+
target = ExoSwitchSourceComposeActivity::class.java,
138+
),
139+
DemoEntry(
140+
title = "P5 · Wrapper 真并行多窗口",
141+
subtitle = "对齐 Java ListMultiNormalAdapter:MultiSampleVideo + CustomManager,3 个并行播放(非互斥)",
142+
target = MultiWindowParallelComposeActivity::class.java,
143+
),
120144
)
121145

122146
@OptIn(ExperimentalMaterial3Api::class)
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
package com.example.gsyvideoplayer.compose.host
2+
3+
import android.os.Bundle
4+
import androidx.activity.ComponentActivity
5+
import androidx.activity.compose.setContent
6+
import androidx.compose.foundation.background
7+
import androidx.compose.foundation.layout.Arrangement
8+
import androidx.compose.foundation.layout.Box
9+
import androidx.compose.foundation.layout.Column
10+
import androidx.compose.foundation.layout.Row
11+
import androidx.compose.foundation.layout.aspectRatio
12+
import androidx.compose.foundation.layout.fillMaxSize
13+
import androidx.compose.foundation.layout.fillMaxWidth
14+
import androidx.compose.foundation.layout.padding
15+
import androidx.compose.foundation.rememberScrollState
16+
import androidx.compose.foundation.verticalScroll
17+
import androidx.compose.material3.Button
18+
import androidx.compose.material3.ExperimentalMaterial3Api
19+
import androidx.compose.material3.MaterialTheme
20+
import androidx.compose.material3.OutlinedButton
21+
import androidx.compose.material3.Scaffold
22+
import androidx.compose.material3.Surface
23+
import androidx.compose.material3.Text
24+
import androidx.compose.material3.TopAppBar
25+
import androidx.compose.runtime.Composable
26+
import androidx.compose.runtime.LaunchedEffect
27+
import androidx.compose.runtime.getValue
28+
import androidx.compose.runtime.mutableStateOf
29+
import androidx.compose.runtime.remember
30+
import androidx.compose.runtime.setValue
31+
import androidx.compose.ui.Alignment
32+
import androidx.compose.ui.Modifier
33+
import androidx.compose.ui.graphics.Color
34+
import androidx.compose.ui.unit.dp
35+
import com.shuyu.gsyvideoplayer.builder.GSYVideoOptionBuilder
36+
import com.shuyu.gsyvideoplayer.compose.native_.GSYDefaultControls
37+
import com.shuyu.gsyvideoplayer.compose.native_.GSYPlayerEvent
38+
import com.shuyu.gsyvideoplayer.compose.native_.GSYPlayerSurface
39+
import com.shuyu.gsyvideoplayer.compose.native_.rememberGSYPlayerController
40+
41+
class AdInListComposeActivity : ComponentActivity() {
42+
override fun onCreate(savedInstanceState: Bundle?) {
43+
super.onCreate(savedInstanceState)
44+
setContent {
45+
MaterialTheme {
46+
Surface(modifier = Modifier.fillMaxSize()) {
47+
AdInListScreen()
48+
}
49+
}
50+
}
51+
}
52+
}
53+
54+
private const val AD_URL = "https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8"
55+
private const val FEATURE_URL = "https://www.w3schools.com/html/mov_bbb.mp4"
56+
57+
@OptIn(ExperimentalMaterial3Api::class)
58+
@Composable
59+
private fun AdInListScreen() {
60+
// D3 简化版(对齐 Java ListADVideoActivity 的 startAdPlay → onAutoComplete → 切正片):
61+
// Compose 端用单个 controller,先 setUp 广告 URL 自动播放;监听 events.AutoComplete,
62+
// 当广告播完后 setUp 正片 URL。这是 controller setUp 链 + AutoComplete 边沿事件的协同样例。
63+
val controller = rememberGSYPlayerController()
64+
val snap by controller.snapshot
65+
66+
var phase by remember { mutableStateOf("ad") } // "ad" | "feature" | "done"
67+
var skipAd by remember { mutableStateOf(false) }
68+
69+
LaunchedEffect(Unit) {
70+
// 起始播广告
71+
val builder = GSYVideoOptionBuilder()
72+
.setUrl(AD_URL)
73+
.setVideoTitle("广告片头 (Compose AD Demo)")
74+
.setLooping(false)
75+
controller.setUp(builder, autoPlay = true)
76+
}
77+
78+
LaunchedEffect(Unit) {
79+
controller.events.collect { ev ->
80+
// 关键事件:广告播完 → 切正片
81+
if (ev is GSYPlayerEvent.AutoComplete && phase == "ad") {
82+
phase = "feature"
83+
val builder = GSYVideoOptionBuilder()
84+
.setUrl(FEATURE_URL)
85+
.setVideoTitle("正片 (Compose AD Demo)")
86+
.setLooping(false)
87+
controller.setUp(builder, autoPlay = true)
88+
} else if (ev is GSYPlayerEvent.AutoComplete && phase == "feature") {
89+
phase = "done"
90+
}
91+
}
92+
}
93+
94+
LaunchedEffect(skipAd) {
95+
if (skipAd && phase == "ad") {
96+
phase = "feature"
97+
val builder = GSYVideoOptionBuilder()
98+
.setUrl(FEATURE_URL)
99+
.setVideoTitle("正片 (Compose AD Demo)")
100+
.setLooping(false)
101+
controller.setUp(builder, autoPlay = true)
102+
}
103+
}
104+
105+
Scaffold(
106+
topBar = { TopAppBar(title = { Text("Native 前贴片广告 Demo") }) }
107+
) { padding ->
108+
Column(
109+
modifier = Modifier
110+
.fillMaxSize()
111+
.padding(padding)
112+
.padding(12.dp)
113+
.verticalScroll(rememberScrollState()),
114+
verticalArrangement = Arrangement.spacedBy(10.dp),
115+
) {
116+
Text(
117+
"对齐 Java ListADVideoActivity 的 ad → feature 切换思路:" +
118+
"Compose 端无需额外 GSYADVideoPlayer,直接借助 controller.events.AutoComplete 边沿事件 " +
119+
"在广告 onAutoComplete 时调 setUp(featureUrl) 重启播放管道。",
120+
style = MaterialTheme.typography.bodyMedium,
121+
)
122+
123+
Box(
124+
modifier = Modifier
125+
.fillMaxWidth()
126+
.aspectRatio(16f / 9f)
127+
.background(Color.Black),
128+
contentAlignment = Alignment.Center,
129+
) {
130+
GSYPlayerSurface(controller = controller, modifier = Modifier.fillMaxSize())
131+
GSYDefaultControls(controller = controller, modifier = Modifier.fillMaxSize())
132+
}
133+
134+
Text(
135+
"阶段:${
136+
when (phase) {
137+
"ad" -> "▶ 广告播放中"
138+
"feature" -> "🎬 正片播放中"
139+
else -> "✅ 全部完成"
140+
}
141+
}\n状态:${snap.state} | ${snap.currentPosition} / ${snap.duration} ms",
142+
style = MaterialTheme.typography.bodySmall,
143+
)
144+
145+
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
146+
Button(onClick = { skipAd = true }, enabled = phase == "ad") {
147+
Text("跳过广告")
148+
}
149+
OutlinedButton(onClick = {
150+
phase = "ad"
151+
skipAd = false
152+
val builder = GSYVideoOptionBuilder()
153+
.setUrl(AD_URL)
154+
.setVideoTitle("广告片头 (Compose AD Demo)")
155+
.setLooping(false)
156+
controller.setUp(builder, autoPlay = true)
157+
}) { Text("重置(重新看广告)") }
158+
}
159+
}
160+
}
161+
}

0 commit comments

Comments
 (0)