[宣传视频] refactor: 宣传视频播放重构 step2 ;

This commit is contained in:
aibingbing
2023-12-12 18:12:13 +08:00
parent 606cab9fe0
commit 600548ea9d
7 changed files with 323 additions and 20 deletions

View File

@@ -19,6 +19,12 @@ class OchCommonConst {
fun getSweeperUrl(): String {
return FunctionBuildConfig.urlJson.sweeperUrl
}
@JvmStatic
fun getEagleMisUrl(): String {
return FunctionBuildConfig.urlJson.eagleMisUrl
}
// token 失效 重新获取token
const val WAIT_TAKEN = 100046

View File

@@ -1,6 +1,6 @@
package com.mogo.och.common.module.wigets.video
data class AdsDatas(val ads: MutableList<MediaItem>)
data class MediaDataList(val ads: MutableList<MediaItem>)
data class MediaItem(
var path: String,

View File

@@ -0,0 +1,258 @@
package com.mogo.och.common.module.wigets.video
import android.annotation.SuppressLint
import android.content.Context
import android.text.TextUtils
import com.google.gson.reflect.TypeToken
import com.mogo.commons.AbsMogoApplication
import com.mogo.eagle.core.data.BaseData
import com.mogo.eagle.core.data.config.FunctionBuildConfig
import com.mogo.eagle.core.function.call.telematic.CallerTelematicManager
import com.mogo.eagle.core.network.MoGoRetrofitFactory
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger
import com.mogo.eagle.core.utilcode.util.GsonUtils
import com.mogo.eagle.core.utilcode.util.NetworkUtils
import com.mogo.eagle.core.utilcode.util.UiThreadHandler
import com.mogo.och.common.module.biz.constant.OchCommonConst
import com.mogo.och.common.module.biz.network.OchCommonServiceCallback
import com.mogo.och.common.module.biz.network.OchCommonSubscribeImpl
import com.mogo.och.common.module.biz.network.interceptor.transformTry
import com.mogo.och.common.module.wigets.video.MediaItem.Companion.MEDIA_TYPE_IMAGE
import com.mogo.och.common.module.wigets.video.MediaItem.Companion.MEDIA_TYPE_VIDEO
import io.reactivex.Observable
import retrofit2.http.GET
import retrofit2.http.Headers
import retrofit2.http.Query
import java.util.concurrent.ConcurrentHashMap
/**
* 广告视频数据源 管理类
* 1.第一优先级:从管理后台拿对应车型的宣传视频
* 如果无网络或无司机SN或请求异常 失败5次后立马使用第二优先级本地数据播放
* 失败尝试时间间隔1秒
* 2.第二优先级:使用本地数据播放
* 3.请求管理后台数据成功后间隔10分钟再次检查
*/
object AdDataSourceManager {
private val TAG = AdDataSourceManager::class.java.simpleName
private const val RETRY_MAX_COUNT = 5
private var mRetryCount = 0
private val mNetworkService: IAdNetworkApi =
MoGoRetrofitFactory.getInstance(OchCommonConst.getEagleMisUrl())
.create(IAdNetworkApi::class.java)
private var driverSnCache = ""
private val driverSn: String
get() {
val serverToken = CallerTelematicManager.getServerToken()
if (serverToken != driverSnCache && serverToken.isNotEmpty()) {
driverSnCache = serverToken
}
return driverSnCache
}
val context: Context
get() {
return AbsMogoApplication.getApp()
}
private val mLastAdDataSourceList = mutableListOf<MediaItem>()
private var mHasEverGetAdDataFromMis = false
private val mAdDataSourceListenerMap: ConcurrentHashMap<String, IAdDataSourceListener> =
ConcurrentHashMap()
private val getAdDataSourceLoopRunnable = Runnable {
startGetAdDataSourceLoop()
}
fun init(tag: String, dataSourceListener: IAdDataSourceListener) {
if (!mAdDataSourceListenerMap.containsKey(tag)) {
mAdDataSourceListenerMap[tag] = dataSourceListener
}
startGetAdDataSourceLoop()
}
fun unInit(tag: String) {
removeGetAdDataSourceLoop()
if (mAdDataSourceListenerMap.containsKey(tag)) {
mAdDataSourceListenerMap.remove(tag)
}
}
@SuppressLint("MissingPermission")
private fun startGetAdDataSourceLoop() {
removeGetAdDataSourceLoop()
// 失败3次且从来没有从MIS获取配置信息成功过先试用本地数据播放
if (mRetryCount == RETRY_MAX_COUNT && !mHasEverGetAdDataFromMis) {
val localAdDataList = getAdDataFromLocalConfig()
updateAdDataSource(localAdDataList)
CallerLogger.e(TAG,
"startGetAdDataSourceLoop失败${mRetryCount}次,先使用本地数据播放"
)
}
if (driverSn.isBlank()) {
CallerLogger.e(TAG, "startGetAdDataSourceLoop司机屏sn为空跳过本次查询"
)
mRetryCount++
UiThreadHandler.postDelayed(getAdDataSourceLoopRunnable, 1000L)
return
}
if (!NetworkUtils.isConnected()) {
CallerLogger.e(TAG, "startGetAdDataSourceLoop当前无网络跳过本次查询"
)
mRetryCount++
UiThreadHandler.postDelayed(getAdDataSourceLoopRunnable, 1000L)
return
}
getAdDataFromMis(object : OchCommonServiceCallback<AdDataResp> {
override fun onSuccess(data: AdDataResp?) {
mHasEverGetAdDataFromMis = true
CallerLogger.e(TAG,
"startGetAdDataSourceLoopsuccess, 从管理后台获取到数据AdData=${
GsonUtils.toJson(
data
)
}"
)
val newDataList = AdDataResp.toMediaItemList(data?.data)
// 管理平台如果配置数据为空,不更新
if (newDataList.isNotEmpty()) {
if (compareAdDataSource(newDataList)) {
updateAdDataSource(newDataList)
CallerLogger.e(TAG,
"startGetAdDataSourceLoopsuccess, 从管理后台获取到数据,更新数据"
)
} else {
CallerLogger.e(TAG,
"startGetAdDataSourceLoopsuccess, 从管理后台获取到数据,数据无变化"
)
}
}
// 获取成功后延迟5分钟再查询
UiThreadHandler.postDelayed(getAdDataSourceLoopRunnable, 5 * 60 * 1000L)
}
override fun onFail(code: Int, msg: String?) {
CallerLogger.e(TAG,
"startGetAdDataSourceLoopfailed, code=$code, msg=$msg"
)
mRetryCount++
val delay = if (mHasEverGetAdDataFromMis) 5000L else 1000L
UiThreadHandler.postDelayed(getAdDataSourceLoopRunnable, delay)
}
override fun onError() {
super.onError()
CallerLogger.e(TAG, "startGetAdDataSourceLooperror, 网络异常"
)
mRetryCount++
val delay = if (mHasEverGetAdDataFromMis) 5000L else 1000L
UiThreadHandler.postDelayed(getAdDataSourceLoopRunnable, delay)
}
})
}
private fun removeGetAdDataSourceLoop() {
UiThreadHandler.removeCallbacks(getAdDataSourceLoopRunnable)
}
private fun getAdDataFromMis(callback: OchCommonServiceCallback<AdDataResp>) {
CallerLogger.d(TAG, "getAdDataFromMis准备发送请求driverSn=$driverSn"
)
mNetworkService.queryAdDataFromMis(
sn = driverSn,
screenType = "2",
).transformTry().subscribe(OchCommonSubscribeImpl(context, callback, "getAdDataFromMis"))
}
private fun getAdDataFromLocalConfig(): List<MediaItem> {
val localAdDataList = mutableListOf<MediaItem>()
try {
val datas: MediaDataList = GsonUtils.fromJson(
FunctionBuildConfig.tempConfig, object : TypeToken<MediaDataList>() {}.type
)
localAdDataList.addAll(datas.ads)
} catch (e: Exception) {
e.printStackTrace()
}
return localAdDataList
}
private fun compareAdDataSource(newDataList: List<MediaItem>): Boolean {
if (mLastAdDataSourceList.isEmpty() && newDataList.isNotEmpty()) {
return true
}
if (mLastAdDataSourceList.size != newDataList.size) {
return true
}
try {
newDataList.forEachIndexed { index, rotationItem ->
val oldIndexItem = mLastAdDataSourceList[index]
if (rotationItem?.path != oldIndexItem?.path) {
return true
}
}
} catch (e: Exception) {
e.printStackTrace()
}
return false
}
private fun updateAdDataSource(newDataList: List<MediaItem>) {
mLastAdDataSourceList.clear()
mLastAdDataSourceList.addAll(newDataList)
mAdDataSourceListenerMap.forEach {
val listener = it.value
listener.onAdDataSourceChanged(newDataList)
}
}
}
interface IAdDataSourceListener {
fun onAdDataSourceChanged(list: List<MediaItem>)
}
interface IAdNetworkApi {
@Headers("Content-type:application/json;charset=UTF-8")
@GET("/platform/biz/adv/screen/advs")
fun queryAdDataFromMis(
@Query("sn") sn: String,
@Query("screenType") screenType: String
): Observable<AdDataResp>
}
data class AdData(
var id: String?,
var title: String?, //素材标题
var brand: String?,
var file_type: Int = 0, //素材类型: 1 - 视频,2 - 图片
var cover_path: String?, //封面图片, 适用 素材类型为视频
var file_path: String?, //素材url
var descr: String?, //素材描述
var apply_screen: Int = 0 //应用屏幕类型: 1司机屏幕 2乘客屏
)
data class AdDataResp(val data: List<AdData>) : BaseData() {
companion object {
fun toMediaItemList(adDataList: List<AdData>?): List<MediaItem> {
val rotationItemList = mutableListOf<MediaItem>()
adDataList?.forEach {
val rotationItem = MediaItem(
path = if (TextUtils.isEmpty(it.file_path)) "" else "${it.file_path}",
type = if (it.file_type == 1) MEDIA_TYPE_VIDEO else MEDIA_TYPE_IMAGE,
cacheImgPath = if (TextUtils.isEmpty(it.cover_path)) "" else "${it.cover_path}",
title = if (TextUtils.isEmpty(it.title)) "" else "${it.title}"
)
rotationItemList.add(rotationItem)
}
return rotationItemList
}
}
}

View File

@@ -12,6 +12,8 @@ import androidx.viewpager.widget.PagerAdapter
import androidx.viewpager.widget.ViewPager
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger
import com.mogo.eagle.core.utilcode.util.CountDownTimer
import com.mogo.eagle.core.utilcode.util.ToastUtils
import com.mogo.och.common.module.wigets.video.MediaLoopPlayView.Companion.IMAGE_COUNT_DOWN_SECONDS
import com.shuyu.gsyvideoplayer.listener.GSYSampleCallBack
class MediaLoopPlayView @JvmOverloads constructor(
@@ -20,6 +22,7 @@ class MediaLoopPlayView @JvmOverloads constructor(
companion object {
const val TAG = "MediaLoopPlayView"
const val IMAGE_COUNT_DOWN_SECONDS = 5
}
private var viewPager: AdvanceViewPager? = null
@@ -40,6 +43,10 @@ class MediaLoopPlayView @JvmOverloads constructor(
pagerAdapter?.setMediaData(list)
}
fun setNewMediaData(list: MutableList<MediaItem>){
pagerAdapter?.setNewMediaData(list)
}
fun setPause() {
pagerAdapter?.setPause()
}
@@ -70,6 +77,8 @@ class AdvancePagerAdapter(context: Context, viewPager: ViewPager) : PagerAdapter
private var mDataList = mutableListOf<MediaItem>()
private var mItemViewList = mutableListOf<View>()
//新的数据,在轮播下一次切换的时机完成整体数据的更新
private val mNewDataList: MutableList<MediaItem> = mutableListOf()
private var mLastViewPagerPosition = -1
private var mImageCountDownTimer: CountDownTimer? = null
@@ -80,6 +89,7 @@ class AdvancePagerAdapter(context: Context, viewPager: ViewPager) : PagerAdapter
return
}
mDataList.clear()
mDataList.addAll(list)
mItemViewList.clear()
list.forEach {
@@ -95,6 +105,11 @@ class AdvancePagerAdapter(context: Context, viewPager: ViewPager) : PagerAdapter
}
}
fun setNewMediaData(list: MutableList<MediaItem>){
mNewDataList.clear()
mNewDataList.addAll(list)
}
override fun getCount(): Int {
return mDataList.size
}
@@ -103,8 +118,13 @@ class AdvancePagerAdapter(context: Context, viewPager: ViewPager) : PagerAdapter
return view === `object`
}
override fun destroyItem(container: ViewGroup, position: Int, `object`: Any) {
container.removeView(mItemViewList[position])
override fun destroyItem(container: ViewGroup, position: Int, obj: Any) {
try {
container.removeView(obj as View)
} catch (e: Exception) {
e.printStackTrace()
}
}
override fun instantiateItem(container: ViewGroup, position: Int): Any {
@@ -184,7 +204,7 @@ class AdvancePagerAdapter(context: Context, viewPager: ViewPager) : PagerAdapter
mImageCountDownTimer?.cancel()
mImageCountDownTimer = null
}
mImageCountDownTimer = object : CountDownTimer(5000L, 1000L) {
mImageCountDownTimer = object : CountDownTimer(IMAGE_COUNT_DOWN_SECONDS * 1000L, 1000L) {
override fun onTick(millisUntilFinished: Long) {
CallerLogger.d(
MediaLoopPlayView.TAG,
@@ -210,6 +230,14 @@ class AdvancePagerAdapter(context: Context, viewPager: ViewPager) : PagerAdapter
* 根据当前item情况播放下一个item
*/
private fun playNextItemView(isOnVideoError: Boolean) {
// 在播放完成的时机更新整体数据
if (mNewDataList.isNotEmpty()) {
setMediaData(mNewDataList)
mNewDataList.clear()
ToastUtils.showShort("宣传视频数据已更新")
return
}
val currentPosition = mViewPager.currentItem
val currentMediaItem = mDataList[currentPosition]
val currentItemView = mItemViewList[currentPosition]

View File

@@ -1,10 +1,10 @@
package com.mogo.och.common.module.wigets.video
import com.google.gson.reflect.TypeToken
import com.mogo.commons.mvp.MvpFragment
import com.mogo.commons.mvp.Presenter
import com.mogo.eagle.core.data.config.FunctionBuildConfig
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger
import com.mogo.eagle.core.utilcode.util.GsonUtils
import com.mogo.eagle.core.utilcode.util.UiThreadHandler
import com.mogo.och.common.module.R
import kotlinx.android.synthetic.main.fragment_video_player.imageVideoRotationView
@@ -34,8 +34,23 @@ class VideoPlayerFragment :
}
override fun initViews() {
initResourceData()
imageVideoRotationView.setMediaData(arrayListOf)
AdDataSourceManager.init(TAG, object : IAdDataSourceListener {
override fun onAdDataSourceChanged(list: List<MediaItem>) {
val isNewData = arrayListOf.isNotEmpty()
CallerLogger.d(
TAG, "onAdDataSourceChangedisNewData=$isNewData, list=${GsonUtils.toJson(list)}"
)
arrayListOf.clear()
arrayListOf.addAll(list)
UiThreadHandler.post {
if (isNewData) {
imageVideoRotationView.setNewMediaData(arrayListOf)
} else {
imageVideoRotationView.setMediaData(arrayListOf)
}
}
}
})
}
override fun onPause() {
@@ -48,13 +63,9 @@ class VideoPlayerFragment :
imageVideoRotationView.setResume()
}
private fun initResourceData() {
try {
arrayListOf.clear()
var datas: AdsDatas = GsonUtils.fromJson(FunctionBuildConfig.tempConfig,object : TypeToken<AdsDatas>() {}.type)
arrayListOf.addAll(datas.ads)
} catch (e: Exception) {
}
override fun onDestroy() {
AdDataSourceManager.unInit(TAG)
super.onDestroy()
}
}

View File

@@ -6,7 +6,7 @@ import com.mogo.eagle.core.data.config.FunctionBuildConfig
import com.mogo.eagle.core.utilcode.util.GsonUtils
import com.mogo.och.bus.passenger.R
import com.mogo.och.bus.passenger.presenter.PM2VideoPresenter
import com.mogo.och.common.module.wigets.video.AdsDatas
import com.mogo.och.common.module.wigets.video.MediaDataList
import com.mogo.och.common.module.wigets.video.MediaItem
import kotlinx.android.synthetic.m2.p_m2_video_fragment.*
@@ -55,7 +55,7 @@ class PM2VideoFragment :
try {
arrayListOf.clear()
var datas: AdsDatas = GsonUtils.fromJson(FunctionBuildConfig.tempConfig,object : TypeToken<AdsDatas>() {}.type)
var datas: MediaDataList = GsonUtils.fromJson(FunctionBuildConfig.tempConfig,object : TypeToken<MediaDataList>() {}.type)
arrayListOf.addAll(datas.ads)
} catch (e: Exception) {
e.printStackTrace()

View File

@@ -342,7 +342,7 @@
}
},
"saas": {
"shuttlepassenger": {
"shuttlepassengerochjl": {
"ads": [
{
"path": "https://img.zhidaohulian.com/fileServer/online_car_hailing/1676357256102/1.jpg",
@@ -406,7 +406,7 @@
}
]
},
"buspassenger": {
"buspassengerochjl": {
"ads": [
{
"path": "https://img.zhidaohulian.com/fileServer/online_car_hailing/1676357256102/1.jpg",
@@ -470,7 +470,7 @@
}
]
},
"shuttlepassengerm2": {
"shuttlepassengerochm2": {
"ads": [
{
"path": "https://img.zhidaohulian.com/fileServer/online_car_hailing/1681716116231/6923474a99a1983c9a0410ad3357888d.mov",