添加在线资源加载功能,更新VOD对话框和相关逻辑,支持动态获取播放格式
This commit is contained in:
Binary file not shown.
@@ -3,11 +3,17 @@ package com.demo.SellyCloudSDK
|
||||
import android.app.Dialog
|
||||
import android.content.Intent
|
||||
import android.graphics.Rect
|
||||
import android.graphics.Typeface
|
||||
import android.os.Bundle
|
||||
import android.util.TypedValue
|
||||
import android.view.Gravity
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.GridLayout
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
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.login.DemoLoginStore
|
||||
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.sellycloud.sellycloudsdk.SellyLiveMode
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
@@ -499,9 +507,96 @@ class FeatureHubActivity : AppCompatActivity() {
|
||||
startActivity(VodPlayActivity.createIntent(this, "asset:///vod/sample.mp4"))
|
||||
}
|
||||
|
||||
loadVodList(dialogBinding)
|
||||
|
||||
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() {
|
||||
binding.btnSaveSettings.setOnClickListener {
|
||||
val settings = uiToSettingsOrNull() ?: return@setOnClickListener
|
||||
|
||||
@@ -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 ?: "网络请求失败")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
6
example/src/main/res/drawable/selector_av_vod_chip.xml
Normal file
6
example/src/main/res/drawable/selector_av_vod_chip.xml
Normal 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>
|
||||
@@ -17,78 +17,142 @@
|
||||
android:src="@drawable/ic_av_close"
|
||||
app:tint="@color/av_text_primary" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/card"
|
||||
<ScrollView
|
||||
android:layout_width="320dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:background="@drawable/bg_av_dialog_card_gray"
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="18dp"
|
||||
android:paddingTop="16dp"
|
||||
android:paddingEnd="18dp"
|
||||
android:paddingBottom="18dp">
|
||||
android:scrollbars="none">
|
||||
|
||||
<TextView
|
||||
<LinearLayout
|
||||
android:id="@+id/card"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:text="@string/vod_config_title"
|
||||
android:textColor="@color/av_text_primary"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold" />
|
||||
android:background="@drawable/bg_av_dialog_card_gray"
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="18dp"
|
||||
android:paddingTop="16dp"
|
||||
android:paddingEnd="18dp"
|
||||
android:paddingBottom="18dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="14dp"
|
||||
android:text="@string/vod_config_hint"
|
||||
android:textColor="@color/av_text_secondary"
|
||||
android:textSize="12sp" />
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:text="@string/vod_config_title"
|
||||
android:textColor="@color/av_text_primary"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/etVodUrl"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/av_field_height"
|
||||
android:layout_marginTop="8dp"
|
||||
android:background="@drawable/bg_av_input_field"
|
||||
android:importantForAutofill="no"
|
||||
android:inputType="textUri"
|
||||
android:paddingStart="12dp"
|
||||
android:paddingEnd="12dp"
|
||||
android:textColor="@color/av_text_primary"
|
||||
android:textColorHint="@color/av_text_hint"
|
||||
android:textSize="14sp" />
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="14dp"
|
||||
android:text="@string/vod_config_hint"
|
||||
android:textColor="@color/av_text_secondary"
|
||||
android:textSize="12sp" />
|
||||
|
||||
<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" />
|
||||
<EditText
|
||||
android:id="@+id/etVodUrl"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/av_field_height"
|
||||
android:layout_marginTop="8dp"
|
||||
android:background="@drawable/bg_av_input_field"
|
||||
android:importantForAutofill="no"
|
||||
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
|
||||
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" />
|
||||
<!-- VOD Online Resources Section -->
|
||||
<LinearLayout
|
||||
android:id="@+id/vodListSection"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<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>
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
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>
|
||||
|
||||
@@ -90,6 +90,10 @@
|
||||
<string name="vod_config_hint">请输入播放地址(URL、本地路径、asset:///...)</string>
|
||||
<string name="vod_pick_local_file">选择本地文件</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_rtc">RTC</string>
|
||||
|
||||
Reference in New Issue
Block a user