[宣传视频] refactor: 宣传视频播放重构 step2 ;
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -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,
|
||||
@@ -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,
|
||||
"startGetAdDataSourceLoop:success, 从管理后台获取到数据,AdData=${
|
||||
GsonUtils.toJson(
|
||||
data
|
||||
)
|
||||
}"
|
||||
)
|
||||
val newDataList = AdDataResp.toMediaItemList(data?.data)
|
||||
// 管理平台如果配置数据为空,不更新
|
||||
if (newDataList.isNotEmpty()) {
|
||||
if (compareAdDataSource(newDataList)) {
|
||||
updateAdDataSource(newDataList)
|
||||
CallerLogger.e(TAG,
|
||||
"startGetAdDataSourceLoop:success, 从管理后台获取到数据,更新数据"
|
||||
)
|
||||
} else {
|
||||
CallerLogger.e(TAG,
|
||||
"startGetAdDataSourceLoop:success, 从管理后台获取到数据,数据无变化"
|
||||
)
|
||||
}
|
||||
}
|
||||
// 获取成功后,延迟5分钟再查询
|
||||
UiThreadHandler.postDelayed(getAdDataSourceLoopRunnable, 5 * 60 * 1000L)
|
||||
}
|
||||
|
||||
override fun onFail(code: Int, msg: String?) {
|
||||
CallerLogger.e(TAG,
|
||||
"startGetAdDataSourceLoop:failed, code=$code, msg=$msg"
|
||||
)
|
||||
mRetryCount++
|
||||
val delay = if (mHasEverGetAdDataFromMis) 5000L else 1000L
|
||||
UiThreadHandler.postDelayed(getAdDataSourceLoopRunnable, delay)
|
||||
}
|
||||
|
||||
override fun onError() {
|
||||
super.onError()
|
||||
CallerLogger.e(TAG, "startGetAdDataSourceLoop:error, 网络异常"
|
||||
)
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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]
|
||||
|
||||
@@ -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, "onAdDataSourceChanged:isNewData=$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()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user