添加在线资源加载功能,更新VOD对话框和相关逻辑,支持动态获取播放格式
This commit is contained in:
Binary file not shown.
@@ -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
|
||||||
|
|||||||
@@ -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"
|
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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user