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

This commit is contained in:
aibingbing
2023-12-14 19:24:48 +08:00
parent 600548ea9d
commit cdbba2af3a
15 changed files with 673 additions and 420 deletions

View File

@@ -1,7 +1,6 @@
package com.mogo.och.bus.passenger;
import static com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant.M_BUS_P;
import static com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant.M_TAXI_P;
import android.content.Context;
@@ -17,7 +16,7 @@ import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger;
import com.mogo.eagle.core.utilcode.util.MultiDisplayUtils;
import com.mogo.och.bus.passenger.constant.BusPassengerConst;
import com.mogo.och.bus.passenger.ui.BusPassengerRouteFragment;
import com.mogo.och.common.module.wigets.video.VideoPlayerActivity;
import com.mogo.och.common.module.wigets.media.MediaPlayerActivity;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -48,7 +47,7 @@ public class MogoOCHBusPassenger implements IMogoOCH {
showFragment();
if (AppIdentityModeUtils.isJL(FunctionBuildConfig.appIdentityMode) && activity != null) {
MultiDisplayUtils.INSTANCE.startActWithSecond(activity, VideoPlayerActivity.class);
MultiDisplayUtils.INSTANCE.startActWithSecond(activity, MediaPlayerActivity.class);
}
return null;

View File

@@ -3,11 +3,11 @@
<application>
<activity
android:name=".wigets.video.VideoPlayerActivity"
android:name=".wigets.media.MediaPlayerActivity"
android:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|screenLayout|fontScale|uiMode|orientation|screenSize|smallestScreenSize"
android:enabled="true"
android:exported="true"
android:process=":video_ad"
android:process=":media"
android:resizeableActivity="false"
android:resumeWhilePausing="true"
android:screenOrientation="landscape"

View File

@@ -0,0 +1,178 @@
package com.mogo.och.common.module.utils;
import android.content.Context;
import android.os.Environment;
import android.text.TextUtils;
import android.util.Log;
import java.io.File;
/**
* @authorliuhai
* @date2017/10/14 14:47
* @modifierubt
* @modify_date2017/10/14 14:47
* 文件缓存存储
* version
*/
public class FileUtils {
/**
* 获取应用专属缓存目录
* android 4.4及以上系统不需要申请SD卡读写权限
* 因此也不用考虑6.0系统动态申请SD卡读写权限问题切随应用被卸载后自动清空 不会污染用户存储空间
*
* @param context 上下文
* @param type 文件夹类型 可以为空为空则返回API得到的一级目录
* @return 缓存文件夹 如果没有SD卡或SD卡有问题则返回内存缓存目录否则优先返回SD卡缓存目录
*/
public static String getCacheDirectory(Context context, String type) {
File appCacheDir = getExternalCacheDirectory(context, type);
if (appCacheDir == null) {
appCacheDir = getInternalCacheDirectory(context, type);
}
if (appCacheDir == null) {
Log.e("FileUtils", "getCacheDirectory fail ,the reason is mobile phone unknown exception !");
} else {
if (!appCacheDir.exists() && !appCacheDir.mkdirs()) {
Log.e("FileUtils", "getCacheDirectory fail ,the reason is make directory fail !");
}
}
Log.d("FileUtils", "appCacheDir===" + appCacheDir.getPath() + File.separator);
return appCacheDir.getPath() + File.separator;
}
/**
* 获取SD卡缓存目录
*
* @param context 上下文
* @param type 文件夹类型 如果为空则返回 /storage/emulated/0/Android/data/app_package_name/cache
* 否则返回对应类型的文件夹如Environment.DIRECTORY_PICTURES 对应的文件夹为 .../data/app_package_name/files/Pictures
* {@link android.os.Environment#DIRECTORY_MUSIC},
* {@link android.os.Environment#DIRECTORY_PODCASTS},
* {@link android.os.Environment#DIRECTORY_RINGTONES},
* {@link android.os.Environment#DIRECTORY_ALARMS},
* {@link android.os.Environment#DIRECTORY_NOTIFICATIONS},
* {@link android.os.Environment#DIRECTORY_PICTURES}, or
* {@link android.os.Environment#DIRECTORY_MOVIES}.or 自定义文件夹名称
* @return 缓存目录文件夹 或 null无SD卡或SD卡挂载失败
*/
public static File getExternalCacheDirectory(Context context, String type) {
File appCacheDir = null;
if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
if (TextUtils.isEmpty(type)) {
appCacheDir = context.getExternalCacheDir();
} else {
appCacheDir = context.getExternalFilesDir(type);
}
if (appCacheDir == null) {// 有些手机需要通过自定义目录
appCacheDir = new File(Environment.getExternalStorageDirectory(), "Android/data/" + context.getPackageName() + "/cache/" + type);
}
if (appCacheDir == null) {
Log.e("FileUtils", "getExternalDirectory fail ,the reason is sdCard unknown exception !");
} else {
if (!appCacheDir.exists() && !appCacheDir.mkdirs()) {
Log.e("FileUtils", "getExternalDirectory fail ,the reason is make directory fail !");
}
}
} else {
Log.e("FileUtils", "getExternalDirectory fail ,the reason is sdCard nonexistence or sdCard mount fail !");
}
return appCacheDir;
}
/**
* 获取内存缓存目录
*
* @param type 子目录,可以为空,为空直接返回一级目录
* @return 缓存目录文件夹 或 null创建目录文件失败
* 注:该方法获取的目录是能供当前应用自己使用,外部应用没有读写权限,如 系统相机应用
*/
public static File getInternalCacheDirectory(Context context, String type) {
File appCacheDir = null;
if (TextUtils.isEmpty(type)) {
appCacheDir = context.getCacheDir();// /data/data/app_package_name/cache
} else {
appCacheDir = new File(context.getFilesDir(), type);// /data/data/app_package_name/files/type
}
if (!appCacheDir.exists() && !appCacheDir.mkdirs()) {
Log.e("FileUtils", "getInternalDirectory fail ,the reason is make directory fail !");
}
return appCacheDir;
}
/**
* 删除缓存文件
*
* @param context
* @param fileName 视频文件名称
*/
public static void clearCacheFile(Context context, String fileName) {
String filepath = getCacheDirectory(context, "");
File file = new File(filepath + fileName);
if (file.exists()) {
file.delete();
}
}
private static final int NOT_FOUND = -1;
/**
* The Unix separator character.
*/
private static final char UNIX_NAME_SEPARATOR = '/';
/**
* The Windows separator character.
*/
private static final char WINDOWS_NAME_SEPARATOR = '\\';
/**
* The extension separator character.
*
* @since 1.4
*/
public static final char EXTENSION_SEPARATOR = '.';
public static String getExtension(final String filename) {
if (filename == null) {
return null;
}
final int index = indexOfExtension(filename);
if (index == NOT_FOUND) {
return "";
} else {
return filename.substring(index + 1);
}
}
public static int indexOfExtension(final String filename) {
if (filename == null) {
return NOT_FOUND;
}
final int extensionPos = filename.lastIndexOf(EXTENSION_SEPARATOR);//点的位置
final int lastSeparator = indexOfLastSeparator(filename);//最后一个斜杠的位置
return lastSeparator > extensionPos ? NOT_FOUND : extensionPos;
}
public static int indexOfLastSeparator(final String filename) {
if (filename == null) {
return NOT_FOUND;
}
final int lastUnixPos = filename.lastIndexOf(UNIX_NAME_SEPARATOR);//unix的/
final int lastWindowsPos = filename.lastIndexOf(WINDOWS_NAME_SEPARATOR);// windows的\
return Math.max(lastUnixPos, lastWindowsPos);
}
public static void main(String[] args) {
String url = "https://img.zhidaohulian.com/fileServer/online_car_hailing/1676357834634/5.m4v";
String[] fileName = url.split("/");
String fileSuffix = getExtension(fileName[fileName.length - 1]);
System.out.println(fileSuffix);
}
}

View File

@@ -1,4 +1,4 @@
package com.mogo.och.common.module.wigets.video
package com.mogo.och.common.module.wigets.media
data class MediaDataList(val ads: MutableList<MediaItem>)

View File

@@ -1,4 +1,4 @@
package com.mogo.och.common.module.wigets.video
package com.mogo.och.common.module.wigets.media
import android.annotation.SuppressLint
import android.content.Context
@@ -17,8 +17,8 @@ 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 com.mogo.och.common.module.wigets.media.MediaItem.Companion.MEDIA_TYPE_IMAGE
import com.mogo.och.common.module.wigets.media.MediaItem.Companion.MEDIA_TYPE_VIDEO
import io.reactivex.Observable
import retrofit2.http.GET
import retrofit2.http.Headers

View File

@@ -0,0 +1,87 @@
package com.mogo.och.common.module.wigets.media
import android.content.Context
import android.os.Environment
import android.text.TextUtils
import android.util.Log
import com.mogo.eagle.core.utilcode.download.DownloadUtils
import com.mogo.eagle.core.utilcode.download.callback.IDownloadListener
import com.mogo.eagle.core.utilcode.util.EncryptUtils
import com.mogo.och.common.module.utils.FileUtils
import java.io.File
/**
* 宣传视频文件本地缓存管理类
* 1, 统一了本地缓存文件的存放目录
* 2统一了本地缓存文件的命名base64编码
*/
object MediaFileCacheManager {
const val TAG = "MediaFileCacheManager"
/**
* 创建media缓存文件夹目录优先放SD卡
*/
fun createFileCacheDir(context: Context): Boolean {
val cacheDirPath = getFileCacheDir(context)
return com.mogo.eagle.core.utilcode.util.FileUtils.createFileDir(cacheDirPath)
}
/**
* 获取本地缓存文件的文件全路径
*/
private fun getFileCacheDir(context: Context): String {
// 有些手机需要通过自定义目录
val relativePath = "mogo" + File.separator + "media" + File.separator
val cacheDir = File(Environment.getExternalStorageDirectory(), relativePath)
if (com.mogo.eagle.core.utilcode.util.FileUtils.createOrExistsDir(cacheDir)) {
return cacheDir.absolutePath
}
return FileUtils.getCacheDirectory(context, "") + relativePath
}
/**
* 本地缓存文件的文件名md5编码避免文件名重复或者特殊字符编码问题
*/
fun getCacheFileName(mediaUrl: String): String {
val fileSuffix = FileUtils.getExtension(mediaUrl)
if (TextUtils.isEmpty(fileSuffix)) {
Log.e(TAG, "getCacheFileName 根据url获取文件后缀不合法mediaUrl=$mediaUrl")
return ""
}
return EncryptUtils.encryptMD5ToString(mediaUrl) + FileUtils.EXTENSION_SEPARATOR + fileSuffix
}
/**
* 获取文件缓存的缓存path, 文件名以base64编码避免 中文命名,重复文件名的影响
*/
fun getCacheFileFullPathByUrl(context: Context, mediaUrl: String): String {
return getFileCacheDir(context) + getCacheFileName(mediaUrl)
}
/**
* 本地是否已经存在下载完成的文件
*/
fun isLocalCacheFileExists(context: Context, mediaUrl: String): Boolean {
val localVideoCacheFilePath =
getCacheFileFullPathByUrl(context, mediaUrl)
return com.mogo.eagle.core.utilcode.util.FileUtils.isFileExists(localVideoCacheFilePath)
}
/**
* 下载文件
*/
fun downloadFile(context: Context, mediaUrl: String, listener: IDownloadListener) {
val downloadUrl = mediaUrl
val downloadDir = getFileCacheDir(context)
val downloadFileName = getCacheFileName(mediaUrl)
DownloadUtils.downLoad(
context,
downloadUrl,
downloadDir,
downloadFileName,
listener
)
}
}

View File

@@ -1,4 +1,4 @@
package com.mogo.och.common.module.wigets.video
package com.mogo.och.common.module.wigets.media
import AdvanceImageView
import AdvanceVideoView
@@ -13,7 +13,7 @@ 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.mogo.och.common.module.wigets.media.MediaLoopPlayView.Companion.IMAGE_COUNT_DOWN_SECONDS
import com.shuyu.gsyvideoplayer.listener.GSYSampleCallBack
class MediaLoopPlayView @JvmOverloads constructor(
@@ -140,11 +140,11 @@ class AdvancePagerAdapter(context: Context, viewPager: ViewPager) : PagerAdapter
private fun addItemView(item: MediaItem) {
if (item.isImageType()) {
val imageView = AdvanceImageView(mContext)
imageView.setImagePath(item.path)
imageView.initImageUrlData(item.path)
mItemViewList.add(imageView)
} else if (item.isVideoType()) {
val videoView = AdvanceVideoView(mContext)
videoView.setVideoPath(item.path, item.cacheImgPath)
videoView.initVideoUrlData(item.path, item.cacheImgPath)
mItemViewList.add(videoView)
} else {
CallerLogger.d(MediaLoopPlayView.TAG, "addItemView 不支持的文件类型:${item.type}")
@@ -165,13 +165,15 @@ class AdvancePagerAdapter(context: Context, viewPager: ViewPager) : PagerAdapter
"startLoopPlay: AdvanceVideoView, url=${currentMediaItem.path}"
)
val videoView = mItemViewList[currentPosition] as AdvanceVideoView
videoView.setCacheImageViewVisible()
videoView.setVideo(videoPlayLifecycleCallBack)
videoView.setThumbImageViewVisible()
videoView.startPlayVideo(videoPlayLifecycleCallBack)
} else if (mItemViewList[currentPosition] is AdvanceImageView) {
CallerLogger.d(
MediaLoopPlayView.TAG,
"startLoopPlay: AdvanceImageView, url=${currentMediaItem.path}"
)
val imageView = mItemViewList[currentPosition] as AdvanceImageView
imageView.displayImage()
startImageCountDownTimer()
} else {
CallerLogger.d(
@@ -275,7 +277,7 @@ class AdvancePagerAdapter(context: Context, viewPager: ViewPager) : PagerAdapter
val currentPosition = mViewPager.currentItem
if (mItemViewList[currentPosition] is AdvanceVideoView) {
val videoView = mItemViewList[mViewPager.currentItem] as AdvanceVideoView
videoView.setPause()
videoView.setVideoPause()
} else if (mItemViewList[currentPosition] is AdvanceImageView) {
cancelImageCountDownTimer()
}
@@ -288,7 +290,7 @@ class AdvancePagerAdapter(context: Context, viewPager: ViewPager) : PagerAdapter
val currentPosition = mViewPager.currentItem
if (mItemViewList[currentPosition] is AdvanceVideoView) {
val videoView = mItemViewList[mViewPager.currentItem] as AdvanceVideoView
videoView.setResume()
videoView.setVideoResume()
} else if (mItemViewList[currentPosition] is AdvanceImageView) {
startImageCountDownTimer()
}

View File

@@ -1,16 +1,16 @@
package com.mogo.och.common.module.wigets.video
package com.mogo.och.common.module.wigets.media
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.mogo.eagle.core.utilcode.util.BarUtils
import com.mogo.och.common.module.R
class VideoPlayerActivity : AppCompatActivity() {
class MediaPlayerActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_video_player)
val fragment = VideoPlayerFragment()
val fragment = MediaPlayerFragment()
supportFragmentManager.beginTransaction().add(R.id.videoPlayerContainer, fragment)
.commitAllowingStateLoss()
BarUtils.hideStatusBarAndSticky(this.window)

View File

@@ -0,0 +1,373 @@
import android.content.Context
import android.media.AudioManager
import android.net.Uri
import android.util.AttributeSet
import android.widget.ImageView
import android.widget.RelativeLayout
import com.bumptech.glide.Glide
import com.bumptech.glide.request.RequestOptions
import com.mogo.eagle.core.utilcode.download.callback.IDownloadListener
import com.mogo.eagle.core.utilcode.mogo.logger.Logger
import com.mogo.eagle.core.utilcode.util.FileUtils
import com.mogo.eagle.core.utilcode.util.ThreadUtils
import com.mogo.eagle.core.utilcode.util.UiThreadHandler
import com.mogo.eagle.core.widget.media.video.TextureVideoViewOutlineProvider
import com.mogo.och.common.module.R
import com.mogo.och.common.module.wigets.media.MediaFileCacheManager
import com.mogo.och.common.module.wigets.media.MediaLoopPlayView
import com.shuyu.gsyvideoplayer.builder.GSYVideoOptionBuilder
import com.shuyu.gsyvideoplayer.listener.GSYSampleCallBack
import com.shuyu.gsyvideoplayer.utils.GSYVideoType
import com.shuyu.gsyvideoplayer.video.StandardGSYVideoPlayer
import me.jessyan.autosize.utils.AutoSizeUtils
import java.io.File
class AdvanceVideoView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null
) : RelativeLayout(context, attrs) {
private var containerLayout: RelativeLayout? = null
private var thumbnailImageView: ImageView? = null
private var videoPlayerView: AdvanceGSYVideoPlayer? = null
private var gsyVideoPlayerOptionBuilder: GSYVideoOptionBuilder? = null
private var gsyVideoPlayerLifecycleCallback: GSYSampleCallBack? = null
private var thumbnailImageUrl: String = ""
private var videoUrl: String = ""
private val downListener = object : IDownloadListener {
override fun onStart(url: String) {
setThumbImageViewVisible()
Logger.d(MediaLoopPlayView.TAG, "video play download, onStart")
}
override fun onProgress(url: String, downloaded: Long, total: Long) {
Logger.d(
MediaLoopPlayView.TAG,
"video play download, onProgress= ${(downloaded * 100 / total).toInt()}"
)
}
override fun onFinished(url: String, path: String) {
Logger.d(MediaLoopPlayView.TAG, "video play download, onFinished = $url")
//发现下载工具在断网又连网后,已完成的任务又都下载,跳转播放出现问题
if (url == videoUrl) {
//下载完成
ThreadUtils.runOnUiThread {
startPlay(Uri.fromFile(File(path)).toString())
}
} else {//如果当前文件不存在再次去下载当前的
Logger.d(
MediaLoopPlayView.TAG,
"video play download, onFinished but not current url , currentUrl = $videoUrl"
)
if (FileUtils.isFileExists(path)) {
Logger.d(MediaLoopPlayView.TAG, "video play download, had download, startPlay")
ThreadUtils.runOnUiThread {
startPlay(Uri.fromFile(File(path)).toString())
}
} else {
startDownLoadVideoFile()
}
}
}
override fun onError(url: String, error: String?) {
Logger.d(MediaLoopPlayView.TAG, "video play download, onError msg=$error")
//出错再次下载
startDownLoadVideoFile()
}
}
init {
initView()
}
private fun initView() {
//容器
containerLayout = RelativeLayout(context)
addView(containerLayout, LayoutParams(-1, -1))
//缩略图
thumbnailImageView = ImageView(context)
thumbnailImageView?.scaleType = ImageView.ScaleType.FIT_XY
//视频播放控件
if (videoPlayerView === null) {
videoPlayerView = AdvanceGSYVideoPlayer(context)
}
val videoPlayerViewLayoutParams = LayoutParams(-1, -1)
//设置videoview占满父view播放
videoPlayerViewLayoutParams.addRule(ALIGN_PARENT_LEFT)
videoPlayerViewLayoutParams.addRule(ALIGN_PARENT_RIGHT)
videoPlayerViewLayoutParams.addRule(ALIGN_PARENT_TOP)
videoPlayerViewLayoutParams.addRule(ALIGN_PARENT_BOTTOM)
containerLayout?.addView(videoPlayerView, videoPlayerViewLayoutParams)
}
/**
* 初始化数据
*/
fun initVideoUrlData(videoUrl: String, thumbnailImageUrl: String) {
// https://img.zhidaohulian.com/fileServer/online_car_hailing/1676357834634/5.m4v
// https://img.zhidaohulian.com/fileServer/online_car_hailing/1676360274126/10.mp4
this.videoUrl = videoUrl
this.thumbnailImageUrl = thumbnailImageUrl
}
/**
* 开始播放逻辑
*/
fun startPlayVideo(onCompletionListener: GSYSampleCallBack) {
//首先根据url检查video是否有已经下载完的本地视频文件
gsyVideoPlayerLifecycleCallback = onCompletionListener
if (MediaFileCacheManager.isLocalCacheFileExists(context, this.videoUrl)) {
val localVideoCacheFilePath = MediaFileCacheManager.getCacheFileFullPathByUrl(
context, this.videoUrl
)
Logger.d(
MediaLoopPlayView.TAG, "本地已经有缓存文件准备开始播放videoPath=${
localVideoCacheFilePath
}"
)
val realUri = Uri.fromFile(File(localVideoCacheFilePath)).toString()
setThumbImageViewGone()
startPlay(realUri)
Logger.d(
MediaLoopPlayView.TAG, "播放视频videoUri=$realUri"
)
} else {
thumbnailImageView?.setImageResource(R.drawable.video_holder)
videoPlayerView?.thumbImageView = thumbnailImageView
thumbnailImageView?.also {
Glide.with(context).asBitmap().load(thumbnailImageUrl)
.apply(
RequestOptions().useUnlimitedSourceGeneratorsPool(true)
.placeholder(R.drawable.video_holder)
.error(R.drawable.video_holder)
.fallback(R.drawable.video_holder)
.centerCrop()
)
.into(it)
}
setThumbImageViewVisible()
startDownLoadVideoFile()
}
}
fun setThumbImageViewVisible() {
UiThreadHandler.post {
videoPlayerView?.setCacheImageViewVisible()
}
}
private fun setThumbImageViewGone() {
UiThreadHandler.post {
videoPlayerView?.setCacheImageViewGone()
}
}
private fun startDownLoadVideoFile() {
//下载视频,下载成功后再播放
Logger.d(MediaLoopPlayView.TAG, "startDownLoadVideoFile")
MediaFileCacheManager.downloadFile(
context,
videoUrl,
downListener
)
}
fun clearLocalErrorVideo() {
val localVideoPath = MediaFileCacheManager.getCacheFileFullPathByUrl(context, videoUrl)
if (FileUtils.isFileExists(localVideoPath)) {
FileUtils.delete(localVideoPath)
}
}
fun startPlay(localVideoPath: String?) {
try {
Logger.d(MediaLoopPlayView.TAG, "startPlay")
gsyVideoPlayerOptionBuilder = GSYVideoOptionBuilder()
gsyVideoPlayerOptionBuilder
?.setUrl(localVideoPath) // "/data/user/0/com.mogo.launcher.f/files/video/"
?.setPlayTag(MediaFileCacheManager.getCacheFileName(videoUrl))
?.setCacheWithPlay(false)
?.setThumbPlay(false)
?.build(videoPlayerView)
videoPlayerView?.isFocusableInTouchMode = false
videoPlayerView?.setVideoAllCallBack(gsyVideoPlayerLifecycleCallback)
videoPlayerView?.startPlayLogic()
} catch (e: Exception) {
Logger.e(MediaLoopPlayView.TAG, "startPlay error, msg=${e.message}")
}
}
fun onVideoReset() {
videoPlayerView?.onVideoReset()
gsyVideoPlayerLifecycleCallback = null
}
fun setVideoPause() {
videoPlayerView?.onVideoPause()
}
fun setVideoResume() {
videoPlayerView?.onVideoResume()
}
}
class AdvanceGSYVideoPlayer : StandardGSYVideoPlayer {
constructor(context: Context?) : super(context)
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
init {
hideWidget()
GSYVideoType.setShowType(GSYVideoType.SCREEN_MATCH_FULL)
GSYVideoType.setRenderType(GSYVideoType.GLSURFACE)
}
override fun hideAllWidget() {
Logger.d(MediaLoopPlayView.TAG, "AdvanceGSYVideoPlayerhideAllWidget")
}
override fun changeUiToNormal() {
Logger.d(MediaLoopPlayView.TAG, "AdvanceGSYVideoPlayerchangeUiToNormal-hide")
hideWidget()
}
override fun changeUiToPreparingShow() {
Logger.d(MediaLoopPlayView.TAG, "AdvanceGSYVideoPlayerchangeUiToPreparingShow-hide")
hideWidget()
}
override fun changeUiToPlayingShow() {
Logger.d(MediaLoopPlayView.TAG, "AdvanceGSYVideoPlayerchangeUiToPlayingShow")
setCacheImageViewGone()
}
override fun changeUiToPauseShow() {
Logger.d(MediaLoopPlayView.TAG, "AdvanceGSYVideoPlayerchangeUiToPauseShow-hide")
startPlayLogic()
}
override fun changeUiToCompleteShow() {
Logger.d(MediaLoopPlayView.TAG, "AdvanceGSYVideoPlayerchangeUiToCompleteShow")
setCacheImageViewGone()
}
override fun changeUiToPlayingBufferingShow() {
Logger.d(MediaLoopPlayView.TAG, "AdvanceGSYVideoPlayerchangeUiToPlayingBufferingShow")
hideWidget()
}
override fun changeUiToError() {
Logger.d(MediaLoopPlayView.TAG, "AdvanceGSYVideoPlayerchangeUiToError")
hideWidget()
}
private fun hideWidget() {
setViewShowState(mBottomContainer, INVISIBLE)
setViewShowState(mProgressBar, INVISIBLE)
setViewShowState(mCurrentTimeTextView, INVISIBLE)
setViewShowState(mTotalTimeTextView, INVISIBLE)
setViewShowState(mBottomProgressBar, INVISIBLE)
setViewShowState(mBackButton, INVISIBLE)
setViewShowState(mStartButton, INVISIBLE)
setViewShowState(mThumbImageViewLayout, VISIBLE)
setViewShowState(mThumbImageView, VISIBLE)
setViewShowState(mTopContainer, INVISIBLE)
setViewShowState(mLoadingProgressBar, INVISIBLE)
setViewShowState(mLockScreen, INVISIBLE)
setIsTouchWiget(false)
isFocusableInTouchMode = false
}
fun setCacheImageViewVisible() {
Logger.d(MediaLoopPlayView.TAG, "AdvanceGSYVideoPlayersetCacheImageViewVisible")
setViewShowState(mThumbImageViewLayout, VISIBLE)
// setViewShowState(mThumbImageView, VISIBLE)
}
fun setCacheImageViewGone() {
Logger.d(MediaLoopPlayView.TAG, "AdvanceGSYVideoPlayersetCacheImageViewGone")
setViewShowState(mThumbImageViewLayout, INVISIBLE)
// setViewShowState(mThumbImageView, INVISIBLE)
}
//失去焦点声音压低
override fun onLossTransientCanDuck() {
// setStreamVolume(0.2f)
setNeedMute(true)
}
//获取焦点声音恢复
override fun onGankAudio() {
// setStreamVolume(5.0f)
setNeedMute(false)
}
private fun setStreamVolume(percent: Float) {
var mAudioManager = mContext?.getSystemService(Context.AUDIO_SERVICE) as AudioManager
var maxVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC)
var volume = (percent * maxVolume).toInt()
if (volume < 0) {
volume = 0
}
mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0)
}
private fun setNeedMute(isMute: Boolean) {
gsyVideoManager?.player?.setNeedMute(isMute)
}
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
if (!mIfCurrentIsFullscreen) {
val dp2px = AutoSizeUtils.dp2px(context, 16f)
this.outlineProvider = TextureVideoViewOutlineProvider(dp2px.toFloat())
this.clipToOutline = true
}
}
}
class AdvanceImageView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null
) : RelativeLayout(context, attrs) {
private var imageView: ImageView? = null
private var imageUrl: String = ""
init {
initView()
}
private fun initView() {
imageView = ImageView(context)
imageView?.scaleType = ImageView.ScaleType.FIT_XY
addView(imageView, LayoutParams(-1, -1))
}
fun initImageUrlData(url: String) {
this.imageUrl = url
}
fun displayImage() {
imageView?.setImageResource(R.drawable.video_holder)
imageView?.also {
Glide.with(context).asBitmap().load(imageUrl).apply(
RequestOptions().useUnlimitedSourceGeneratorsPool(true)
.placeholder(R.drawable.video_holder)
.error(R.drawable.video_holder)
.fallback(R.drawable.video_holder)
.centerCrop()
).into(it)
}
}
}

View File

@@ -1,7 +1,8 @@
package com.mogo.och.common.module.wigets.video
package com.mogo.och.common.module.wigets.media
import com.mogo.commons.mvp.MvpFragment
import com.mogo.commons.mvp.Presenter
import com.mogo.eagle.core.function.main.MainMoGoApplication
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
@@ -12,11 +13,11 @@ import kotlinx.android.synthetic.main.fragment_video_player.imageVideoRotationVi
* @author: wangmingjun
* @date: 2022/4/12
*/
class VideoPlayerFragment :
MvpFragment<VideoPlayerFragment?, VideoPlayerPresenter?>() {
class MediaPlayerFragment :
MvpFragment<MediaPlayerFragment?, VideoPlayerPresenter?>() {
companion object {
private val TAG = VideoPlayerFragment::class.java.simpleName
private val TAG = MediaPlayerFragment::class.java.simpleName
}
private var arrayListOf = mutableListOf<MediaItem>()
@@ -34,6 +35,7 @@ class VideoPlayerFragment :
}
override fun initViews() {
MediaFileCacheManager.createFileCacheDir(MainMoGoApplication.getApp().applicationContext)
AdDataSourceManager.init(TAG, object : IAdDataSourceListener {
override fun onAdDataSourceChanged(list: List<MediaItem>) {
val isNewData = arrayListOf.isNotEmpty()
@@ -69,5 +71,5 @@ class VideoPlayerFragment :
}
}
class VideoPlayerPresenter(view: VideoPlayerFragment?) :
Presenter<VideoPlayerFragment?>(view)
class VideoPlayerPresenter(view: MediaPlayerFragment?) :
Presenter<MediaPlayerFragment?>(view)

View File

@@ -1,388 +0,0 @@
import android.annotation.SuppressLint
import android.content.Context
import android.media.AudioManager
import android.net.Uri
import android.util.AttributeSet
import android.widget.ImageView
import android.widget.RelativeLayout
import com.bumptech.glide.Glide
import com.bumptech.glide.request.RequestOptions
import com.mogo.eagle.core.utilcode.download.*
import com.mogo.eagle.core.utilcode.download.callback.*
import com.mogo.eagle.core.utilcode.mogo.logger.Logger
import com.mogo.eagle.core.utilcode.util.FileUtils
import com.mogo.eagle.core.utilcode.util.ThreadUtils
import com.mogo.eagle.core.utilcode.util.UiThreadHandler
import com.mogo.eagle.core.widget.media.video.TextureVideoViewOutlineProvider
import com.mogo.och.common.module.R
import com.mogo.och.common.module.wigets.video.MediaLoopPlayView
import com.shuyu.gsyvideoplayer.builder.GSYVideoOptionBuilder
import com.shuyu.gsyvideoplayer.listener.GSYSampleCallBack
import com.shuyu.gsyvideoplayer.utils.GSYVideoType
import com.shuyu.gsyvideoplayer.video.StandardGSYVideoPlayer
import me.jessyan.autosize.utils.AutoSizeUtils
import java.io.File
class AdvanceVideoView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null
) : RelativeLayout(context, attrs) {
private var videoRelativeLayout: RelativeLayout? = null
private var cacheImage: ImageView? = null
private var videoViewPlayer: AdvanceGSYVideoPlayer? = null
private var gsyVideoOptionBuilder: GSYVideoOptionBuilder? = null
private var mOnCompletionListener: GSYSampleCallBack? = null
private var downloadVideoName = ""
private var fileNetPath: String? = ""
private var cacheImageUrl: String? = ""
private var mVideoDirPath: String? = ""
init {
mVideoDirPath = context.filesDir.absolutePath + File.separator + "video" + File.separator
initView()
}
private fun initView() {
initCacheImgView()
initVideoView()
}
private fun initCacheImgView() {
cacheImage = ImageView(context)
cacheImage?.scaleType = ImageView.ScaleType.FIT_XY
}
private fun initVideoView() {
videoRelativeLayout = RelativeLayout(context)
addView(videoRelativeLayout, LayoutParams(-1, -1))
if (videoViewPlayer === null) {
//视频播放控件
videoViewPlayer = AdvanceGSYVideoPlayer(context)
}
var layoutParams = LayoutParams(-1, -1)
//设置videoview占满父view播放
layoutParams.addRule(ALIGN_PARENT_LEFT)
layoutParams.addRule(ALIGN_PARENT_RIGHT)
layoutParams.addRule(ALIGN_PARENT_TOP)
layoutParams.addRule(ALIGN_PARENT_BOTTOM)
videoRelativeLayout?.addView(videoViewPlayer, layoutParams)
}
fun setVideoPath(path: String, cacheImageUrl: String) {
// https://img.zhidaohulian.com/fileServer/online_car_hailing/1676357834634/5.m4v
// https://img.zhidaohulian.com/fileServer/online_car_hailing/1676360274126/10.mp4
this.fileNetPath = path
this.cacheImageUrl = cacheImageUrl
val pathList = path.split("/")
if (pathList.isNotEmpty()) {
this.downloadVideoName = pathList[pathList.size - 1]
}
}
private fun loadCacheImg() {
videoViewPlayer?.thumbImageView = cacheImage
cacheImage?.setImageResource(R.drawable.video_holder)
// setCacheImageViewVisible()
Logger.d(MediaLoopPlayView.TAG, "setVideoPath")
// cacheImage?.let {
// Glide.with(context).asBitmap().load(cacheImageUrl)
// .apply(
// RequestOptions().useUnlimitedSourceGeneratorsPool(true)
// .placeholder(R.drawable.video_holder)
// .error(R.drawable.video_holder)
// .fallback(R.drawable.video_holder)
// .centerCrop()
// )
// .into(it)
// }
}
fun clearLocalErrorVideo() {
if (downloadVideoName.isNotEmpty()
&& FileUtils.isFileExists(mVideoDirPath + downloadVideoName)
) {
FileUtils.delete(mVideoDirPath + downloadVideoName)
}
}
@SuppressLint("CheckResult")
fun setCacheImageViewVisible() {
UiThreadHandler.post {
videoViewPlayer?.setCacheImageViewVisible()
}
}
fun setCacheImageViewGone() {
UiThreadHandler.post {
videoViewPlayer?.setCacheImageViewGone()
}
}
fun setVideo(onCompletionListener: GSYSampleCallBack) {
loadCacheImg()
Logger.d(MediaLoopPlayView.TAG, "setVideo")
mOnCompletionListener = onCompletionListener
//判断是否已经下载
if (downloadVideoName.isNotEmpty()) {
Logger.d(
MediaLoopPlayView.TAG,
"video local url = $mVideoDirPath$downloadVideoName"
)
if (FileUtils.isFileExists(mVideoDirPath + downloadVideoName)) {
Logger.d(MediaLoopPlayView.TAG, "have cache startPlay")
startPlay(Uri.fromFile(File(mVideoDirPath + downloadVideoName)).toString())
return
}
startDownLoadVideo()
}
}
private fun startDownLoadVideo() {
//下载视频, 下载成功后再播放
Logger.d(MediaLoopPlayView.TAG, "startDownLoadVideo")
FileUtils.createFileDir(mVideoDirPath)
val downloadUrl = fileNetPath
val downloadDir = mVideoDirPath
if (downloadUrl != null && downloadDir != null) {
DownloadUtils.downLoad(
context, downloadUrl, downloadDir, downloadVideoName, downListener
)
}
}
fun startPlay(localVideoPath: String?) {
try {
Logger.d(MediaLoopPlayView.TAG, "startPlay")
gsyVideoOptionBuilder = GSYVideoOptionBuilder()
gsyVideoOptionBuilder
?.setUrl(
localVideoPath
) // "/data/user/0/com.mogo.launcher.f/files/video/"
?.setPlayTag(downloadVideoName)
?.setCacheWithPlay(false)
?.setThumbPlay(false)
?.build(videoViewPlayer)
videoViewPlayer?.isFocusableInTouchMode = false
videoViewPlayer?.setVideoAllCallBack(mOnCompletionListener)
videoViewPlayer?.startPlayLogic()
} catch (e: Exception) {
Logger.d(MediaLoopPlayView.TAG, "startPlay e = ${e.message}")
}
}
fun onVideoReset() {
videoViewPlayer?.onVideoReset()
mOnCompletionListener = null
}
fun setPause() {
if (videoViewPlayer !== null) {
videoViewPlayer?.onVideoPause()
}
}
fun setResume() {
if (videoViewPlayer !== null) {
videoViewPlayer?.onVideoResume()
}
}
private val downListener = object : IDownloadListener {
override fun onStart(url: String) {
setCacheImageViewVisible()
Logger.d(MediaLoopPlayView.TAG, "download-onStart")
}
override fun onProgress(url: String, downloaded: Long, total: Long) {
Logger.d(MediaLoopPlayView.TAG, "download-onProgress== ${ (downloaded * 100 / total).toInt() }")
}
override fun onFinished(url: String, path: String) {
Logger.d(MediaLoopPlayView.TAG, "download-onFinished = $url")
if (url == fileNetPath) { //发现下载工具在断网又连网后,已完成的任务又都下载,跳转播放出现问题
//下载完成
ThreadUtils.runOnUiThread {
startPlay(Uri.fromFile(File(path)).toString())
}
} else {//如果当前文件不存在再次去下载当前的
Logger.d(
MediaLoopPlayView.TAG, "download-onFinished = not current" +
",currentUrl = $fileNetPath "
)
if (FileUtils.isFileExists(path)) {
Logger.d(MediaLoopPlayView.TAG, "have download startPlay")
ThreadUtils.runOnUiThread {
startPlay(Uri.fromFile(File(path)).toString())
}
return
} else {
startDownLoadVideo()
}
}
}
override fun onError(url: String, error: String?) {
Logger.d(MediaLoopPlayView.TAG, "download-onError-$error")
//出错再次下载
startDownLoadVideo()
}
}
}
class AdvanceImageView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null
) : RelativeLayout(context, attrs) {
private var imageView: ImageView? = null
init {
initView()
}
private fun initView() {
imageView = ImageView(context)
imageView?.scaleType = ImageView.ScaleType.FIT_XY
addView(imageView, LayoutParams(-1, -1))
}
@SuppressLint("CheckResult")
fun setImagePath(path: String) {
imageView?.setImageResource(R.drawable.video_holder)
imageView?.let {
Glide.with(context).asBitmap().load(path)
.apply(
RequestOptions().useUnlimitedSourceGeneratorsPool(true)
.placeholder(R.drawable.video_holder)
.error(R.drawable.video_holder)
.fallback(R.drawable.video_holder)
.centerCrop()
)
.into(it)
}
}
}
class AdvanceGSYVideoPlayer : StandardGSYVideoPlayer {
constructor(context: Context?) : super(context)
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
init {
hideWidget()
GSYVideoType.setShowType(GSYVideoType.SCREEN_MATCH_FULL)
GSYVideoType.setRenderType(GSYVideoType.GLSURFACE)
}
override fun hideAllWidget() {
Logger.d("ImageAndVideoRotation", "hideAllWidget")
}
override fun changeUiToNormal() {
Logger.d("ImageAndVideoRotation", "changeUiToNormal-hide")
hideWidget()
}
override fun changeUiToPreparingShow() {
Logger.d("ImageAndVideoRotation", "changeUiToPreparingShow-hide")
hideWidget()
}
override fun changeUiToPlayingShow() {
Logger.d("ImageAndVideoRotation", "changeUiToPlayingShow")
setCacheImageViewGone()
}
override fun changeUiToPauseShow() {
Logger.d("ImageAndVideoRotation", "changeUiToPauseShow-hide")
startPlayLogic()
}
override fun changeUiToCompleteShow() {
Logger.d("ImageAndVideoRotation", "changeUiToCompleteShow")
setCacheImageViewGone()
}
override fun changeUiToPlayingBufferingShow() {
Logger.d("ImageAndVideoRotation", "changeUiToPlayingBufferingShow -hide")
hideWidget()
}
override fun changeUiToError() {
Logger.d("ImageAndVideoRotation", "changeUiToError-hide")
hideWidget()
}
private fun hideWidget(){
setViewShowState(mBottomContainer, INVISIBLE)
setViewShowState(mProgressBar, INVISIBLE)
setViewShowState(mCurrentTimeTextView, INVISIBLE)
setViewShowState(mTotalTimeTextView, INVISIBLE)
setViewShowState(mBottomProgressBar, INVISIBLE)
setViewShowState(mBackButton, INVISIBLE)
setViewShowState(mStartButton, INVISIBLE)
setViewShowState(mThumbImageViewLayout, VISIBLE)
setViewShowState(mThumbImageView, VISIBLE)
setViewShowState(mTopContainer, INVISIBLE)
setViewShowState(mLoadingProgressBar, INVISIBLE)
setViewShowState(
mLockScreen, INVISIBLE
)
setIsTouchWiget(false)
isFocusableInTouchMode = false
}
fun setCacheImageViewVisible() {
Logger.d("ImageAndVideoRotation", "CacheImageViewVISIBLE")
setViewShowState(mThumbImageViewLayout, VISIBLE)
// setViewShowState(mThumbImageView, VISIBLE)
}
fun setCacheImageViewGone() {
Logger.d("ImageAndVideoRotation", "CacheImageViewGONE")
setViewShowState(mThumbImageViewLayout, INVISIBLE)
// setViewShowState(mThumbImageView, INVISIBLE)
}
//失去焦点声音压低
override fun onLossTransientCanDuck() {
// setStreamVolume(0.2f)
setNeedMute(true)
}
//获取焦点声音恢复
override fun onGankAudio() {
// setStreamVolume(5.0f)
setNeedMute(false)
}
private fun setStreamVolume(percent: Float){
var mAudioManager = mContext?.getSystemService(Context.AUDIO_SERVICE) as AudioManager
var maxVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC)
var volume = (percent * maxVolume).toInt()
if (volume < 0 ){
volume = 0
}
mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC,volume,0)
}
private fun setNeedMute(isMute: Boolean){
gsyVideoManager?.player?.setNeedMute(isMute)
}
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
if (!mIfCurrentIsFullscreen) {
val dp2px = AutoSizeUtils.dp2px(context, 16f)
this.outlineProvider = TextureVideoViewOutlineProvider(dp2px.toFloat())
this.clipToOutline = true
}
}
}

View File

@@ -3,7 +3,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- 图片或视频广告-->
<com.mogo.och.common.module.wigets.video.MediaLoopPlayView
<com.mogo.och.common.module.wigets.media.MediaLoopPlayView
android:id="@+id/imageVideoRotationView"
android:layout_width="match_parent"
android:layout_height="match_parent" />

View File

@@ -16,7 +16,7 @@ import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger;
import com.mogo.eagle.core.utilcode.util.MultiDisplayUtils;
import com.mogo.och.bus.passenger.constant.BusPassengerConst;
import com.mogo.och.bus.passenger.ui.BusPassengerRouteFragment;
import com.mogo.och.common.module.wigets.video.VideoPlayerActivity;
import com.mogo.och.common.module.wigets.media.MediaPlayerActivity;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -47,7 +47,7 @@ public class MogoOCHBusPassenger implements IMogoOCH {
showFragment();
if (AppIdentityModeUtils.isJL(FunctionBuildConfig.appIdentityMode)) {
MultiDisplayUtils.INSTANCE.startActWithSecond(activity, VideoPlayerActivity.class);
MultiDisplayUtils.INSTANCE.startActWithSecond(activity, MediaPlayerActivity.class);
}
return null;
}

View File

@@ -6,8 +6,8 @@ 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.MediaDataList
import com.mogo.och.common.module.wigets.video.MediaItem
import com.mogo.och.common.module.wigets.media.MediaDataList
import com.mogo.och.common.module.wigets.media.MediaItem
import kotlinx.android.synthetic.m2.p_m2_video_fragment.*
/**

View File

@@ -4,7 +4,7 @@ import android.annotation.SuppressLint
import android.content.Context
import android.util.AttributeSet
import android.widget.RelativeLayout
import com.mogo.och.common.module.wigets.video.MediaItem
import com.mogo.och.common.module.wigets.media.MediaItem
/**
* @author: wangmingjun