添加前台播放服务以支持直播播放,更新相关权限和通知逻辑

This commit is contained in:
2026-02-03 12:06:39 +08:00
parent 3c8383ee67
commit 91240b2de5
4 changed files with 104 additions and 0 deletions

View File

@@ -12,6 +12,7 @@
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_CAMERA"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MICROPHONE"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK"/>
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="28" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
@@ -75,6 +76,11 @@
android:exported="false"
android:foregroundServiceType="camera|microphone|mediaProjection" />
<service
android:name=".live.LivePlayForegroundService"
android:exported="false"
android:foregroundServiceType="mediaPlayback" />
</application>
</manifest>

View File

@@ -244,6 +244,7 @@ class LivePlayActivity : AppCompatActivity() {
if (isPlaying) {
logEvent("用户操作: 暂停")
playerClient.pause()
stopPlaybackForegroundService()
return
}
logEvent("用户操作: 播放")
@@ -272,6 +273,7 @@ class LivePlayActivity : AppCompatActivity() {
if (hasReleasedPlayer) return
hasReleasedPlayer = true
playerClient.release()
stopPlaybackForegroundService()
}
private fun finishForNewPlayback() {
@@ -343,12 +345,21 @@ class LivePlayActivity : AppCompatActivity() {
}
private fun beginPlayback() {
startPlaybackForegroundService()
startPlayAttempt()
resetPreviewForPlayback()
playerClient.prepareToPlay()
playerClient.play()
}
private fun startPlaybackForegroundService() {
LivePlayForegroundService.start(this)
}
private fun stopPlaybackForegroundService() {
LivePlayForegroundService.stop(this)
}
private fun setPlayingUi(playing: Boolean) {
isPlaying = playing
binding.tvPlayLabel.setText(if (playing) R.string.play_ctrl_pause else R.string.play_ctrl_play)

View File

@@ -0,0 +1,85 @@
package com.demo.SellyCloudSDK.live
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.Service
import android.content.Context
import android.content.Intent
import android.os.Build
import androidx.core.app.NotificationCompat
import androidx.core.app.ServiceCompat
import androidx.core.content.ContextCompat
import com.demo.SellyCloudSDK.R
/**
* 前台播放服务:用于在后台持续拉流并避免系统回收。
* 仅负责展示常驻通知,不绑定播放业务。
*/
class LivePlayForegroundService : Service() {
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
ServiceCompat.startForeground(
this,
NOTIFICATION_ID,
buildNotification(),
android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK
)
} else {
startForeground(NOTIFICATION_ID, buildNotification())
}
return START_STICKY
}
override fun onBind(intent: Intent?) = null
override fun onDestroy() {
try {
stopForeground(STOP_FOREGROUND_REMOVE)
} catch (_: Exception) {
}
super.onDestroy()
}
private fun buildNotification(): Notification {
ensureChannel()
return NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle(getString(R.string.live_play_foreground_title))
.setContentText(getString(R.string.live_play_foreground_text))
.setSmallIcon(android.R.drawable.ic_media_play)
.setOngoing(true)
.setOnlyAlertOnce(true)
.build()
}
private fun ensureChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val manager = getSystemService(NotificationManager::class.java) ?: return
val existing = manager.getNotificationChannel(CHANNEL_ID)
if (existing == null) {
val channel = NotificationChannel(
CHANNEL_ID,
"Live Playback",
NotificationManager.IMPORTANCE_LOW
)
manager.createNotificationChannel(channel)
}
}
}
companion object {
private const val CHANNEL_ID = "live_play_foreground"
private const val NOTIFICATION_ID = 0x201
fun start(context: Context) {
val intent = Intent(context, LivePlayForegroundService::class.java)
ContextCompat.startForegroundService(context, intent)
}
fun stop(context: Context) {
val intent = Intent(context, LivePlayForegroundService::class.java)
context.stopService(intent)
}
}
}

View File

@@ -97,6 +97,8 @@
<string name="play_ctrl_unmute">取消静音</string>
<string name="play_ctrl_screenshot">截图</string>
<string name="play_ctrl_pip">画中画</string>
<string name="live_play_foreground_title">正在播放</string>
<string name="live_play_foreground_text">直播播放保持中</string>
<string name="live_stats_title">直播数据</string>