添加在线资源加载功能,更新VOD对话框和相关逻辑,支持动态获取播放格式

This commit is contained in:
2026-03-16 13:59:37 +08:00
parent 290f4317c4
commit af40d82982
7 changed files with 288 additions and 63 deletions

Binary file not shown.

View File

@@ -3,11 +3,17 @@ package com.demo.SellyCloudSDK
import android.app.Dialog import android.app.Dialog
import android.content.Intent import android.content.Intent
import android.graphics.Rect import android.graphics.Rect
import android.graphics.Typeface
import android.os.Bundle import android.os.Bundle
import android.util.TypedValue
import android.view.Gravity import android.view.Gravity
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.GridLayout
import android.widget.TextView
import android.widget.Toast import android.widget.Toast
import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
@@ -34,6 +40,8 @@ import com.demo.SellyCloudSDK.live.square.AliveStreamItem
import com.demo.SellyCloudSDK.live.square.isPkStream import com.demo.SellyCloudSDK.live.square.isPkStream
import com.demo.SellyCloudSDK.login.DemoLoginStore import com.demo.SellyCloudSDK.login.DemoLoginStore
import com.demo.SellyCloudSDK.login.LoginActivity import com.demo.SellyCloudSDK.login.LoginActivity
import com.demo.SellyCloudSDK.vod.VodListRepository
import com.demo.SellyCloudSDK.vod.VodListResult
import com.demo.SellyCloudSDK.vod.VodPlayActivity import com.demo.SellyCloudSDK.vod.VodPlayActivity
import com.sellycloud.sellycloudsdk.SellyLiveMode import com.sellycloud.sellycloudsdk.SellyLiveMode
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
@@ -499,9 +507,96 @@ class FeatureHubActivity : AppCompatActivity() {
startActivity(VodPlayActivity.createIntent(this, "asset:///vod/sample.mp4")) startActivity(VodPlayActivity.createIntent(this, "asset:///vod/sample.mp4"))
} }
loadVodList(dialogBinding)
dialog.show() dialog.show()
} }
private fun loadVodList(dialogBinding: DialogVodInputBinding) {
dialogBinding.pbVodListFull.isVisible = true
dialogBinding.pbVodList.isVisible = true
dialogBinding.tvVodListError.isVisible = false
dialogBinding.gridVodFormats.isVisible = false
uiScope.launch {
val result = VodListRepository.fetchVodList()
dialogBinding.pbVodListFull.isVisible = false
dialogBinding.pbVodList.isVisible = false
when (result) {
is VodListResult.Success -> {
populateVodChips(dialogBinding, result.formats)
}
is VodListResult.Error -> {
dialogBinding.tvVodListError.text = result.message
dialogBinding.tvVodListError.isVisible = true
dialogBinding.tvVodListError.setOnClickListener {
loadVodList(dialogBinding)
}
}
}
}
}
private fun populateVodChips(
dialogBinding: DialogVodInputBinding,
formats: Map<String, String>
) {
val grid = dialogBinding.gridVodFormats
grid.removeAllViews()
grid.isVisible = true
val dp3 = TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, 3f, resources.displayMetrics
).toInt()
val chipHeightPx = TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, 34f, resources.displayMetrics
).toInt()
var selectedChip: TextView? = null
formats.entries.forEachIndexed { index, (format, url) ->
val chip = TextView(this).apply {
text = format
gravity = Gravity.CENTER
setTextSize(TypedValue.COMPLEX_UNIT_SP, 13f)
typeface = Typeface.DEFAULT_BOLD
setTextColor(ContextCompat.getColor(context, R.color.av_text_primary))
setBackgroundResource(R.drawable.selector_av_vod_chip)
isSelected = false
}
val row = index / 4
val col = index % 4
val param = GridLayout.LayoutParams(
GridLayout.spec(row, 1f),
GridLayout.spec(col, 1f)
).apply {
width = 0
height = chipHeightPx
setMargins(
if (col > 0) dp3 else 0,
if (row > 0) dp3 else 0,
0, 0
)
}
chip.setOnClickListener {
if (selectedChip == chip) return@setOnClickListener
selectedChip?.let { prev ->
prev.isSelected = false
prev.setTextColor(ContextCompat.getColor(this, R.color.av_text_primary))
}
chip.isSelected = true
chip.setTextColor(ContextCompat.getColor(this, R.color.brand_primary_text_on))
selectedChip = chip
dialogBinding.etVodUrl.setText(url)
}
grid.addView(chip, param)
}
}
private fun setupSettingsSave() { private fun setupSettingsSave() {
binding.btnSaveSettings.setOnClickListener { binding.btnSaveSettings.setOnClickListener {
val settings = uiToSettingsOrNull() ?: return@setOnClickListener val settings = uiToSettingsOrNull() ?: return@setOnClickListener

View File

@@ -0,0 +1,51 @@
package com.demo.SellyCloudSDK.vod
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import okhttp3.OkHttpClient
import okhttp3.Request
import org.json.JSONObject
private const val VOD_LIST_URL = "http://rtmp.sellycloud.io:8089/live/sdk/demo/vodlist"
sealed class VodListResult {
data class Success(val formats: Map<String, String>) : VodListResult()
data class Error(val message: String) : VodListResult()
}
object VodListRepository {
private val client = OkHttpClient()
suspend fun fetchVodList(): VodListResult = withContext(Dispatchers.IO) {
val request = Request.Builder()
.url(VOD_LIST_URL)
.get()
.build()
try {
client.newCall(request).execute().use { response ->
val body = response.body?.string().orEmpty()
if (!response.isSuccessful) {
return@withContext VodListResult.Error("网络错误: ${response.code}")
}
if (body.isBlank()) {
return@withContext VodListResult.Error("服务返回为空")
}
val json = JSONObject(body)
val formats = linkedMapOf<String, String>()
val keys = json.keys()
while (keys.hasNext()) {
val key = keys.next()
val url = json.optString(key).takeIf { it.isNotBlank() } ?: continue
formats[key.uppercase()] = url
}
if (formats.isEmpty()) {
return@withContext VodListResult.Error("暂无在线资源")
}
return@withContext VodListResult.Success(formats)
}
} catch (e: Exception) {
return@withContext VodListResult.Error(e.message ?: "网络请求失败")
}
}
}

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
<solid android:color="@color/brand_primary" />
<corners android:radius="@dimen/av_corner_small" />
</shape>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/bg_av_vod_chip_selected" android:state_selected="true" />
<item android:drawable="@drawable/bg_av_vod_chip_selected" android:state_pressed="true" />
<item android:drawable="@drawable/bg_av_input_field" />
</selector>

View File

@@ -17,78 +17,142 @@
android:src="@drawable/ic_av_close" android:src="@drawable/ic_av_close"
app:tint="@color/av_text_primary" /> app:tint="@color/av_text_primary" />
<LinearLayout <ScrollView
android:id="@+id/card"
android:layout_width="320dp" android:layout_width="320dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center" android:layout_gravity="center"
android:background="@drawable/bg_av_dialog_card_gray" android:scrollbars="none">
android:orientation="vertical"
android:paddingStart="18dp"
android:paddingTop="16dp"
android:paddingEnd="18dp"
android:paddingBottom="18dp">
<TextView <LinearLayout
android:id="@+id/card"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="center" android:background="@drawable/bg_av_dialog_card_gray"
android:text="@string/vod_config_title" android:orientation="vertical"
android:textColor="@color/av_text_primary" android:paddingStart="18dp"
android:textSize="18sp" android:paddingTop="16dp"
android:textStyle="bold" /> android:paddingEnd="18dp"
android:paddingBottom="18dp">
<TextView <TextView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="14dp" android:gravity="center"
android:text="@string/vod_config_hint" android:text="@string/vod_config_title"
android:textColor="@color/av_text_secondary" android:textColor="@color/av_text_primary"
android:textSize="12sp" /> android:textSize="18sp"
android:textStyle="bold" />
<EditText <TextView
android:id="@+id/etVodUrl" android:layout_width="match_parent"
android:layout_width="match_parent" android:layout_height="wrap_content"
android:layout_height="@dimen/av_field_height" android:layout_marginTop="14dp"
android:layout_marginTop="8dp" android:text="@string/vod_config_hint"
android:background="@drawable/bg_av_input_field" android:textColor="@color/av_text_secondary"
android:importantForAutofill="no" android:textSize="12sp" />
android:inputType="textUri"
android:paddingStart="12dp"
android:paddingEnd="12dp"
android:textColor="@color/av_text_primary"
android:textColorHint="@color/av_text_hint"
android:textSize="14sp" />
<Button <EditText
android:id="@+id/btnStartVod" android:id="@+id/etVodUrl"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="@dimen/av_primary_button_height" android:layout_height="@dimen/av_field_height"
android:layout_marginTop="18dp" android:layout_marginTop="8dp"
android:background="@drawable/selector_av_primary_button" android:background="@drawable/bg_av_input_field"
android:text="@string/play_start" android:importantForAutofill="no"
android:textColor="@color/brand_primary_text_on" android:inputType="textUri"
android:textSize="16sp" android:paddingStart="12dp"
android:textStyle="bold" /> android:paddingEnd="12dp"
android:textColor="@color/av_text_primary"
android:textColorHint="@color/av_text_hint"
android:textSize="14sp" />
<Button <!-- VOD Online Resources Section -->
android:id="@+id/btnPickLocalFile" <LinearLayout
android:layout_width="match_parent" android:id="@+id/vodListSection"
android:layout_height="@dimen/av_primary_button_height" android:layout_width="match_parent"
android:layout_marginTop="8dp" android:layout_height="wrap_content"
android:background="@drawable/bg_av_input_field" android:layout_marginTop="12dp"
android:text="@string/vod_pick_local_file" android:orientation="vertical">
android:textColor="@color/av_text_primary"
android:textSize="14sp" />
<Button <LinearLayout
android:id="@+id/btnPlayAssetSample" android:layout_width="match_parent"
android:layout_width="match_parent" android:layout_height="wrap_content"
android:layout_height="@dimen/av_primary_button_height" android:gravity="center_vertical"
android:layout_marginTop="8dp" android:orientation="horizontal">
android:background="@drawable/bg_av_input_field"
android:text="@string/vod_play_asset_sample" <TextView
android:textColor="@color/av_text_primary" android:layout_width="0dp"
android:textSize="14sp" /> android:layout_height="wrap_content"
</LinearLayout> android:layout_weight="1"
android:text="@string/vod_online_resources"
android:textColor="@color/av_text_secondary"
android:textSize="12sp" />
<ProgressBar
android:id="@+id/pbVodList"
style="?android:attr/progressBarStyleSmall"
android:layout_width="16dp"
android:layout_height="16dp"
android:visibility="gone" />
</LinearLayout>
<ProgressBar
android:id="@+id/pbVodListFull"
style="?android:attr/progressBarStyle"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="center"
android:layout_marginTop="8dp"
android:visibility="gone" />
<TextView
android:id="@+id/tvVodListError"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:gravity="center"
android:textColor="@color/av_stats_red"
android:textSize="12sp"
android:visibility="gone" />
<GridLayout
android:id="@+id/gridVodFormats"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:columnCount="4"
android:visibility="gone" />
</LinearLayout>
<Button
android:id="@+id/btnStartVod"
android:layout_width="match_parent"
android:layout_height="@dimen/av_primary_button_height"
android:layout_marginTop="18dp"
android:background="@drawable/selector_av_primary_button"
android:text="@string/play_start"
android:textColor="@color/brand_primary_text_on"
android:textSize="16sp"
android:textStyle="bold" />
<Button
android:id="@+id/btnPickLocalFile"
android:layout_width="match_parent"
android:layout_height="@dimen/av_primary_button_height"
android:layout_marginTop="8dp"
android:background="@drawable/bg_av_input_field"
android:text="@string/vod_pick_local_file"
android:textColor="@color/av_text_primary"
android:textSize="14sp" />
<Button
android:id="@+id/btnPlayAssetSample"
android:layout_width="match_parent"
android:layout_height="@dimen/av_primary_button_height"
android:layout_marginTop="8dp"
android:background="@drawable/bg_av_input_field"
android:text="@string/vod_play_asset_sample"
android:textColor="@color/av_text_primary"
android:textSize="14sp" />
</LinearLayout>
</ScrollView>
</FrameLayout> </FrameLayout>

View File

@@ -90,6 +90,10 @@
<string name="vod_config_hint">请输入播放地址URL、本地路径、asset:///...</string> <string name="vod_config_hint">请输入播放地址URL、本地路径、asset:///...</string>
<string name="vod_pick_local_file">选择本地文件</string> <string name="vod_pick_local_file">选择本地文件</string>
<string name="vod_play_asset_sample">播放包内示例</string> <string name="vod_play_asset_sample">播放包内示例</string>
<string name="vod_online_resources">在线资源</string>
<string name="vod_list_loading">加载中…</string>
<string name="vod_list_error">加载失败,点击重试</string>
<string name="vod_list_empty">暂无在线资源</string>
<string name="protocol_rtmp">RTMP</string> <string name="protocol_rtmp">RTMP</string>
<string name="protocol_rtc">RTC</string> <string name="protocol_rtc">RTC</string>