diff --git a/OCH/mogo-och-common-module/src/main/java/com/mogo/och/common/module/biz/constant/OchCommonConst.kt b/OCH/mogo-och-common-module/src/main/java/com/mogo/och/common/module/biz/constant/OchCommonConst.kt index e110fa20ec..e6fec1f2d5 100644 --- a/OCH/mogo-och-common-module/src/main/java/com/mogo/och/common/module/biz/constant/OchCommonConst.kt +++ b/OCH/mogo-och-common-module/src/main/java/com/mogo/och/common/module/biz/constant/OchCommonConst.kt @@ -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 diff --git a/OCH/mogo-och-common-module/src/main/java/com/mogo/och/common/module/wigets/video/AdsDatas.kt b/OCH/mogo-och-common-module/src/main/java/com/mogo/och/common/module/wigets/video/MediaBean.kt similarity index 88% rename from OCH/mogo-och-common-module/src/main/java/com/mogo/och/common/module/wigets/video/AdsDatas.kt rename to OCH/mogo-och-common-module/src/main/java/com/mogo/och/common/module/wigets/video/MediaBean.kt index c159360ffe..25bc768391 100644 --- a/OCH/mogo-och-common-module/src/main/java/com/mogo/och/common/module/wigets/video/AdsDatas.kt +++ b/OCH/mogo-och-common-module/src/main/java/com/mogo/och/common/module/wigets/video/MediaBean.kt @@ -1,6 +1,6 @@ package com.mogo.och.common.module.wigets.video -data class AdsDatas(val ads: MutableList) +data class MediaDataList(val ads: MutableList) data class MediaItem( var path: String, diff --git a/OCH/mogo-och-common-module/src/main/java/com/mogo/och/common/module/wigets/video/MediaDataSourceManager.kt b/OCH/mogo-och-common-module/src/main/java/com/mogo/och/common/module/wigets/video/MediaDataSourceManager.kt new file mode 100644 index 0000000000..f893fff92b --- /dev/null +++ b/OCH/mogo-och-common-module/src/main/java/com/mogo/och/common/module/wigets/video/MediaDataSourceManager.kt @@ -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() + + private var mHasEverGetAdDataFromMis = false + + private val mAdDataSourceListenerMap: ConcurrentHashMap = + 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 { + 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) { + CallerLogger.d(TAG, "getAdDataFromMis:准备发送请求,driverSn=$driverSn" + ) + mNetworkService.queryAdDataFromMis( + sn = driverSn, + screenType = "2", + ).transformTry().subscribe(OchCommonSubscribeImpl(context, callback, "getAdDataFromMis")) + } + + private fun getAdDataFromLocalConfig(): List { + val localAdDataList = mutableListOf() + try { + val datas: MediaDataList = GsonUtils.fromJson( + FunctionBuildConfig.tempConfig, object : TypeToken() {}.type + ) + localAdDataList.addAll(datas.ads) + } catch (e: Exception) { + e.printStackTrace() + } + return localAdDataList + } + + private fun compareAdDataSource(newDataList: List): 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) { + mLastAdDataSourceList.clear() + mLastAdDataSourceList.addAll(newDataList) + mAdDataSourceListenerMap.forEach { + val listener = it.value + listener.onAdDataSourceChanged(newDataList) + } + } + +} + +interface IAdDataSourceListener { + fun onAdDataSourceChanged(list: List) +} + +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 +} + +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) : BaseData() { + companion object { + fun toMediaItemList(adDataList: List?): List { + val rotationItemList = mutableListOf() + 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 + } + } +} diff --git a/OCH/mogo-och-common-module/src/main/java/com/mogo/och/common/module/wigets/video/MediaLoopPlayView.kt b/OCH/mogo-och-common-module/src/main/java/com/mogo/och/common/module/wigets/video/MediaLoopPlayView.kt index 5a5b7d9b45..68c824818d 100644 --- a/OCH/mogo-och-common-module/src/main/java/com/mogo/och/common/module/wigets/video/MediaLoopPlayView.kt +++ b/OCH/mogo-och-common-module/src/main/java/com/mogo/och/common/module/wigets/video/MediaLoopPlayView.kt @@ -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){ + pagerAdapter?.setNewMediaData(list) + } + fun setPause() { pagerAdapter?.setPause() } @@ -70,6 +77,8 @@ class AdvancePagerAdapter(context: Context, viewPager: ViewPager) : PagerAdapter private var mDataList = mutableListOf() private var mItemViewList = mutableListOf() + //新的数据,在轮播下一次切换的时机完成整体数据的更新 + private val mNewDataList: MutableList = 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){ + 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] diff --git a/OCH/mogo-och-common-module/src/main/java/com/mogo/och/common/module/wigets/video/VideoPlayerFragment.kt b/OCH/mogo-och-common-module/src/main/java/com/mogo/och/common/module/wigets/video/VideoPlayerFragment.kt index 3748d81cf8..ba5ac18a4f 100644 --- a/OCH/mogo-och-common-module/src/main/java/com/mogo/och/common/module/wigets/video/VideoPlayerFragment.kt +++ b/OCH/mogo-och-common-module/src/main/java/com/mogo/och/common/module/wigets/video/VideoPlayerFragment.kt @@ -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) { + 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() {}.type) - arrayListOf.addAll(datas.ads) - } catch (e: Exception) { - } + override fun onDestroy() { + AdDataSourceManager.unInit(TAG) + super.onDestroy() } } diff --git a/OCH/shuttle/passenger/src/m2/java/com/mogo/och/bus/passenger/ui/video/PM2VideoFragment.kt b/OCH/shuttle/passenger/src/m2/java/com/mogo/och/bus/passenger/ui/video/PM2VideoFragment.kt index 0d6923b72b..26fbd734d2 100644 --- a/OCH/shuttle/passenger/src/m2/java/com/mogo/och/bus/passenger/ui/video/PM2VideoFragment.kt +++ b/OCH/shuttle/passenger/src/m2/java/com/mogo/och/bus/passenger/ui/video/PM2VideoFragment.kt @@ -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() {}.type) + var datas: MediaDataList = GsonUtils.fromJson(FunctionBuildConfig.tempConfig,object : TypeToken() {}.type) arrayListOf.addAll(datas.ads) } catch (e: Exception) { e.printStackTrace() diff --git a/app/config/tempConfig.json b/app/config/tempConfig.json index 6b5b6e6a76..fc00dcab9a 100644 --- a/app/config/tempConfig.json +++ b/app/config/tempConfig.json @@ -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",