Commit fa2428dd authored by jyx's avatar jyx

添加腾讯播放器依赖,更新播放器

parent d23e5db4
......@@ -236,7 +236,7 @@ dependencies {
// 穿山甲聚合 接入【穿山甲、快手】
// 穿山甲
implementation ("com.pangle.cn:pangrowth-sdk:3.7.0.1") {
implementation("com.pangle.cn:pangrowth-sdk:3.7.0.1") {
exclude group: 'com.pangle.cn', module: 'pangrowth-game-sdk'
exclude group: 'com.pangle.cn', module: 'pangrowth-novel-sdk'
exclude group: 'com.pangle.cn', module: 'pangrowth-luckycat-sdk'
......@@ -246,6 +246,9 @@ dependencies {
exclude group: 'com.tencent.mm.opensdk', module: 'wechat-sdk-android'
}
// 腾讯播放器
implementation "com.tencent.liteav:LiteAVSDK_Player:latest.release"
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
......
......@@ -488,4 +488,8 @@
-keep class tv.danmaku.ijk.media.player.** {*; }
-dontwarn tv.danmaku.ijk.media.player.*
-keep interface tv.danmaku.ijk.media.player.** { *; }
\ No newline at end of file
-keep interface tv.danmaku.ijk.media.player.** { *; }
# ======================= 腾讯播放器 END ================
-keep class com.tencent.** { *; }
# ======================= 腾讯播放器 END ================
......@@ -147,6 +147,13 @@
android:exported="false"
android:screenOrientation="portrait" />
<activity
android:name=".video.TxVideoActivity"
android:exported="false"
android:hardwareAccelerated="true"
android:screenOrientation="portrait" />
<service
android:name=".ui.service.UpdateService"
android:exported="true" />
......
......@@ -12,6 +12,7 @@ import com.downloader.PRDownloader;
import com.downloader.PRDownloaderConfig;
import com.xinfu.helivideo.ad.TTGroMoreAdManagerHolder;
import com.xinfu.helivideo.common.Constant;
import com.xinfu.helivideo.manager.TXCSDKService;
import com.xinfu.helivideo.manager.UmengManager;
import com.xinfu.helivideo.net.LoanService;
import com.xinfu.helivideo.net.V6Service;
......@@ -42,7 +43,7 @@ public class MintsApplication extends MultiDexApplication {
private V6Service v6Service;
private HttpProxyCacheServer proxy = null;
public static class StaticParams {
public static class StaticParams {
public static HttpProxyCacheServer getProxy() {
MintsApplication app = (MintsApplication) MintsApplication.getContext();
return app.proxy == null ? app.newProxy() : app.proxy;
......@@ -97,6 +98,8 @@ public class MintsApplication extends MultiDexApplication {
TTGroMoreAdManagerHolder.init(this);
TXCSDKService.init(this);
// 判断应用是否在前台
ForegroundOrBackground.init(this);
......
......@@ -13,6 +13,7 @@ import com.xinfu.helivideo.mvp.model.VedioBean
import com.xinfu.helivideo.utils.AppPreferencesManager
import com.xinfu.helivideo.utils.ToastUtil
import com.xinfu.helivideo.video.DramaApiDetailActivity
import com.xinfu.helivideo.video.TxVideoActivity
import com.xinfu.helivideo.video.VideoActivity
/**
......@@ -94,7 +95,8 @@ object LocalVedioManager {
bundle.putString(Constant.VEDIO_BEAN, JsonUtil.toJson(data))
bundle.putBoolean(Constant.VEDIO_NEXT, isPlayNext)
bundle.putBoolean(Constant.VEDIO_THIRD, isThirdId)
readyGo(activity, VideoActivity::class.java, bundle)
// readyGo(activity, VideoActivity::class.java, bundle)
readyGo(activity, TxVideoActivity::class.java, bundle)
} else {
requestDrama(activity, data)
}
......
package com.xinfu.helivideo.manager;
import android.content.Context;
import com.tencent.rtmp.TXLiveBase;
import com.tencent.rtmp.TXLiveBaseListener;
public class TXCSDKService {
private static final String TAG = "TXCSDKService";
// 如何获取License? 请参考官网指引 https://cloud.tencent.com/document/product/454/34750
private static final String licenceUrl =
"https://license.vod2.myqcloud.com/license/v2/1317803579_1/v_cube.license";
private static final String licenseKey = "87b2b95939cfe9949749a9bdc2e34702";
private TXCSDKService() {
}
/**
* 初始化腾讯云相关sdk。
* SDK 初始化过程中可能会读取手机型号等敏感信息,需要在用户同意隐私政策后,才能获取。
*
* @param appContext The application context.
*/
public static void init(Context appContext) {
TXLiveBase.getInstance().setLicence(appContext, licenceUrl, licenseKey);
TXLiveBase.setListener(new TXLiveBaseListener() {
@Override
public void onUpdateNetworkTime(int errCode, String errMsg) {
if (errCode != 0) {
TXLiveBase.updateNetworkTime();
}
}
});
TXLiveBase.updateNetworkTime();
// 短视频licence设置
}
}
\ No newline at end of file
package com.xinfu.helivideo.manager;
import android.text.TextUtils;
import com.xinfu.helivideo.MintsApplication;
import com.xinfu.helivideo.mvp.model.VedioBean;
import com.xinfu.helivideo.mvp.presenters.TrackPresenter;
......@@ -47,13 +44,25 @@ public class TrackManager {
public void commitVedio(VedioBean data) {
if (trackPresenter != null) {
HashMap<String, Object> vo = new HashMap<>();
vo.put("vedioIndex",data.getSeeIndex());
vo.put("vedioId",data.getVedioId());
vo.put("thirdId",data.getThirdId());
vo.put("vedioIndex", data.getSeeIndex());
vo.put("vedioId", data.getVedioId());
vo.put("thirdId", data.getThirdId());
trackPresenter.commitVedio(vo);
}
}
public void collect(String vedioId) {
if (trackPresenter != null) {
trackPresenter.collect(vedioId);
}
}
public void cancelCollect(String vedioId) {
if (trackPresenter != null) {
trackPresenter.cancelCollect(vedioId);
}
}
public void getMyInfo() {
if (trackPresenter != null) {
trackPresenter.getMyInfo();
......
......@@ -6,7 +6,6 @@ import com.google.gson.JsonObject;
import com.xinfu.library.net.neterror.BaseSubscriber;
import com.xinfu.library.net.neterror.Throwable;
import com.xinfu.helivideo.MintsApplication;
import com.xinfu.helivideo.common.AppConfig;
import com.xinfu.helivideo.common.DeviceInfo;
import com.xinfu.helivideo.manager.AppHttpManager;
import com.xinfu.helivideo.manager.UserManager;
......@@ -157,4 +156,39 @@ public class TrackPresenter extends BaseTrackPresenter {
}
public void collect(String vedioId) {
HashMap vo = new HashMap<String, Object>();
vo.put("vedioId", vedioId);
AppHttpManager.getInstance(loanApplication)
.call(loanService.collect(vo), new BaseSubscriber<BaseResponse<JsonObject>>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
}
});
}
public void cancelCollect(String vedioId) {
HashMap vo = new HashMap<String, Object>();
vo.put("vedioId", vedioId);
AppHttpManager.getInstance(loanApplication)
.call(loanService.cancelCollect(vo), new BaseSubscriber<BaseResponse<JsonObject>>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
}
});
}
}
......@@ -24,15 +24,9 @@ import com.xinfu.helivideo.mvp.views.HomeView
import com.xinfu.helivideo.ui.adapter.HomeVideoPageAdapter
import com.xinfu.helivideo.ui.adapter.ImageTitleAdapter
import com.xinfu.helivideo.ui.adapter.TopAdapter
import com.xinfu.helivideo.ui.fragment.base.BaseFragment
import com.xinfu.helivideo.ui.fragment.base.LazyLoadBaseFragment
import com.xinfu.helivideo.video.VideoActivity
import com.scwang.smartrefresh.layout.api.RefreshLayout
import com.scwang.smartrefresh.layout.listener.OnRefreshListener
import com.youth.banner.Banner
import com.youth.banner.adapter.BannerImageAdapter
import com.youth.banner.holder.BannerImageHolder
import com.youth.banner.indicator.CircleIndicator
import kotlinx.android.synthetic.main.fragment_main.*
import java.util.*
import kotlin.concurrent.schedule
......
......@@ -32,6 +32,7 @@ import com.xinfu.helivideo.ui.widgets.DialogListener
import com.xinfu.helivideo.ui.widgets.VideoEpisodeDialog
import com.xinfu.helivideo.ui.widgets.VipCountDialog
import com.xinfu.helivideo.utils.LogUtil
import com.xinfu.library.utils.nodoubleclick.AntiShake
import kotlinx.android.synthetic.main.drama_activity_api_detail.*
import kotlinx.android.synthetic.main.drama_activity_api_detail.episode_tv
import kotlinx.android.synthetic.main.drama_activity_api_detail.fm_bottom
......@@ -119,6 +120,7 @@ class DramaApiDetailActivity : BaseActivity(), VideoEpisodeAdapter.OnEpisodeClic
videoPresenter.attachView(this)
fm_bottom.setOnClickListener {
if (AntiShake.check(it.id)) return@setOnClickListener
showEpisodeDialog()
}
......@@ -492,7 +494,7 @@ class DramaApiDetailActivity : BaseActivity(), VideoEpisodeAdapter.OnEpisodeClic
private var dialog: VideoEpisodeDialog? = null
private fun showEpisodeDialog() {
if (mVedioBean == null) return
if (mVedioBean == null && dialog != null && dialog!!.isShowing) return
dialog = VideoEpisodeDialog(this, mVedioBean, object : DialogListener() {
override fun onClick(dialog: Dialog?, v: View?) {
......
package com.xinfu.helivideo.video
import android.app.Dialog
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.view.View
import com.google.gson.Gson
import com.xinfu.helivideo.R
import com.xinfu.helivideo.ad.AdManager
import com.xinfu.helivideo.ad.AdStatusListener
import com.xinfu.helivideo.ad.NoPreAdManager
import com.xinfu.helivideo.common.Constant
import com.xinfu.helivideo.mvp.model.IndexList
import com.xinfu.helivideo.mvp.model.IndexList.VedioEpisodeBean
import com.xinfu.helivideo.mvp.model.VedioBean
import com.xinfu.helivideo.mvp.model.VideoMultiItemEntity
import com.xinfu.helivideo.mvp.presenters.VideoPresenter
import com.xinfu.helivideo.mvp.views.VideoView
import com.xinfu.helivideo.ui.activitys.VipActivity
import com.xinfu.helivideo.ui.activitys.base.BaseActivity
import com.xinfu.helivideo.ui.adapter.VideoEpisodeAdapter
import com.xinfu.helivideo.ui.widgets.DialogListener
import com.xinfu.helivideo.ui.widgets.VideoEpisodeDialog
import com.xinfu.helivideo.ui.widgets.VipCountDialog
import com.xinfu.helivideo.video.tx.adapter.TxVideoAdapter
import com.xinfu.library.utils.nodoubleclick.AntiShake
import kotlinx.android.synthetic.main.activity_tx_video.*
class TxVideoActivity : BaseActivity(), View.OnClickListener, VideoView,
VideoEpisodeAdapter.OnEpisodeClickListener, TxVideoAdapter.OnCustomChildClickListener {
var videos = ArrayList<VedioEpisodeBean>()
private var dialog: VideoEpisodeDialog? = null
private val videoPresenter by lazy { VideoPresenter() }
private var mVedioBean: VedioBean? = null
private var indexBean: IndexList? = null
private var isPlayNext = false
private var playIndex = 1
override fun getContentViewLayoutID() = R.layout.activity_tx_video
override fun initViewsAndEvents() {
videoPresenter.attachView(this)
mVedioBean?.let { videoPresenter.getIndexList("" + it.vedioId) }
playIndex = if (isPlayNext) {
mVedioBean!!.recommendIndex
} else {
mVedioBean!!.seeIndex - 1
}
if (playIndex > mVedioBean!!.vedioTotal) playIndex = 0
initView()
}
override fun getBundleExtras(extras: Bundle?) {
val json = extras?.getString(Constant.VEDIO_BEAN)
isPlayNext = extras?.getBoolean(Constant.VEDIO_NEXT, false) == true
mVedioBean = Gson().fromJson(json, VedioBean::class.java)
super.getBundleExtras(extras)
}
override fun isApplyKitKatTranslucency() = false
override fun onResume() {
super.onResume()
super_short_video_view.resume()
}
override fun onPause() {
super.onPause()
super_short_video_view.pause()
}
override fun onDestroy() {
super_short_video_view.onDestroy()
super.onDestroy()
super_short_video_view.releasePlayer()
videoPresenter.detachView()
}
private fun initView() {
episode_tv.text = String.format(
"共%d集 %s",
mVedioBean?.vedioTotal,
if (mVedioBean?.completeStatus == 0) "已完结" else "更新中"
)
super_short_video_view.setOnCustomChildClickListener(this)
fm_bottom.setOnClickListener(this)
close_iv.setOnClickListener(this)
}
override fun onClick(v: View?) {
if (AntiShake.check(v?.id)) return
when (v?.id) {
R.id.close_iv -> {
finish()
}
R.id.fm_bottom -> {
showEpisodeDialog()
}
else -> {}
}
}
private fun showEpisodeDialog() {
if (dialog != null && dialog!!.isShowing) return
dialog = VideoEpisodeDialog(this, mVedioBean, object : DialogListener() {
override fun onClick(dialog: Dialog?, v: View?) {
super.onClick(dialog, v)
dialog?.dismiss()
}
})
dialog?.setCurrentIndex(getPosition())
dialog?.setOnEpisodeClickListener(this)
dialog?.show()
}
override fun getIndexListSuc(indexList: IndexList) {
// 处理推荐页跳转自动播放下一集逻辑
playIndex = if (isPlayNext) {
mVedioBean!!.recommendIndex - 1
} else {
mVedioBean!!.seeIndex - 1
}
if (playIndex > mVedioBean!!.vedioTotal) playIndex = 0
mVedioBean = indexList.vedioMsg
indexBean = indexList
videos.clear()
videos.addAll(indexList.list)
super_short_video_view.loadData(mVedioBean, getAdapterData(), playIndex)
}
override fun getIndexListFail() {
}
override fun collectSuc() {
mVedioBean!!.collect = 1
}
override fun collectFail() {
}
override fun cancelCollectSuc() {
mVedioBean!!.collect = 0
}
override fun cancelCollectFail() {
}
override fun unlockSuc(indexList: IndexList) {
videos.addAll(indexList.list)
mVedioBean!!.unlockIndex = videos.size
super_short_video_view.loadMoreData(getAdapterData())
}
override fun unlockFail() {
}
override fun onEpisodeClick(position: Int) {
dialog?.dismiss()
if (position >= mVedioBean!!.unlockIndex) {
super_short_video_view.onItemClick(mVedioBean!!.unlockIndex)
return
}
super_short_video_view.onItemClick(position)
Handler(Looper.getMainLooper()).postDelayed({
showVipCountDialog(position)
}, 300)
}
private fun getAdapterData(): MutableList<VideoMultiItemEntity> {
val data = mutableListOf<VideoMultiItemEntity>()
if (videos.size > 0) {
for (video in videos) {
val item = VideoMultiItemEntity(VideoMultiItemEntity.MULTI_ITEM_1, video)
data.add(item)
}
if (mVedioBean!!.unlockIndex < mVedioBean!!.vedioTotal) {
val item = VideoMultiItemEntity(VideoMultiItemEntity.MULTI_ITEM_2, videos[0])
data.add(item)
}
}
return data
}
override fun onCustomChildClick(view: View, position: Int) {
when (view.id) {
R.id.ll_collect -> {
// 收藏
mVedioBean?.let {
if (it.collect == 0) {
videoPresenter.collect("" + it.vedioId)
} else {
videoPresenter.cancelCollect("" + it.vedioId)
}
}
}
R.id.leave -> {
// 返回
finish()
}
R.id.unlock -> {
// 看广告解锁
AdManager.instance.showAd(this, "",
object : AdStatusListener {
var canFail = false
override fun adSuccess() {
canFail = true
}
override fun adFail() {
if (canFail) return
NoPreAdManager.loadVideoAd(
this@TxVideoActivity,
"", object : AdStatusListener {
override fun adFail() {
showToast("广告太火爆了,请稍候再试")
}
override fun adSuccess() {
}
override fun adClose(vo: HashMap<String, Any>?) {
vo?.let {
vo["vedioId"] = mVedioBean!!.vedioId
videoPresenter.unlock(vo)
}
}
})
}
override fun adClose(vo: HashMap<String, Any>?) {
vo?.let {
vo["vedioId"] = mVedioBean!!.vedioId
videoPresenter.unlock(vo)
}
}
})
}
R.id.vip -> {
// 开通VIP
val bundle = Bundle()
bundle.putString(VipActivity.VEDIO_ID, mVedioBean!!.vedioId.toString())
bundle.putString(VipActivity.THIRD_ID, mVedioBean!!.thirdId)
readyGo(VipActivity::class.java, bundle)
}
else -> {}
}
}
private fun showVipCountDialog(position: Int) {
if (isFinishing) return
if (position == 0 || mVedioBean!!.tipMaxIndex == 0 || mVedioBean!!.tipMaxIndex - 1 < position) {
return
}
VipCountDialog(this, mVedioBean!!)
.setOnDialogDismiss(object : VipCountDialog.OnDialogDismiss {
override fun onDialogDismiss() {
}
})
.show()
}
private fun getPosition() = super_short_video_view.currentPosition
}
\ No newline at end of file
package com.xinfu.helivideo.video
import java.io.Serializable
/**
* author : ChenWenJie
* email : 1181620038@qq.com
* date : 2020/9/22
* desc : 视频实体类
*/
class VideoBean : Serializable {
// ID
var id: Int = 0
//用户名
var user_name: String = ""
//用户头像
var user_image: String = ""
//视频标题
var video_title: String = ""
//视频路径
var video_path: String = ""
//视频图片
var video_image: String = ""
constructor(
id: Int,
user_name: String,
user_image: String,
video_title: String,
video_path: String,
video_image: String
) {
this.id = id
this.user_name = user_name
this.user_image = user_image
this.video_title = video_title
this.video_path = video_path
this.video_image = video_image
}
}
\ No newline at end of file
package com.xinfu.helivideo.video.tx;
import com.tencent.rtmp.TXLiveConstants;
import com.tencent.rtmp.TXVodConstants;
public class ConfigBean {
private boolean mIsEnableSelfAdaption;
private boolean mIsEnableHardWareDecode;
private int mLogLevel;
private boolean mIsUseDash;
private boolean mEnableAccurateSeek;
private boolean mSmoothSwitchBitrate;
private boolean mAutoRotate;
private int mConnectRetryInterval;
private boolean mEnableRenderProcess;
private int mProgressInterval;
private int mConnectRetryCount;
private int mTimeout;
private String mCacheFolderPath;
private int mMaxCacheItems;
private long mPreferredResolution;
private int mMediaType;
private int mMaxPreloadSize;
private int mMaxBufferSize;
private int mBitRateIndex;
private static volatile ConfigBean mInstance;
private ConfigBean() {
reset();
}
public static ConfigBean getInstance() {
if (mInstance == null) {
synchronized (ConfigBean.class) {
if (mInstance == null) {
mInstance = new ConfigBean();
}
}
}
return mInstance;
}
public void reset() {
setEnableAccurateSeek(true);
setSmoothSwitchBitrate(true);
setAutoRotate(true);
setEnableSelfAdaption(true);
setEnableRenderProcess(true);
setConnectRetryCount(3);
setConnectRetryInterval(3);
setTimeout(10);
setProgressInterval(500);
setCacheFolderPath(null);
setMaxCacheItems(500);
setMaxPreloadSize(50);
setMaxBufferSize(50);
setPreferredResolution(720 * 1280);
setMediaType(TXVodConstants.MEDIA_TYPE_AUTO);
setEnableHardWareDecode(true);
setLogLevel(TXLiveConstants.LOG_LEVEL_VERBOSE);
}
public boolean isEnableSelfAdaption() {
return mIsEnableSelfAdaption;
}
public void setEnableSelfAdaption(boolean isEnableSelfAdaption) {
this.mIsEnableSelfAdaption = isEnableSelfAdaption;
}
public boolean isEnableHardWareDecode() {
return mIsEnableHardWareDecode;
}
public void setEnableHardWareDecode(boolean enableHardWareDecode) {
this.mIsEnableHardWareDecode = enableHardWareDecode;
}
public int getLogLevel() {
return mLogLevel;
}
public void setLogLevel(int logLevel) {
this.mLogLevel = logLevel;
}
public boolean isIsUseDash() {
return mIsUseDash;
}
public void setsIsUseDash(boolean isUseDash) {
this.mIsUseDash = isUseDash;
}
public boolean isEnableAccurateSeek() {
return mEnableAccurateSeek;
}
public void setEnableAccurateSeek(boolean enableAccurateSeek) {
this.mEnableAccurateSeek = enableAccurateSeek;
}
public boolean isSmoothSwitchBitrate() {
return mSmoothSwitchBitrate;
}
public void setSmoothSwitchBitrate(boolean smoothSwitchBitrate) {
this.mSmoothSwitchBitrate = smoothSwitchBitrate;
}
public boolean isAutoRotate() {
return mAutoRotate;
}
public void setAutoRotate(boolean autoRotate) {
this.mAutoRotate = autoRotate;
}
public boolean isEnableRenderProcess() {
return mEnableRenderProcess;
}
public void setEnableRenderProcess(boolean enableRenderProcess) {
this.mEnableRenderProcess = enableRenderProcess;
}
public int getProgressInterval() {
return mProgressInterval;
}
public void setProgressInterval(int progressInterval) {
this.mProgressInterval = progressInterval;
}
public int getConnectRetryCount() {
return mConnectRetryCount;
}
public void setConnectRetryCount(int connectRetryCount) {
this.mConnectRetryCount = connectRetryCount;
}
public int getTimeout() {
return mTimeout;
}
public void setTimeout(int timeout) {
this.mTimeout = timeout;
}
public String getCacheFolderPath() {
return mCacheFolderPath;
}
public void setCacheFolderPath(String cacheFolderPath) {
this.mCacheFolderPath = cacheFolderPath;
}
public int getMaxCacheItems() {
return mMaxCacheItems;
}
public void setMaxCacheItems(int maxCacheItems) {
this.mMaxCacheItems = maxCacheItems;
}
public long getPreferredResolution() {
return mPreferredResolution;
}
public void setPreferredResolution(long preferredResolution) {
this.mPreferredResolution = preferredResolution;
}
public int getMediaType() {
return mMediaType;
}
public void setMediaType(int mediaType) {
this.mMediaType = mediaType;
}
public int getConnectRetryInterval() {
return mConnectRetryInterval;
}
public void setConnectRetryInterval(int connectRetryInterval) {
this.mConnectRetryInterval = connectRetryInterval;
}
public int getMaxPreloadSize() {
return mMaxPreloadSize;
}
public void setMaxPreloadSize(int maxPreloadSize) {
this.mMaxPreloadSize = maxPreloadSize;
}
public int getMaxBufferSize() {
return mMaxBufferSize;
}
public void setMaxBufferSize(int maxBufferSize) {
this.mMaxBufferSize = maxBufferSize;
}
}
\ No newline at end of file
package com.xinfu.helivideo.video.tx;
import android.content.Context;
import android.util.Log;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class PlayerManager {
private static final String TAG = "ShortVideoDemo:PlayerManager";
private final static int sMaxPlayerSize = 10;
private Map<VideoModel, TXVodPlayerWrapper> mUrlPlayerMap;
private VideoModel mLastPlayedVideoBean;
private Context mContext;
public PlayerManager(Context context) {
mContext = context.getApplicationContext();
mUrlPlayerMap = new HashMap<>();
}
public void updateManager(List<VideoModel> shortVideoBeanList) {
if (shortVideoBeanList == null || shortVideoBeanList.isEmpty()) {
return;
}
if (shortVideoBeanList.size() > sMaxPlayerSize) {
throw new IllegalArgumentException("shortVideoBeanList is larger than sMaxPlayerSize");
}
List<VideoModel> lastBeanList = playedVideoModel();
Log.i(TAG, " [updateManager]" + ",urlList = " + shortVideoBeanList.toString() + ",lastBeanList = " + lastBeanList.toString());
//找到 lastUrlList中不包含urlList的 lastUrlList为上次传进来的urlList urlList为这次传进来的urlList
List<VideoModel> exprList = findDiffBeanList(shortVideoBeanList, lastBeanList);
//找到 urlList中不包含lastUrlList的
List<VideoModel> newList = findDiffBeanList(lastBeanList, shortVideoBeanList);
if (exprList != null) {
for (int i = 0; i < exprList.size(); i++) {
Log.i(TAG, "[updateManager] exprUrl " + exprList.get(i).videoURL);
}
}
if (newList != null) {
for (int i = 0; i < newList.size(); i++) {
Log.i(TAG, "[updateManager] newUrl " + newList.get(i).videoURL);
}
}
if (newList.size() > 0) {
for (int i = 0; i < newList.size(); i++) {
TXVodPlayerWrapper tempPlayer = null;
if (exprList.size() > 0) {
tempPlayer = mUrlPlayerMap.remove(exprList.remove(0));
}
if (tempPlayer == null) {
tempPlayer = new TXVodPlayerWrapper(mContext);
}
tempPlayer.preStartPlay(newList.get(i));
mUrlPlayerMap.put(newList.get(i), tempPlayer);
}
}
if (exprList.size() > 0) {
for (int i = 0; i < exprList.size(); i++) {
TXVodPlayerWrapper exprPlayer = mUrlPlayerMap.get(exprList.get(i));
mUrlPlayerMap.remove(exprList.get(i));
exprPlayer.stopPlay();
exprPlayer = null;
}
}
if (shortVideoBeanList.contains(mLastPlayedVideoBean)) {
Log.i(TAG, " [updateManager]" + ",mLastPlayedBean = " + mLastPlayedVideoBean.videoURL);
if (mUrlPlayerMap.get(mLastPlayedVideoBean) != null) {
mUrlPlayerMap.get(mLastPlayedVideoBean).preStartPlay(mLastPlayedVideoBean);
}
}
}
public TXVodPlayerWrapper getPlayer(VideoModel bean) {
mLastPlayedVideoBean = bean;
return mUrlPlayerMap.get(bean);
}
private List<VideoModel> findDiffBeanList(List<VideoModel> playUrlList, List<VideoModel> lastPlayUrlList) {
List<VideoModel> exprList = new ArrayList<>();
for (int i = 0; i < lastPlayUrlList.size(); i++) {
if (!playUrlList.contains(lastPlayUrlList.get(i))) {
exprList.add(lastPlayUrlList.get(i));
}
}
return exprList;
}
public void releasePlayer() {
for (TXVodPlayerWrapper txVodPlayerWrapper : mUrlPlayerMap.values()) {
txVodPlayerWrapper.stopPlay();
}
mUrlPlayerMap.clear();
}
private List<VideoModel> playedVideoModel() {
List<VideoModel> urlList = new ArrayList<>();
for (VideoModel bean : mUrlPlayerMap.keySet()) {
urlList.add(bean);
}
return urlList;
}
}
package com.xinfu.helivideo.video.tx;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.RelativeLayout;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.PagerSnapHelper;
import androidx.recyclerview.widget.RecyclerView;
import com.xinfu.helivideo.R;
import com.xinfu.helivideo.mvp.model.VedioBean;
import com.xinfu.helivideo.mvp.model.VideoMultiItemEntity;
import com.xinfu.helivideo.video.tx.adapter.TxVideoAdapter;
import java.util.ArrayList;
import java.util.List;
public class SuperShortVideoView extends RelativeLayout {
private static final String TAG = "SuperShortVideoView";
private static final int MAX_PLAYER_COUNT_ON_PASS = 3;
private RecyclerView mRecyclerView;
private TxVideoAdapter mAdapter;
private List<VideoModel> mUrlList;
private LinearLayoutManager mLayoutManager;
private PagerSnapHelper mSnapHelper;
private int mLastPositionInIDLE = -1;
private TXVideoBaseView mBaseItemView;
private Context mContext;
private boolean mIsOnDestroy;
private PlayerManager mPlayerManager;
private int mCurrentPosition = 0;
private List<VideoMultiItemEntity> mList;
public SuperShortVideoView(Context context) {
this(context, null);
}
public SuperShortVideoView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public SuperShortVideoView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mContext = context;
init(mContext);
}
public void loadMoreData(List<VideoMultiItemEntity> list) {
this.mList = list;
List<VideoModel> videoList = new ArrayList();
for (int i = 0; i < list.size(); i++) {
if (list.get(i).getItemType() == VideoMultiItemEntity.MULTI_ITEM_1) {
videoList.add(new VideoModel(list.get(i).getVideo().getVedioUrl()));
}
}
mUrlList.addAll(videoList);
mAdapter.setNewInstance(mList);
onItemClick(mCurrentPosition);
}
public void loadData(VedioBean vedioBean, List<VideoMultiItemEntity> list, int currentPosition) {
this.mList = list;
this.mCurrentPosition = currentPosition;
List<VideoModel> videoList = new ArrayList();
for (int i = 0; i < list.size(); i++) {
if (list.get(i).getItemType() == VideoMultiItemEntity.MULTI_ITEM_1) {
videoList.add(new VideoModel(list.get(i).getVideo().getVedioUrl()));
}
}
mUrlList.addAll(videoList);
mAdapter.setVedioBean(vedioBean);
mAdapter.setNewInstance(mList);
onItemClick(mCurrentPosition);
}
public void init(Context context) {
View mRootView = LayoutInflater.from(context).inflate(R.layout.super_short_video_view, null);
addView(mRootView);
mPlayerManager = new PlayerManager(getContext());
mRecyclerView = mRootView.findViewById(R.id.rv_super_short_video);
mUrlList = new ArrayList<>();
mSnapHelper = new PagerSnapHelper();
mSnapHelper.attachToRecyclerView(mRecyclerView);
mAdapter = new TxVideoAdapter();
mAdapter.setNewInstance(mList);
mAdapter.setOnVideoEndListener(() -> onItemClick(mCurrentPosition + 1));
mLayoutManager = new LinearLayoutManager(getContext(), LinearLayoutManager.VERTICAL, false) {
@Override
protected int getExtraLayoutSpace(RecyclerView.State state) {
return 300;
}
};
mRecyclerView.setLayoutManager(mLayoutManager);
mRecyclerView.setItemViewCacheSize(6);
mRecyclerView.setHasFixedSize(true);
mRecyclerView.setDrawingCacheEnabled(true);
mRecyclerView.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_LOW);
mRecyclerView.setAdapter(mAdapter);
// mLayoutManager.scrollToPosition(0);
addListener();
}
private void addListener() {
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
if (!recyclerView.canScrollVertically(1) || !recyclerView.canScrollVertically(-1)) {
onScrollStateChanged(recyclerView, RecyclerView.SCROLL_STATE_IDLE);
}
}
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
switch (newState) {
case RecyclerView.SCROLL_STATE_IDLE://停止滚动
View view = mSnapHelper.findSnapView(mLayoutManager);
int position = recyclerView.getChildAdapterPosition(view);
Log.i(TAG, "[SCROLL_STATE_IDLE] mLastPositionInIDLE " + mLastPositionInIDLE + " position " + position);
mCurrentPosition = position;
onPageSelectedMethod(position);
break;
case RecyclerView.SCROLL_STATE_DRAGGING://拖动
break;
case RecyclerView.SCROLL_STATE_SETTLING://惯性滑动
break;
}
}
});
}
private void onPageSelectedMethod(int position) {
if (position == mUrlList.size()) return;
if (mLastPositionInIDLE != position) {
View view = mSnapHelper.findSnapView(mLayoutManager);
if (view == null) {
return;
}
mBaseItemView = view.findViewById(R.id.baseItemView);
Log.i(TAG, "onPageSelected " + position);
List<VideoModel> tempUrlList = initUrlList(position, MAX_PLAYER_COUNT_ON_PASS);
mPlayerManager.updateManager(tempUrlList);
TXVodPlayerWrapper txVodPlayerWrapper = mPlayerManager.getPlayer(mUrlList.get(position));
Log.i(TAG, "txVodPlayerWrapper " + txVodPlayerWrapper + "url-- " + mUrlList.get(position).videoURL);
Log.i(TAG, "txVodPlayerWrapper " + txVodPlayerWrapper);
mBaseItemView.setTXVodPlayer(txVodPlayerWrapper);
mLastPositionInIDLE = position;
}
if (mBaseItemView != null && !mIsOnDestroy) {
mBaseItemView.startPlay();
}
}
/**
* 初始化向PlayManager传递的urlList
*
* @param startIndex 开始的索引
* @param maxCount 传递的urlList的数目
* @return
*/
private List<VideoModel> initUrlList(int startIndex, int maxCount) {
int i = startIndex - 1;
if (i + maxCount > mUrlList.size()) {
i = mUrlList.size() - maxCount;
}
if (i < 0) {
i = 0;
}
int addedCount = 0;
List<VideoModel> cacheList = new ArrayList<>();
while (i < mUrlList.size() && addedCount < maxCount) {
cacheList.add(mUrlList.get(i));
addedCount++;
i++;
}
return cacheList;
}
public void resume() {
if (mBaseItemView != null) {
mBaseItemView.startPlay();
}
}
public void pause() {
if (mBaseItemView != null) {
mBaseItemView.pausePlayer();
}
}
public void releasePlayer() {
if (mBaseItemView != null) {
mBaseItemView.stopPlayer();
}
mPlayerManager.releasePlayer();
}
public void onListPageScrolled() {
if (mBaseItemView != null) {
mBaseItemView.pausePlayer();
}
}
public void onItemClick(final int position) {
mRecyclerView.scrollToPosition(position);
mRecyclerView.post(() -> {
Log.i(TAG, "onItemClick");
mCurrentPosition = position;
onPageSelectedMethod(position);
});
}
public int getCurrentPosition() {
return mCurrentPosition;
}
public void setOnCustomChildClickListener(TxVideoAdapter.OnCustomChildClickListener onCustomChildClickListener) {
mAdapter.setOnCustomChildClickListener(onCustomChildClickListener);
}
public void onDestroy() {
mIsOnDestroy = true;
}
}
package com.xinfu.helivideo.video.tx;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Color;
import android.os.Bundle;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.style.ForegroundColorSpan;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.SeekBar;
import android.widget.TextView;
import com.tencent.rtmp.TXLiveConstants;
import com.tencent.rtmp.ui.TXCloudVideoView;
import com.xinfu.helivideo.R;
import java.util.Locale;
/**
* 沉浸式播放组件
*/
public class TXVideoBaseView extends RelativeLayout implements View.OnClickListener,
SeekBar.OnSeekBarChangeListener, TXVodPlayerWrapper.OnPlayEventChangedListener {
private static final String TAG = "TXVideoBaseView";
private View mRootView;
private ProgressBar mBgProgress;
private SeekBar mSeekBar;
private TXCloudVideoView mTXCloudVideoView;
private ImageView mIvCover;
private ImageView mPauseImageView;
private TextView mProgressTime;
private TXVodPlayerWrapper mTXVodPlayerWrapper;
private boolean mStartSeek = false;
private long mTrackingTouchTS = 0;
public TXVideoBaseView(Context context) {
super(context);
init(context);
}
public TXVideoBaseView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public TXVideoBaseView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
public void setTXVodPlayer(TXVodPlayerWrapper TXVodPlayerWrapper) {
mTXVodPlayerWrapper = TXVodPlayerWrapper;
mTXVodPlayerWrapper.setPlayerView(mTXCloudVideoView);
mTXCloudVideoView.requestLayout();
Log.i(TAG, "[setTXVodPlayer] , PLAY_EVT_PLAY_PROGRESS," + mTXVodPlayerWrapper.getVodPlayer().hashCode() + " url " + TXVodPlayerWrapper.getUrl());
}
@SuppressLint("ClickableViewAccessibility")
private void init(Context context) {
mRootView = LayoutInflater.from(context).inflate(R.layout.player_item_base_view, null);
addView(mRootView);
mBgProgress = mRootView.findViewById(R.id.progress_short_video);
mSeekBar = mRootView.findViewById(R.id.seekbar_short_video);
mSeekBar.setOnSeekBarChangeListener(this);
mIvCover = mRootView.findViewById(R.id.iv_cover);
mPauseImageView = mRootView.findViewById(R.id.iv_pause);
mPauseImageView.setOnClickListener(this);
mTXCloudVideoView = mRootView.findViewById(R.id.tcv_video_view);
mTXCloudVideoView.setOnClickListener(this);
mProgressTime = mRootView.findViewById(R.id.tv_progress_time);
setProgressTimeColor(mProgressTime.getText().toString());
mSeekBar.setVisibility(View.GONE);
mProgressTime.setVisibility(View.GONE);
OnTouchListener listener = new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mBgProgress.setVisibility(View.GONE);
mSeekBar.setVisibility(View.VISIBLE);
mProgressTime.setVisibility(View.VISIBLE);
break;
case MotionEvent.ACTION_UP:
mBgProgress.setVisibility(View.VISIBLE);
mSeekBar.setVisibility(View.GONE);
mProgressTime.setVisibility(View.GONE);
break;
}
return false;
}
};
mBgProgress.setOnTouchListener(listener);
mSeekBar.setOnTouchListener(listener);
}
private void handlePlayProgress(Bundle param) {
if (mStartSeek) {
return;
}
int progress = param.getInt(TXLiveConstants.EVT_PLAY_PROGRESS);
int duration = param.getInt(TXLiveConstants.EVT_PLAY_DURATION);//单位为s
int progressMS = param.getInt(TXLiveConstants.EVT_PLAY_PROGRESS_MS);
int durationMS = param.getInt(TXLiveConstants.EVT_PLAY_DURATION_MS);
long curTS = System.currentTimeMillis();
// 避免滑动进度条松开的瞬间可能出现滑动条瞬间跳到上一个位置
if (Math.abs(curTS - mTrackingTouchTS) < 500) {
return;
}
mTrackingTouchTS = curTS;
if (mSeekBar != null) {
mSeekBar.setMax(durationMS);
mSeekBar.setProgress(progressMS);
mBgProgress.setMax(durationMS);
mBgProgress.setProgress(progressMS);
}
if (mProgressTime != null) {
String tempString = String.format(Locale.CHINA, "%02d:%02d/%02d:%02d", (progress) / 60, progress % 60, (duration) / 60, duration % 60);
setProgressTimeColor(tempString);
}
}
@Override
public void onClick(View v) {
int id = v.getId();
if (id == R.id.tcv_video_view || id == R.id.iv_pause) {
if (mTXVodPlayerWrapper == null) {
return;
}
if (!mTXVodPlayerWrapper.isPlaying()) {
mTXVodPlayerWrapper.resumePlay();
mPauseImageView.setVisibility(View.GONE);
} else {
pausePlayer();
}
}
}
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if (mProgressTime != null) {
String tempString = String.format(Locale.CHINA, "%02d:%02d/%02d:%02d", (progress / 1000) / 60, (progress / 1000) % 60, (seekBar.getMax() / 1000) / 60, (seekBar.getMax() / 1000) % 60);
setProgressTimeColor(tempString);
}
}
private void setProgressTimeColor(String value) {
SpannableStringBuilder builder = new SpannableStringBuilder(value);
ForegroundColorSpan gray = new ForegroundColorSpan(Color.GRAY);
ForegroundColorSpan white = new ForegroundColorSpan(Color.WHITE);
builder.setSpan(gray, 5, 11, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
builder.setSpan(white, 0, 5, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
mProgressTime.setText(builder);
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
mStartSeek = true;
// 解决viewPager和的滑动冲突问题
getParent().requestDisallowInterceptTouchEvent(true);
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
// 解决viewPager和的滑动冲突问题
getParent().requestDisallowInterceptTouchEvent(false);
if (mTXVodPlayerWrapper != null) {
Log.i(TAG, "[onStopTrackingTouch] seekBar.getProgress() " + seekBar.getProgress());
mTXVodPlayerWrapper.seekTo(seekBar.getProgress() / 1000F);
}
mTrackingTouchTS = System.currentTimeMillis();
mStartSeek = false;
}
public void pausePlayer() {
if (mTXVodPlayerWrapper != null) {
mTXVodPlayerWrapper.pausePlay();
mPauseImageView.setVisibility(View.VISIBLE);
}
}
public void startPlay() {
if (mTXVodPlayerWrapper != null) {
mPauseImageView.setVisibility(View.GONE);
mTXVodPlayerWrapper.setVodChangeListener(this);
mTXVodPlayerWrapper.resumePlay();
Log.i(TAG, "[startPlay] mTXVodPlayerWrapper.url " + mTXVodPlayerWrapper.getUrl());
}
}
public void stopPlayer() {
if (mTXVodPlayerWrapper != null) {
mTXVodPlayerWrapper.stopPlay();
mTXVodPlayerWrapper.setVodChangeListener(null);
Log.i(TAG, "[stopPlayer] mTXVodPlayerWrapper.url " + mTXVodPlayerWrapper.getUrl());
mPauseImageView.setVisibility(View.GONE);
}
}
public void stopForPlaying() {
if (mTXVodPlayerWrapper != null) {
mTXVodPlayerWrapper.stopForPlaying();
mTXVodPlayerWrapper.setVodChangeListener(null);
Log.i(TAG, "[stopForPlaying] mTXVodPlayerWrapper.url " + mTXVodPlayerWrapper.getUrl());
mPauseImageView.setVisibility(View.GONE);
mIvCover.setVisibility(VISIBLE);
}
}
@Override
public void onProgress(Bundle bundle) {
handlePlayProgress(bundle);
}
@Override
public void onRcvFirstFrame() {
Log.i(TAG, "[onPrepared in TXVideoBaseView]");
mIvCover.setVisibility(GONE);
}
@Override
public void onEnded() {
if (this.mOnVideoEndListener != null) {
this.mOnVideoEndListener.onVideoEnd();
}
}
public void setOnVideoEndListener(OnVideoEndListener onVideoEndListener) {
this.mOnVideoEndListener = onVideoEndListener;
}
private OnVideoEndListener mOnVideoEndListener;
public interface OnVideoEndListener {
void onVideoEnd();
}
}
package com.xinfu.helivideo.video.tx;
import static com.tencent.rtmp.TXLiveConstants.PLAY_EVT_PLAY_END;
import static com.tencent.rtmp.TXLiveConstants.PLAY_EVT_PLAY_PROGRESS;
import static com.tencent.rtmp.TXLiveConstants.PLAY_EVT_RCV_FIRST_I_FRAME;
import static com.tencent.rtmp.TXLiveConstants.PLAY_EVT_VOD_PLAY_PREPARED;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import com.tencent.rtmp.ITXVodPlayListener;
import com.tencent.rtmp.TXPlayerGlobalSetting;
import com.tencent.rtmp.TXVodConstants;
import com.tencent.rtmp.TXVodPlayConfig;
import com.tencent.rtmp.TXVodPlayer;
import com.tencent.rtmp.ui.TXCloudVideoView;
import java.io.File;
public class TXVodPlayerWrapper implements ITXVodPlayListener {
private static final String TAG = "ShortVideoDemo:TXVodPlayerWrapper";
private TXVodPlayer mVodPlayer;
private OnPlayEventChangedListener mOnPlayEventChangedListener;
private TxVodStatus mStatus;
private String mUrl;
private boolean mStartOnPrepare;
private TXVodPlayConfig mTXVodPlayConfig;
public TXVodPlayerWrapper(Context context) {
mVodPlayer = new TXVodPlayer(context);
mVodPlayer.setVodListener(this);
if (ConfigBean.getInstance().isIsUseDash()) {
mVodPlayer.setRenderMode(TXVodConstants.RENDER_MODE_ADJUST_RESOLUTION);
} else {
mVodPlayer.setRenderMode(TXVodConstants.RENDER_MODE_FULL_FILL_SCREEN);
}
mTXVodPlayConfig = new TXVodPlayConfig();
mTXVodPlayConfig.setProgressInterval(500);
mTXVodPlayConfig.setSmoothSwitchBitrate(true);
mTXVodPlayConfig.setMaxBufferSize(5);
mTXVodPlayConfig.setPreferredResolution(1080 * 1920);
mTXVodPlayConfig.setMaxCacheItems(8);
if (TXPlayerGlobalSetting.getCacheFolderPath() == null
|| TXPlayerGlobalSetting.getCacheFolderPath().equals("")) {
File sdcardDir = context.getExternalFilesDir(null);
TXPlayerGlobalSetting.setCacheFolderPath(sdcardDir.getPath() + "/txcache");
}
mVodPlayer.setConfig(mTXVodPlayConfig);
}
@Override
public void onPlayEvent(TXVodPlayer txVodPlayer, int event, Bundle bundle) {
switch (event) {
case PLAY_EVT_VOD_PLAY_PREPARED:
playerStatusChanged(TxVodStatus.TX_VIDEO_PLAYER_STATUS_PREPARED);
Log.i(TAG, "[onPlayEvent] , startOnPrepare," + mStartOnPrepare + ",mVodPlayer "
+ mVodPlayer.hashCode() + " mUrl " + mUrl);
if (mStartOnPrepare) {
mVodPlayer.resume();
mStartOnPrepare = false;
playerStatusChanged(TxVodStatus.TX_VIDEO_PLAYER_STATUS_PLAYING);
}
break;
case PLAY_EVT_RCV_FIRST_I_FRAME:
if (mOnPlayEventChangedListener != null) {
mOnPlayEventChangedListener.onRcvFirstFrame();
}
break;
case PLAY_EVT_PLAY_PROGRESS:
if (mOnPlayEventChangedListener != null) {
mOnPlayEventChangedListener.onProgress(bundle);
}
break;
case PLAY_EVT_PLAY_END:
playerStatusChanged(TxVodStatus.TX_VIDEO_PLAYER_STATUS_ENDED);
if (mOnPlayEventChangedListener != null) {
mOnPlayEventChangedListener.onEnded();
}
break;
default:
break;
}
}
@Override
public void onNetStatus(TXVodPlayer txVodPlayer, Bundle bundle) {
}
public void pausePlay() {
mVodPlayer.pause();
playerStatusChanged(TxVodStatus.TX_VIDEO_PLAYER_STATUS_PAUSED);
}
public void resumePlay() {
Log.i(TAG, "[resumePlay] , startOnPrepare, " + mStartOnPrepare
+ " mVodPlayer " + mVodPlayer.hashCode() + " url " + mUrl);
if (mStatus == TxVodStatus.TX_VIDEO_PLAYER_STATUS_STOPPED) {
mVodPlayer.setAutoPlay(true);
mVodPlayer.startVodPlay(mUrl);
playerStatusChanged(TxVodStatus.TX_VIDEO_PLAYER_STATUS_PLAYING);
return;
}
if (mStatus == TxVodStatus.TX_VIDEO_PLAYER_STATUS_PREPARED
|| mStatus == TxVodStatus.TX_VIDEO_PLAYER_STATUS_PAUSED) {
mVodPlayer.resume();
playerStatusChanged(TxVodStatus.TX_VIDEO_PLAYER_STATUS_PLAYING);
} else {
mStartOnPrepare = true;
}
}
public void seekTo(float time) {
mVodPlayer.seek(time);
}
public boolean isPlaying() {
return mVodPlayer.isPlaying();
}
public void stopForPlaying() {
if (mStatus == TxVodStatus.TX_VIDEO_PLAYER_STATUS_PLAYING) {
mVodPlayer.stopPlay(true);
playerStatusChanged(TxVodStatus.TX_VIDEO_PLAYER_STATUS_STOPPED);
}
}
public void stopPlay() {
mVodPlayer.stopPlay(true);
playerStatusChanged(TxVodStatus.TX_VIDEO_PLAYER_STATUS_STOPPED);
}
public void setPlayerView(TXCloudVideoView txCloudVideoView) {
mVodPlayer.setPlayerView(txCloudVideoView);
}
public void preStartPlay(VideoModel bean) {
this.mUrl = bean.videoURL;
this.mStatus = TxVodStatus.TX_VIDEO_PLAYER_STATUS_UNLOAD;
mStartOnPrepare = false;
mVodPlayer.setLoop(false);
mVodPlayer.stopPlay(true);
Log.i(TAG, "[preStartPlay] , startOnPrepare ," + mStartOnPrepare + ", mVodPlayer " + mVodPlayer.hashCode());
mVodPlayer.setAutoPlay(false);
mVodPlayer.startVodPlay(bean.videoURL);
}
private void playerStatusChanged(TxVodStatus status) {
this.mStatus = status;
Log.i(TAG, " [playerStatusChanged] mVodPlayer" + mVodPlayer.hashCode() + " mStatus " + mStatus);
}
public void setVodChangeListener(OnPlayEventChangedListener listener) {
mOnPlayEventChangedListener = listener;
}
public enum TxVodStatus {
TX_VIDEO_PLAYER_STATUS_UNLOAD, // 未加载
TX_VIDEO_PLAYER_STATUS_PREPARED, // 准备播放
TX_VIDEO_PLAYER_STATUS_LOADING, // 加载中
TX_VIDEO_PLAYER_STATUS_PLAYING, // 播放中
TX_VIDEO_PLAYER_STATUS_PAUSED, // 暂停
TX_VIDEO_PLAYER_STATUS_ENDED, // 播放完成
TX_VIDEO_PLAYER_STATUS_STOPPED // 手动停止播放
}
public interface OnPlayEventChangedListener {
void onProgress(Bundle bundle);
void onRcvFirstFrame();
void onEnded();
}
public TXVodPlayer getVodPlayer() {
return mVodPlayer;
}
public String getUrl() {
return mUrl;
}
}
package com.xinfu.helivideo.video.tx;
import java.io.Serializable;
/**
* Created by yuejiaoli on 2018/7/4.
*/
public class VideoModel implements Serializable {
public VideoModel(String videoURL) {
this.videoURL = videoURL;
}
/**
* 视频URL
*/
public String videoURL;
/**
* 从服务器拉取的封面图片
*/
public String placeholderImage;
}
package com.xinfu.helivideo.video.tx.adapter;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;
public abstract class AbsPlayerRecyclerViewAdapter<T, K extends AbsViewHolder> extends RecyclerView.Adapter<K> {
private List<T> mList;
public Context mContext;
public AbsPlayerRecyclerViewAdapter(List<T> list) {
this.mList = list;
}
@Override
public K onCreateViewHolder(ViewGroup parent, int viewType) {
mContext = parent.getContext();
K holder = onCreateHolder(parent);
return holder;
}
@Override
public void onBindViewHolder(K holder, int position) {
onHolder(holder, mList.get(position), position);
}
@Override
public int getItemViewType(int position) {
return position;
}
@Override
public int getItemCount() {
return mList == null ? 0 : mList.size();
}
public abstract void onHolder(K holder, T bean, int position);
public abstract K onCreateHolder(ViewGroup parent);
public View getViewByRes(int res, ViewGroup parent) {
return LayoutInflater.from(mContext).inflate(res, parent, false);
}
}
package com.xinfu.helivideo.video.tx.adapter;
import android.view.View;
import androidx.recyclerview.widget.RecyclerView;
public abstract class AbsViewHolder extends RecyclerView.ViewHolder {
public AbsViewHolder(View itemView) {
super(itemView);
}
}
package com.xinfu.helivideo.video.tx.adapter;
import android.content.Context;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.tencent.rtmp.ui.TXCloudVideoView;
import com.xinfu.helivideo.R;
import com.xinfu.helivideo.video.tx.TXVideoBaseView;
import com.xinfu.helivideo.video.tx.VideoModel;
import java.util.List;
public class ShortVideoPlayAdapter extends AbsPlayerRecyclerViewAdapter<VideoModel,
ShortVideoPlayAdapter.VideoViewHolder> {
private static final String TAG = "ShortVideoDemo:ShortVideoPlayAdapter";
private Context mContext;
public ShortVideoPlayAdapter(Context context, List<VideoModel> list) {
super(list);
mContext = context;
}
@Override
public void onHolder(VideoViewHolder holder, VideoModel bean, int position) {
if (bean != null && bean.placeholderImage != null) {
Glide.with(mContext).load(bean.placeholderImage)
.skipMemoryCache(true)
.diskCacheStrategy(DiskCacheStrategy.ALL).centerCrop()
.into(holder.mImageViewCover);
}
}
@Override
public void onViewRecycled(@NonNull VideoViewHolder holder) {
super.onViewRecycled(holder);
Glide.with(mContext).clear(holder.mImageViewCover);
}
@Override
public VideoViewHolder onCreateHolder(ViewGroup viewGroup) {
return new VideoViewHolder(getViewByRes(R.layout.player_item_short_video_play, viewGroup));
}
@Override
public void onViewDetachedFromWindow(@NonNull VideoViewHolder holder) {
super.onViewDetachedFromWindow(holder);
Log.i(TAG, "onViewDetachedFromWindow");
TXVideoBaseView videoView = (TXVideoBaseView) holder.mRootView.findViewById(R.id.baseItemView);
videoView.stopForPlaying();
}
public class VideoViewHolder extends AbsViewHolder {
public View mRootView;
public TextView mEpisodeTv;
public ImageView mImageViewCover;
public TXCloudVideoView mVideoView;
public VideoViewHolder(View rootView) {
super(rootView);
this.mRootView = rootView;
this.mImageViewCover = rootView.findViewById(R.id.iv_cover);
this.mVideoView = rootView.findViewById(R.id.tcv_video_view);
}
}
}
package com.xinfu.helivideo.video.tx.adapter
import android.view.View
import android.widget.LinearLayout
import android.widget.TextView
import com.airbnb.lottie.LottieAnimationView
import com.airbnb.lottie.LottieComposition
import com.airbnb.lottie.LottieCompositionFactory
import com.airbnb.lottie.LottieDrawable
import com.bumptech.glide.Glide
import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.chad.library.adapter.base.BaseMultiItemQuickAdapter
import com.chad.library.adapter.base.viewholder.BaseViewHolder
import com.xinfu.helivideo.R
import com.xinfu.helivideo.mvp.model.VedioBean
import com.xinfu.helivideo.mvp.model.VideoMultiItemEntity
import com.xinfu.helivideo.video.tx.TXVideoBaseView
class TxVideoAdapter : BaseMultiItemQuickAdapter<VideoMultiItemEntity, BaseViewHolder>() {
private var vedioBean: VedioBean? = null
init {
addItemType(VideoMultiItemEntity.MULTI_ITEM_1, R.layout.player_item_short_video_play)
addItemType(VideoMultiItemEntity.MULTI_ITEM_2, R.layout.item_block_view)
}
override fun convert(holder: BaseViewHolder, item: VideoMultiItemEntity) {
if (holder.itemViewType == VideoMultiItemEntity.MULTI_ITEM_1) {
initVideoHolder(holder, item)
} else {
initLockHolder(holder)
}
}
private fun initLockHolder(holder: BaseViewHolder) {
Glide.with(context).load(vedioBean?.coverImage)
.into(holder.getView(R.id.iv_bg))
holder.getView<View>(R.id.vip).setOnClickListener {
mOnCustomChildClickListener?.onCustomChildClick(it, holder.adapterPosition)
}
holder.getView<TextView>(R.id.unlock).text = "看广告解锁" + vedioBean?.adGiveVedioNum + "集"
holder.getView<View>(R.id.unlock).setOnClickListener {
mOnCustomChildClickListener?.onCustomChildClick(it, holder.adapterPosition)
}
}
private fun initVideoHolder(holder: BaseViewHolder, item: VideoMultiItemEntity) {
//标题
holder.setText(R.id.title_tv, vedioBean?.title)
//集数
holder.setText(R.id.info_tv, "第" + item.video.vedioIndex + "集")
//收藏数量
holder.setText(R.id.zan_num_tv, "" + vedioBean?.hot)
//缩略图
Glide.with(context).load(vedioBean?.coverImage)
.skipMemoryCache(true)
.diskCacheStrategy(DiskCacheStrategy.ALL).centerCrop()
.into(holder.getView<TXVideoBaseView>(R.id.baseItemView).findViewById(R.id.iv_cover))
holder.getView<LinearLayout>(R.id.ll_collect).setOnClickListener {
mOnCustomChildClickListener?.onCustomChildClick(it, holder.adapterPosition)
if (vedioBean?.collect == 0) {
playCollectAnim(holder.getView(R.id.zan_iv))
} else {
playCancelCollectAnim(holder.getView(R.id.zan_iv))
}
}
if (vedioBean?.collect == 0) {
// 未收藏
setCollectImage(holder.getView(R.id.zan_iv))
} else {
// 已收藏
setCancelCollectImage(holder.getView(R.id.zan_iv))
}
holder.getView<TXVideoBaseView>(R.id.baseItemView).setOnVideoEndListener(mOnVideoEndListener)
}
override fun onViewDetachedFromWindow(holder: BaseViewHolder) {
super.onViewDetachedFromWindow(holder)
if (holder.itemViewType == VideoMultiItemEntity.MULTI_ITEM_1) {
holder.getView<TXVideoBaseView>(R.id.baseItemView).stopForPlaying()
}
}
override fun onViewRecycled(holder: BaseViewHolder) {
super.onViewRecycled(holder)
if (holder.itemViewType == VideoMultiItemEntity.MULTI_ITEM_1) {
Glide.with(context).clear(holder.getView<View>(R.id.iv_cover))
}
}
private fun setCollectImage(view: LottieAnimationView) {
val lottieDrawable = LottieDrawable()
LottieCompositionFactory.fromAsset(context, "home_collect.json")
.addListener { result: LottieComposition? ->
lottieDrawable.setImagesAssetsFolder("images/")
lottieDrawable.composition = result
}
view.setImageDrawable(lottieDrawable)
}
private fun setCancelCollectImage(view: LottieAnimationView) {
val lottieDrawable = LottieDrawable()
LottieCompositionFactory.fromAsset(context, "home_cancel_collect.json")
.addListener { result: LottieComposition? ->
lottieDrawable.setImagesAssetsFolder("images/")
lottieDrawable.composition = result
}
view.setImageDrawable(lottieDrawable)
}
private fun playCollectAnim(view: LottieAnimationView) {
val lottieDrawable = LottieDrawable()
LottieCompositionFactory.fromAsset(context, "home_collect.json")
.addListener { result: LottieComposition? ->
lottieDrawable.setImagesAssetsFolder("images/")
lottieDrawable.composition = result
lottieDrawable.loop(false)
lottieDrawable.playAnimation()
}
view.setImageDrawable(lottieDrawable)
}
private fun playCancelCollectAnim(view: LottieAnimationView) {
val lottieDrawable = LottieDrawable()
LottieCompositionFactory.fromAsset(context, "home_cancel_collect.json")
.addListener { result: LottieComposition? ->
lottieDrawable.setImagesAssetsFolder("images/")
lottieDrawable.composition = result
lottieDrawable.loop(false)
lottieDrawable.playAnimation()
}
view.setImageDrawable(lottieDrawable)
}
private var mOnVideoEndListener: TXVideoBaseView.OnVideoEndListener? = null
fun setOnVideoEndListener(onVideoEndListener: TXVideoBaseView.OnVideoEndListener) {
this.mOnVideoEndListener = onVideoEndListener
}
private var mOnCustomChildClickListener: OnCustomChildClickListener? = null
fun setOnCustomChildClickListener(onCustomChildClickListener: OnCustomChildClickListener) {
this.mOnCustomChildClickListener = onCustomChildClickListener
}
interface OnCustomChildClickListener {
fun onCustomChildClick(view: View, position: Int)
}
fun setVedioBean(vedioBean: VedioBean) {
this.vedioBean = vedioBean
}
}
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<stroke
android:width="1dp"
android:color="@color/white"></stroke>
<corners android:radius="20dp"></corners>
<solid android:color="#4D000000"/>
</shape>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true">
<shape android:shape="oval">
<solid android:color="#FFFFFFFF" />
<size android:width="8dp" android:height="8dp" />
</shape>
</item>
<item>
<shape android:shape="oval">
<solid android:color="#FFFFFFFF" />
<size android:width="8dp" android:height="8dp" />
</shape>
</item>
</selector>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@android:id/background" android:height="5dp">
<shape>
<corners android:radius="5dp"/>
<solid android:color="#4CD8D8D8"/> <!-- 颜色-->
</shape>
</item>
<item android:id="@android:id/secondaryProgress" android:height="5dp">
<clip>
<shape>
<corners android:radius="5dp"/>
<solid android:color="#20B2AA"/> <!-- 颜色-->
</shape>
</clip>
</item>
<item android:id="@android:id/progress" android:height="5dp">
<clip>
<shape>
<corners android:radius="5dp"/>
<solid android:color="#C9C7C7"/> <!-- 颜色-->
</shape>
</clip>
</item>
</layer-list>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/black"
android:orientation="vertical">
<com.xinfu.helivideo.video.tx.SuperShortVideoView
android:id="@+id/super_short_video_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<ImageView
android:id="@+id/close_iv"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginTop="20dp"
android:padding="10dp"
android:src="@mipmap/ic_arrow_white" />
<FrameLayout
android:id="@+id/fm_bottom"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_gravity="bottom">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="@dimen/dp_40"
android:layout_gravity="center"
android:layout_marginStart="20dp"
android:layout_marginEnd="20dp"
android:background="@drawable/shape_half_trans"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingStart="@dimen/dp_10"
android:paddingEnd="@dimen/dp_10">
<ImageView
android:layout_width="20dp"
android:layout_height="20dp"
android:src="@mipmap/ic_video_epsiode" />
<TextView
android:id="@+id/episode_tv"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_weight="1"
android:text="共100集 已完结"
android:textColor="@color/white" />
<ImageView
android:layout_width="20dp"
android:layout_height="20dp"
android:src="@mipmap/ic_arrow_top" />
</LinearLayout>
</FrameLayout>
</FrameLayout>
\ No newline at end of file
......@@ -49,14 +49,4 @@
</LinearLayout>
</FrameLayout>
<ImageView
android:id="@+id/leave"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:padding="10dp"
android:src="@mipmap/ic_arrow_white" />
</FrameLayout>
......@@ -20,50 +20,47 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="15dp"
android:orientation="horizontal"
android:layout_marginBottom="10dp"
app:layout_constraintBottom_toTopOf="@id/info_tv"
android:layout_marginBottom="100dp"
android:orientation="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent">
<TextView
android:id="@+id/title_tv"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:gravity="center"
android:maxEms="9"
android:maxLines="1"
android:textStyle="bold"
android:text="-"
android:textColor="#fff"
android:textSize="16sp" />
</LinearLayout>
android:textSize="16sp"
android:textStyle="bold" />
<TextView
android:id="@+id/info_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="24dp"
android:layout_marginStart="15dp"
android:ellipsize="end"
android:gravity="center|left"
android:maxEms="14"
android:maxLines="3"
android:text="-"
android:textColor="#fff"
android:textSize="14sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
tools:ignore="RtlHardcoded" />
<TextView
android:id="@+id/info_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:gravity="center|left"
android:maxEms="14"
android:maxLines="3"
android:text="-"
android:textColor="#fff"
android:textSize="14sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent" />
</LinearLayout>
<LinearLayout
android:id="@+id/ll_collect"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="15dp"
android:layout_marginBottom="40dp"
android:gravity="center"
android:orientation="vertical"
android:layout_marginBottom="40dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintRight_toRightOf="parent">
......@@ -78,8 +75,8 @@
android:id="@+id/zan_num_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:layout_marginTop="6dp"
android:gravity="center"
android:text="-"
android:textColor="#fff"
android:textSize="14sp" />
......
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.tencent.rtmp.ui.TXCloudVideoView
android:id="@+id/tcv_video_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center" />
<View
android:id="@+id/empty_view"
android:layout_width="match_parent"
android:layout_height="52dp"
android:layout_alignParentBottom="true"
android:background="@android:color/transparent" />
<ImageView
android:id="@+id/iv_cover"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:scaleType="centerInside"
android:visibility="visible" />
<SeekBar
android:id="@+id/seekbar_short_video"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_above="@id/empty_view"
android:maxHeight="6dp"
android:minHeight="6dp"
android:progressDrawable="@drawable/player_video_seek_bar"
android:thumb="@drawable/player_thumb_view" />
<TextView
android:id="@+id/tv_progress_time"
android:layout_width="118dp"
android:layout_height="30dp"
android:layout_above="@id/seekbar_short_video"
android:layout_centerHorizontal="true"
android:layout_marginBottom="12dp"
android:background="@drawable/player_short_video_time"
android:gravity="center"
android:text="00:00/00:00"
android:textSize="16sp" />
<ProgressBar
android:id="@+id/progress_short_video"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="6dp"
android:layout_above="@id/empty_view"
android:max="100"
android:progressDrawable="@drawable/jz_bottom_seek_progress" />
<ImageView
android:id="@+id/iv_pause"
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_centerHorizontal="true"
android:layout_marginTop="314dp"
android:src="@mipmap/player_pause"
android:visibility="gone" />
</RelativeLayout>
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.xinfu.helivideo.video.tx.TXVideoBaseView
android:id="@+id/baseItemView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<LinearLayout
android:id="@+id/ll"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="15dp"
android:layout_marginBottom="120dp"
android:orientation="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent">
<TextView
android:id="@+id/title_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:gravity="center"
android:maxEms="9"
android:maxLines="1"
android:text="-"
android:textColor="#fff"
android:textSize="16sp"
android:textStyle="bold" />
<TextView
android:id="@+id/info_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:gravity="center|left"
android:maxEms="14"
android:maxLines="3"
android:text="-"
android:textColor="#fff"
android:textSize="14sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent" />
</LinearLayout>
<LinearLayout
android:id="@+id/ll_collect"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="15dp"
android:layout_marginBottom="160dp"
android:gravity="center"
android:orientation="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintRight_toRightOf="parent">
<com.airbnb.lottie.LottieAnimationView
android:id="@+id/zan_iv"
android:layout_width="35dp"
android:layout_height="35dp"
android:layout_marginTop="10dp"
android:src="@mipmap/home_collect_img_0" />
<TextView
android:id="@+id/zan_num_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:text="-"
android:textColor="#fff"
android:textSize="14sp" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_super_short_video"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
\ No newline at end of file
......@@ -3,4 +3,4 @@ include ':oaid'
include ':rxpay'
include ':alipay'
include ':wxpay'
include ':videocache'
\ No newline at end of file
include ':videocache'
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment