|
|
@@ -1,796 +0,0 @@
|
|
|
-package com.adealink.weparty.effect
|
|
|
-
|
|
|
-import android.content.Context
|
|
|
-import android.content.res.TypedArray
|
|
|
-import android.graphics.Matrix
|
|
|
-import android.os.Bundle
|
|
|
-import android.util.AttributeSet
|
|
|
-import android.view.ViewTreeObserver
|
|
|
-import android.widget.FrameLayout
|
|
|
-import android.widget.ImageView
|
|
|
-import androidx.core.view.isVisible
|
|
|
-import com.adealink.frame.base.Rlt
|
|
|
-import com.adealink.frame.coroutine.dispatcher.Dispatcher
|
|
|
-import com.adealink.frame.download.manager.downloadManager
|
|
|
-import com.adealink.frame.download.task.TaskPriority
|
|
|
-import com.adealink.frame.effect.data.INFINITE_LOOP
|
|
|
-import com.adealink.frame.effect.svga.data.PathType
|
|
|
-import com.adealink.frame.log.Log
|
|
|
-import com.adealink.frame.tceffect.TCEffectManager
|
|
|
-import com.adealink.frame.util.AppUtil
|
|
|
-import com.adealink.frame.util.createOrExistsDir
|
|
|
-import com.adealink.frame.util.runOnUiThread
|
|
|
-import com.adealink.weparty.R
|
|
|
-import com.adealink.weparty.commonui.ext.gone
|
|
|
-import com.adealink.weparty.commonui.ext.isMP4
|
|
|
-import com.adealink.weparty.commonui.ext.isSVGAImage
|
|
|
-import com.adealink.weparty.commonui.ext.isTCMP4
|
|
|
-import com.adealink.weparty.commonui.ext.show
|
|
|
-import com.adealink.weparty.storage.file.FilePath
|
|
|
-import com.opensource.svgaplayer.WenextSvgaView
|
|
|
-import com.opensource.svgaplayer.control.ControllerListener
|
|
|
-import com.opensource.svgaplayer.entities.SvgaInfo
|
|
|
-import com.tencent.qgame.animplayer.AnimConfig
|
|
|
-import com.tencent.qgame.animplayer.AnimView
|
|
|
-import com.tencent.qgame.animplayer.inter.IAnimListener
|
|
|
-import com.tencent.qgame.animplayer.util.ScaleType
|
|
|
-import com.tencent.tcmediax.tceffectplayer.api.TCEffectAnimView
|
|
|
-import com.tencent.tcmediax.tceffectplayer.api.TCEffectConfig
|
|
|
-import com.tencent.tcmediax.tceffectplayer.api.TCEffectPlayerConstant
|
|
|
-import com.tencent.tcmediax.tceffectplayer.api.TCEffectPlayerConstant.REPORT_INFO_ON_PLAY_EVT_RCV_FIRST_I_FRAME
|
|
|
-import com.tencent.tcmediax.tceffectplayer.api.data.TCEffectAnimInfo
|
|
|
-import kotlinx.coroutines.CoroutineScope
|
|
|
-import kotlinx.coroutines.Job
|
|
|
-import kotlinx.coroutines.SupervisorJob
|
|
|
-import kotlinx.coroutines.android.asCoroutineDispatcher
|
|
|
-import kotlinx.coroutines.launch
|
|
|
-import kotlinx.coroutines.withContext
|
|
|
-import java.io.File
|
|
|
-import java.io.FileOutputStream
|
|
|
-
|
|
|
-/**
|
|
|
- * 通用动效播放view,支持svga,带透明度mp4(vap或者腾讯云特效格式),tcmp4,特效类型播放
|
|
|
- * 带透明度mp4在腾讯特效播放器可用情况下使用腾讯云特效播放器播放,否则使用vap播放器播放
|
|
|
- */
|
|
|
-open class WeAnimView @JvmOverloads constructor(
|
|
|
- context: Context, attrs: AttributeSet? = null
|
|
|
-) : FrameLayout(context, attrs), CoroutineScope {
|
|
|
- private val serialHandler = Dispatcher.getSerialHandler()
|
|
|
- override val coroutineContext = SupervisorJob() + serialHandler.asCoroutineDispatcher()
|
|
|
-
|
|
|
- private var originWidth: Int = 0
|
|
|
- private var originHeight: Int = 0
|
|
|
-
|
|
|
- private var svgaAnimView: WenextSvgaView? = null
|
|
|
- private var tcAnimView: TCEffectAnimView? = null
|
|
|
- private var vapAnimView: AnimView? = null
|
|
|
-
|
|
|
- private var downloadJob: Job? = null
|
|
|
-
|
|
|
- private var autoPlay: Boolean = true
|
|
|
- private var path: String? = null
|
|
|
- private var pathType: PathType? = null
|
|
|
- private var animType: EffectAnimType? = null
|
|
|
- private var extraConfig: AnimExtraConfig? = null
|
|
|
- private var listener: IWeAnimPlayListener? = null
|
|
|
-
|
|
|
- init {
|
|
|
- attrs?.let { loadAttrs(it) }
|
|
|
- }
|
|
|
-
|
|
|
- private fun loadAttrs(attrs: AttributeSet) {
|
|
|
- if (isInEditMode) {
|
|
|
- return
|
|
|
- }
|
|
|
- val typedArray = context.theme.obtainStyledAttributes(attrs, R.styleable.WeAnimView, 0, 0)
|
|
|
- onAttrsLoad(typedArray, context)
|
|
|
- typedArray.recycle()
|
|
|
- }
|
|
|
-
|
|
|
- protected open fun onAttrsLoad(typedArray: TypedArray, context: Context) {
|
|
|
- autoPlay = typedArray.getBoolean(R.styleable.WeAnimView_autoPlay, true)
|
|
|
- }
|
|
|
-
|
|
|
- fun setAsset(
|
|
|
- url: String,
|
|
|
- animType: EffectAnimType? = null,
|
|
|
- extraConfig: AnimExtraConfig? = null,
|
|
|
- onPlayStart: (() -> Unit)? = null,
|
|
|
- onPlayEnd: (() -> Unit)? = null,
|
|
|
- onPlayError: ((errorCode: String) -> Unit)? = null,
|
|
|
- ) {
|
|
|
- val listener = if (onPlayStart != null || onPlayEnd != null || onPlayError != null) {
|
|
|
- object : IWeAnimPlayListener {
|
|
|
- override fun onPlayStart() {
|
|
|
- onPlayStart?.invoke()
|
|
|
- }
|
|
|
-
|
|
|
- override fun onPlayEnd() {
|
|
|
- onPlayEnd?.invoke()
|
|
|
- }
|
|
|
-
|
|
|
- override fun onPlayError(errorCode: String) {
|
|
|
- onPlayError?.invoke(errorCode)
|
|
|
- }
|
|
|
- }
|
|
|
- } else {
|
|
|
- null
|
|
|
- }
|
|
|
-
|
|
|
- setAsset(url, animType, extraConfig, listener)
|
|
|
- }
|
|
|
-
|
|
|
- fun setAsset(
|
|
|
- assetName: String,
|
|
|
- animType: EffectAnimType? = null,
|
|
|
- extraConfig: AnimExtraConfig? = null,
|
|
|
- listener: IWeAnimPlayListener? = null
|
|
|
- ) {
|
|
|
- Log.d(TAG, "hashCode:${hashCode()}, setAsset, assetName: $assetName")
|
|
|
-
|
|
|
- this.path = assetName
|
|
|
- this.pathType = PathType.ASSERT
|
|
|
- this.animType = animType
|
|
|
- this.extraConfig = extraConfig
|
|
|
- this.listener = listener
|
|
|
-
|
|
|
- var finalAnimType = animType
|
|
|
- if (animType == null) {
|
|
|
- //未指定animType,根据后缀判断
|
|
|
- finalAnimType = getEffectAnimTypeBySuffix(assetName)
|
|
|
- }
|
|
|
- if (finalAnimType == null) {
|
|
|
- stopPlay()
|
|
|
- gone()
|
|
|
- return
|
|
|
- }
|
|
|
- when {
|
|
|
- finalAnimType == EffectAnimType.SVGA -> {
|
|
|
- playSVGA(assetName, PathType.ASSERT, extraConfig, listener)
|
|
|
- }
|
|
|
-
|
|
|
- finalAnimType == EffectAnimType.MP4 && !TCEffectManager.isTcEffectEnable() -> {
|
|
|
- cancelDownloadJob()
|
|
|
- downloadJob = launch {
|
|
|
- val localPath = copyAssetToCache(assetName)
|
|
|
- withContext(Dispatcher.UI) {
|
|
|
- playVapMp4File(localPath, extraConfig, listener)
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- finalAnimType == EffectAnimType.MP4 || finalAnimType == EffectAnimType.TC -> {
|
|
|
- cancelDownloadJob()
|
|
|
- downloadJob = launch {
|
|
|
- val localPath = copyAssetToCache(assetName)
|
|
|
- withContext(Dispatcher.UI) {
|
|
|
- playMp4File(localPath, extraConfig, listener)
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- else -> {
|
|
|
- //其他暂不支持
|
|
|
- stopPlay()
|
|
|
- gone()
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- fun setFile(
|
|
|
- filePath: String,
|
|
|
- animType: EffectAnimType? = null,
|
|
|
- extraConfig: AnimExtraConfig? = null,
|
|
|
- onPlayStart: (() -> Unit)? = null,
|
|
|
- onPlayEnd: (() -> Unit)? = null,
|
|
|
- onPlayError: ((errorCode: String) -> Unit)? = null,
|
|
|
- ) {
|
|
|
- val listener = if (onPlayStart != null || onPlayEnd != null || onPlayError != null) {
|
|
|
- object : IWeAnimPlayListener {
|
|
|
- override fun onPlayStart() {
|
|
|
- onPlayStart?.invoke()
|
|
|
- }
|
|
|
-
|
|
|
- override fun onPlayEnd() {
|
|
|
- onPlayEnd?.invoke()
|
|
|
- }
|
|
|
-
|
|
|
- override fun onPlayError(errorCode: String) {
|
|
|
- onPlayError?.invoke(errorCode)
|
|
|
- }
|
|
|
- }
|
|
|
- } else {
|
|
|
- null
|
|
|
- }
|
|
|
-
|
|
|
- setFile(filePath, animType, extraConfig, listener)
|
|
|
- }
|
|
|
-
|
|
|
- fun setFile(
|
|
|
- filePath: String,
|
|
|
- animType: EffectAnimType? = null,
|
|
|
- extraConfig: AnimExtraConfig? = null,
|
|
|
- listener: IWeAnimPlayListener? = null
|
|
|
- ) {
|
|
|
- Log.d(TAG, "hashCode:${hashCode()}, setFile, filePath: $filePath")
|
|
|
-
|
|
|
- this.path = filePath
|
|
|
- this.pathType = PathType.FILE
|
|
|
- this.animType = animType
|
|
|
- this.extraConfig = extraConfig
|
|
|
- this.listener = listener
|
|
|
-
|
|
|
- var finalAnimType = animType
|
|
|
- if (animType == null) {
|
|
|
- //未指定animType,根据后缀判断
|
|
|
- finalAnimType = getEffectAnimTypeBySuffix(filePath)
|
|
|
- }
|
|
|
- if (finalAnimType == null) {
|
|
|
- stopPlay()
|
|
|
- gone()
|
|
|
- return
|
|
|
- }
|
|
|
- when {
|
|
|
- finalAnimType == EffectAnimType.SVGA -> {
|
|
|
- playSVGA(filePath, PathType.FILE, extraConfig, listener)
|
|
|
- }
|
|
|
-
|
|
|
- finalAnimType == EffectAnimType.MP4 && !TCEffectManager.isTcEffectEnable() -> {
|
|
|
- playVapMp4File(filePath, extraConfig, listener)
|
|
|
- }
|
|
|
-
|
|
|
- finalAnimType == EffectAnimType.MP4 || finalAnimType == EffectAnimType.TC -> {
|
|
|
- playMp4File(filePath, extraConfig, listener)
|
|
|
- }
|
|
|
-
|
|
|
- else -> {
|
|
|
- //其他暂不支持
|
|
|
- stopPlay()
|
|
|
- gone()
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- fun setUrl(
|
|
|
- url: String,
|
|
|
- animType: EffectAnimType? = null,
|
|
|
- extraConfig: AnimExtraConfig? = null,
|
|
|
- onPlayStart: (() -> Unit)? = null,
|
|
|
- onPlayEnd: (() -> Unit)? = null,
|
|
|
- onPlayError: ((errorCode: String) -> Unit)? = null,
|
|
|
- ) {
|
|
|
- val listener = if (onPlayStart != null || onPlayEnd != null || onPlayError != null) {
|
|
|
- object : IWeAnimPlayListener {
|
|
|
- override fun onPlayStart() {
|
|
|
- onPlayStart?.invoke()
|
|
|
- }
|
|
|
-
|
|
|
- override fun onPlayEnd() {
|
|
|
- onPlayEnd?.invoke()
|
|
|
- }
|
|
|
-
|
|
|
- override fun onPlayError(errorCode: String) {
|
|
|
- onPlayError?.invoke(errorCode)
|
|
|
- }
|
|
|
- }
|
|
|
- } else {
|
|
|
- null
|
|
|
- }
|
|
|
-
|
|
|
- setUrl(url, animType, extraConfig, listener)
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- fun setUrl(
|
|
|
- url: String,
|
|
|
- animType: EffectAnimType? = null,
|
|
|
- extraConfig: AnimExtraConfig? = null,
|
|
|
- listener: IWeAnimPlayListener? = null
|
|
|
- ) {
|
|
|
- Log.d(TAG, "hashCode:${hashCode()}, setUrl, url: $url")
|
|
|
-
|
|
|
- this.path = url
|
|
|
- this.pathType = PathType.URL
|
|
|
- this.animType = animType
|
|
|
- this.extraConfig = extraConfig
|
|
|
- this.listener = listener
|
|
|
-
|
|
|
- var finalAnimType = animType
|
|
|
- if (animType == null) {
|
|
|
- //未指定animType,根据后缀判断
|
|
|
- finalAnimType = getEffectAnimTypeBySuffix(url)
|
|
|
- }
|
|
|
- if (finalAnimType == null) {
|
|
|
- stopPlay()
|
|
|
- gone()
|
|
|
- return
|
|
|
- }
|
|
|
- when {
|
|
|
- finalAnimType == EffectAnimType.SVGA -> {
|
|
|
- playSVGA(url, PathType.URL, extraConfig, listener)
|
|
|
- }
|
|
|
-
|
|
|
- finalAnimType == EffectAnimType.MP4 && !TCEffectManager.isTcEffectEnable() -> {
|
|
|
- downloadEffect(url) { effectSavePath ->
|
|
|
- if (effectSavePath.isNullOrEmpty()) {
|
|
|
- gone()
|
|
|
- return@downloadEffect
|
|
|
- }
|
|
|
- playVapMp4File(effectSavePath, extraConfig, listener)
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- finalAnimType == EffectAnimType.MP4 || finalAnimType == EffectAnimType.TC -> {
|
|
|
- downloadEffect(url) { effectSavePath ->
|
|
|
- if (effectSavePath.isNullOrEmpty()) {
|
|
|
- gone()
|
|
|
- return@downloadEffect
|
|
|
- }
|
|
|
- playMp4File(effectSavePath, extraConfig, listener)
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- else -> {
|
|
|
- //其他暂不支持
|
|
|
- stopPlay()
|
|
|
- gone()
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private fun downloadEffect(url: String, onResult: (path: String?) -> Unit) {
|
|
|
- cancelDownloadJob()
|
|
|
- downloadJob = launch {
|
|
|
- var effectSavePath: String? = null
|
|
|
- val downloadRlt = downloadManager.downloadResource(
|
|
|
- FilePath.effectPath ?: FilePath.backupEffectPath,
|
|
|
- url,
|
|
|
- TaskPriority.HIGH
|
|
|
- )
|
|
|
- if (downloadRlt is Rlt.Success) {
|
|
|
- effectSavePath = downloadRlt.data
|
|
|
- }
|
|
|
- withContext(Dispatcher.UI) {
|
|
|
- if (effectSavePath.isNullOrEmpty()) {
|
|
|
- onResult.invoke(null)
|
|
|
- return@withContext
|
|
|
- }
|
|
|
- onResult.invoke(effectSavePath)
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- fun startPlay() {
|
|
|
- svgaAnimView?.startAnimation()
|
|
|
- tcAnimView?.resume()
|
|
|
- if (vapAnimView?.isVisible == true) {
|
|
|
- //vap不支持恢复播放,额外处理
|
|
|
- val path = path
|
|
|
- val pathType = pathType
|
|
|
- if (path != null && pathType != null) {
|
|
|
- when (pathType) {
|
|
|
- PathType.ASSERT -> setAsset(path, EffectAnimType.MP4, extraConfig, listener)
|
|
|
- PathType.FILE -> setFile(path, EffectAnimType.MP4, extraConfig, listener)
|
|
|
- PathType.URL -> setUrl(path, EffectAnimType.MP4, extraConfig, listener)
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- fun pausePlay() {
|
|
|
- svgaAnimView?.pauseAnimation()
|
|
|
- tcAnimView?.pause()
|
|
|
- //vap不支持暂停,直接停止
|
|
|
- vapAnimView?.stopPlay()
|
|
|
- }
|
|
|
-
|
|
|
- fun stopPlay() {
|
|
|
- this.path = null
|
|
|
- this.pathType = null
|
|
|
- this.animType = null
|
|
|
- this.extraConfig = null
|
|
|
- this.listener= null
|
|
|
-
|
|
|
- svgaAnimView?.apply {
|
|
|
- stopAnimation(true)
|
|
|
- setImageDrawable(null)
|
|
|
- }
|
|
|
- cancelDownloadJob()
|
|
|
- tcAnimView?.stopPlay(true)
|
|
|
- vapAnimView?.stopPlay()
|
|
|
- }
|
|
|
-
|
|
|
- private fun playSVGA(
|
|
|
- path: String,
|
|
|
- pathType: PathType,
|
|
|
- extraConfig: AnimExtraConfig?,
|
|
|
- listener: IWeAnimPlayListener?
|
|
|
- ) {
|
|
|
- inflateAnimView(AnimViewType.SVGA)
|
|
|
- show()
|
|
|
- autoPlay = extraConfig?.autoPlay ?: autoPlay
|
|
|
- svgaAnimView?.apply {
|
|
|
- setRatio(extraConfig?.ratio ?: 1f)
|
|
|
- val loopCnt = extraConfig?.loop ?: 0
|
|
|
- //svga循环次数要特殊处理 <0代表无限循环,大于等于0需要+1
|
|
|
- loops = if (loopCnt >= 0) loopCnt + 1 else loopCnt
|
|
|
- extraConfig?.svgaExtraConfig?.let {
|
|
|
- clearsAfterDetached = it.clearsAfterDetached
|
|
|
- fillMode = it.fillMode
|
|
|
- }
|
|
|
- setAutoPlay(autoPlay)
|
|
|
- extraConfig?.scaleType?.let {
|
|
|
- scaleType = it
|
|
|
- }
|
|
|
- }
|
|
|
- val dynamicEntitySupplier = extraConfig?.svgaExtraConfig?.dynamicEntitySupplier
|
|
|
- var controllerListener: ControllerListener? = null
|
|
|
- if (listener != null) {
|
|
|
- controllerListener = object : ControllerListener {
|
|
|
- override fun onBeforeImageSet(id: String?, svgaInfo: SvgaInfo?) {
|
|
|
- super.onBeforeImageSet(id, svgaInfo)
|
|
|
- listener.onGetSvgaInfo(svgaInfo)
|
|
|
- listener.onPlayStart()
|
|
|
- }
|
|
|
-
|
|
|
- override fun onFinalImageSet(id: String?, svgaInfo: SvgaInfo?) {
|
|
|
- super.onFinalImageSet(id, svgaInfo)
|
|
|
- listener.onPlayEnd()
|
|
|
- }
|
|
|
-
|
|
|
- override fun onFailure(id: String?, throwable: Throwable?) {
|
|
|
- super.onFailure(id, throwable)
|
|
|
- listener.onPlayError(id ?: "")
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- svgaAnimView?.volume = if (extraConfig?.mute == true) 0f else 1f
|
|
|
- when (pathType) {
|
|
|
- PathType.FILE -> {
|
|
|
- svgaAnimView?.setFile(File(path), dynamicEntitySupplier, controllerListener)
|
|
|
- }
|
|
|
-
|
|
|
- PathType.ASSERT -> {
|
|
|
- svgaAnimView?.setAsset(path, dynamicEntitySupplier, controllerListener)
|
|
|
- }
|
|
|
-
|
|
|
- PathType.URL -> {
|
|
|
- svgaAnimView?.setUrl(path, dynamicEntitySupplier, controllerListener)
|
|
|
- }
|
|
|
-
|
|
|
- else -> {
|
|
|
- //Ntd.
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private fun playMp4File(
|
|
|
- filePath: String,
|
|
|
- extraConfig: AnimExtraConfig?,
|
|
|
- listener: IWeAnimPlayListener?
|
|
|
- ) {
|
|
|
- inflateAnimView(AnimViewType.TC)
|
|
|
- show()
|
|
|
- setRatio(extraConfig?.ratio ?: 1f)
|
|
|
- tcAnimView?.setPlayListener(object : TCEffectAnimView.IAnimPlayListener {
|
|
|
- override fun onPlayStart() {
|
|
|
- runOnUiThread {
|
|
|
- listener?.onPlayStart()
|
|
|
- listener?.onGetTcInfo(tcAnimView?.tcAnimInfo)
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- override fun onPlayEnd() {
|
|
|
- runOnUiThread {
|
|
|
- listener?.onPlayEnd()
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- override fun onPlayError(p0: Int) {
|
|
|
- runOnUiThread {
|
|
|
- listener?.onPlayError(p0.toString())
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- override fun onPlayEvent(p0: Int, p1: Bundle?) {
|
|
|
-
|
|
|
- }
|
|
|
- })
|
|
|
- extraConfig?.tcExtraConfig?.tcFetchResource?.let {
|
|
|
- tcAnimView?.setFetchResource(it)
|
|
|
- }
|
|
|
- val loopCnt = extraConfig?.loop ?: 0
|
|
|
- //tc循环次数要特殊处理 <=0代表无限循环
|
|
|
- val loops = when {
|
|
|
- loopCnt < 0 -> {
|
|
|
- INFINITE_LOOP
|
|
|
- }
|
|
|
- loopCnt == 0 -> {
|
|
|
- 1
|
|
|
- }
|
|
|
- else -> {
|
|
|
- loopCnt
|
|
|
- }
|
|
|
- }
|
|
|
- tcAnimView?.setScaleType(
|
|
|
- getTCScaleTypeBy(
|
|
|
- extraConfig?.scaleType ?: ImageView.ScaleType.FIT_CENTER
|
|
|
- )
|
|
|
- )
|
|
|
- tcAnimView?.setLoopCount(loops)
|
|
|
- tcAnimView?.setMute(extraConfig?.mute == true)
|
|
|
- tcAnimView?.startPlay(filePath)
|
|
|
- autoPlay = extraConfig?.autoPlay ?: autoPlay
|
|
|
- if (!autoPlay) {
|
|
|
- tcAnimView?.post {
|
|
|
- tcAnimView?.pause()
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private fun playVapMp4File(
|
|
|
- filePath: String,
|
|
|
- extraConfig: AnimExtraConfig?,
|
|
|
- listener: IWeAnimPlayListener?
|
|
|
- ) {
|
|
|
- inflateAnimView(AnimViewType.VAP)
|
|
|
- show()
|
|
|
- setRatio(extraConfig?.ratio ?: 1f)
|
|
|
- vapAnimView?.setAnimListener(object : IAnimListener {
|
|
|
- override fun onVideoConfigReady(config: AnimConfig): Boolean {
|
|
|
- runOnUiThread{
|
|
|
- listener?.onGetVapInfo(config)
|
|
|
- }
|
|
|
- return super.onVideoConfigReady(config)
|
|
|
- }
|
|
|
- override fun onFailed(errorType: Int, errorMsg: String?) {
|
|
|
- runOnUiThread{
|
|
|
- listener?.onPlayError("errorType:$errorType, errorMsg:$errorMsg")
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- override fun onVideoComplete() {
|
|
|
- runOnUiThread{
|
|
|
- listener?.onPlayEnd()
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- override fun onVideoDestroy() {}
|
|
|
-
|
|
|
- override fun onVideoRender(frameIndex: Int, config: AnimConfig?) {}
|
|
|
-
|
|
|
- override fun onVideoStart() {
|
|
|
- runOnUiThread{
|
|
|
- listener?.onPlayStart()
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- })
|
|
|
- extraConfig?.vapExtraConfig?.vapFetchResource?.let {
|
|
|
- vapAnimView?.setFetchResource(it)
|
|
|
- }
|
|
|
- val loopCnt = extraConfig?.loop ?: 0
|
|
|
- //vap循环次数要特殊处理 <=0代表无限循环
|
|
|
- val loops = when {
|
|
|
- loopCnt < 0 -> {
|
|
|
- Int.MAX_VALUE
|
|
|
- }
|
|
|
-
|
|
|
- loopCnt == 0 -> {
|
|
|
- 1
|
|
|
- }
|
|
|
-
|
|
|
- else -> {
|
|
|
- loopCnt
|
|
|
- }
|
|
|
- }
|
|
|
- ScaleType.getScaleTypeBy(extraConfig?.scaleType ?: ImageView.ScaleType.FIT_CENTER)?.apply {
|
|
|
- vapAnimView?.setScaleType(this)
|
|
|
- }
|
|
|
- vapAnimView?.setLoop(loops)
|
|
|
- vapAnimView?.setMute(extraConfig?.mute == true)
|
|
|
- autoPlay = extraConfig?.autoPlay ?: autoPlay
|
|
|
- if (autoPlay) {
|
|
|
- vapAnimView?.startPlay(File(filePath))
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private fun getTCScaleTypeBy(type: ImageView.ScaleType): TCEffectPlayerConstant.ScaleType? {
|
|
|
- return when (type) {
|
|
|
- ImageView.ScaleType.MATRIX -> null
|
|
|
- ImageView.ScaleType.FIT_XY -> TCEffectPlayerConstant.ScaleType.FIT_XY
|
|
|
- ImageView.ScaleType.FIT_START -> null
|
|
|
- ImageView.ScaleType.FIT_CENTER -> TCEffectPlayerConstant.ScaleType.FIT_CENTER
|
|
|
- ImageView.ScaleType.FIT_END -> null
|
|
|
- ImageView.ScaleType.CENTER -> null
|
|
|
- ImageView.ScaleType.CENTER_CROP -> TCEffectPlayerConstant.ScaleType.CENTER_CROP
|
|
|
- ImageView.ScaleType.CENTER_INSIDE -> null
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- fun setRatio(ratio: Float) {
|
|
|
- if (width == 0 || height == 0) {
|
|
|
- // 视图尚未布局完成,添加布局监听器
|
|
|
- viewTreeObserver.addOnGlobalLayoutListener(object :
|
|
|
- ViewTreeObserver.OnGlobalLayoutListener {
|
|
|
- override fun onGlobalLayout() {
|
|
|
- val actualWidth = width
|
|
|
- val actualHeight = height
|
|
|
-
|
|
|
- // 可能前面的回调还没有完成布局,拿不到宽高,这时无需处理,也不要removeOnGlobalLayoutListener,否则会得不到实际宽高
|
|
|
- if (actualWidth == 0 && actualHeight == 0) {
|
|
|
- return
|
|
|
- }
|
|
|
-
|
|
|
- viewTreeObserver.removeOnGlobalLayoutListener(this)
|
|
|
- updateViewSize(actualWidth, actualHeight, ratio)
|
|
|
- }
|
|
|
- })
|
|
|
- return
|
|
|
- }
|
|
|
- // 视图已布局完成,直接获取实际宽高
|
|
|
- updateViewSize(width, height, ratio)
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 设置视图的缩放大小
|
|
|
- */
|
|
|
- private fun updateViewSize(width: Int, height: Int, ratio: Float) {
|
|
|
- if (originWidth == 0) {
|
|
|
- originWidth = width
|
|
|
- }
|
|
|
- if (originHeight == 0) {
|
|
|
- originHeight = height
|
|
|
- }
|
|
|
-
|
|
|
- val lp = layoutParams
|
|
|
- lp.width = (originWidth * ratio).toInt()
|
|
|
- lp.height = (originHeight * ratio).toInt()
|
|
|
- layoutParams = lp
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 预加载资源
|
|
|
- */
|
|
|
- fun preloadAssetFile(assetName: String): String {
|
|
|
- return copyAssetToCache(assetName)
|
|
|
- }
|
|
|
-
|
|
|
- private fun copyAssetToCache(assetName: String): String {
|
|
|
- createOrExistsDir(FilePath.effectPath)
|
|
|
- val cacheFile = File(FilePath.effectPath, assetName)
|
|
|
- if (!cacheFile.exists()) {
|
|
|
- AppUtil.appContext.assets.open(assetName).use { inputStream ->
|
|
|
- FileOutputStream(cacheFile).use { outputStream ->
|
|
|
- inputStream.copyTo(outputStream)
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- return cacheFile.absolutePath
|
|
|
- }
|
|
|
-
|
|
|
- private fun getEffectAnimTypeBySuffix(name: String): EffectAnimType? {
|
|
|
- return when {
|
|
|
- isSVGAImage(name) -> EffectAnimType.SVGA
|
|
|
- isMP4(name) && !TCEffectManager.isTcEffectEnable() -> EffectAnimType.MP4
|
|
|
- (isMP4(name) && TCEffectManager.isTcEffectEnable()) || isTCMP4(name) -> EffectAnimType.TC
|
|
|
- else -> null
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private fun inflateAnimView(type: AnimViewType) {
|
|
|
- when (type) {
|
|
|
- AnimViewType.SVGA -> {
|
|
|
- if (svgaAnimView?.parent == null) {
|
|
|
- svgaAnimView = WenextSvgaView(context)
|
|
|
- addView(svgaAnimView, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
|
|
|
- svgaAnimView?.show()
|
|
|
- }
|
|
|
- removeAnimView(AnimViewType.TC)
|
|
|
- removeAnimView(AnimViewType.VAP)
|
|
|
- }
|
|
|
- AnimViewType.TC -> {
|
|
|
- if (tcAnimView?.parent == null) {
|
|
|
- tcAnimView = TCEffectAnimView(context)
|
|
|
- val tceConfigExtendMap: HashMap<String, Any> = hashMapOf(
|
|
|
- TCEffectPlayerConstant.PARAM_OPTIONAL_STRING_EXTRA_LICENSE_KEY to TCEffectManager.getOldLicense()
|
|
|
- )
|
|
|
- tcAnimView?.setConfig(
|
|
|
- TCEffectConfig.Builder().setExtendMapParams(tceConfigExtendMap).build()
|
|
|
- )
|
|
|
- addView(tcAnimView, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
|
|
|
- tcAnimView?.show()
|
|
|
- }
|
|
|
- removeAnimView(AnimViewType.SVGA)
|
|
|
- removeAnimView(AnimViewType.VAP)
|
|
|
- }
|
|
|
- AnimViewType.VAP -> {
|
|
|
- if (vapAnimView?.parent == null) {
|
|
|
- vapAnimView = AnimView(context)
|
|
|
- addView(vapAnimView, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
|
|
|
- vapAnimView?.show()
|
|
|
- }
|
|
|
- removeAnimView(AnimViewType.SVGA)
|
|
|
- removeAnimView(AnimViewType.TC)
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private fun removeAnimView(type: AnimViewType) {
|
|
|
- when (type) {
|
|
|
- AnimViewType.SVGA -> {
|
|
|
- svgaAnimView?.let {
|
|
|
- it.stopAnimation(true)
|
|
|
- removeView(it)
|
|
|
- }
|
|
|
- }
|
|
|
- AnimViewType.TC -> {
|
|
|
- tcAnimView?.let {
|
|
|
- it.stopPlay(true)
|
|
|
- removeView(it)
|
|
|
- }
|
|
|
- }
|
|
|
- AnimViewType.VAP -> {
|
|
|
- vapAnimView?.let {
|
|
|
- it.stopPlay()
|
|
|
- removeView(it)
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- fun cancelDownloadJob() {
|
|
|
- downloadJob?.cancel()
|
|
|
- downloadJob = null
|
|
|
- }
|
|
|
-
|
|
|
- override fun onAttachedToWindow() {
|
|
|
- super.onAttachedToWindow()
|
|
|
- Log.d(TAG, "hashCode:${hashCode()}, onAttachedToWindow")
|
|
|
- if (tcAnimView?.isVisible == true || vapAnimView?.isVisible == true) {
|
|
|
- val path = path
|
|
|
- val pathType = pathType
|
|
|
- if (path != null && pathType != null) {
|
|
|
- Log.d(TAG, "hashCode:${hashCode()}, onAttachedToWindow, path:${path}, pathType:${pathType}, animType:${animType}")
|
|
|
- when (pathType) {
|
|
|
- PathType.ASSERT -> setAsset(path, animType, extraConfig, listener)
|
|
|
- PathType.FILE -> setFile(path, animType, extraConfig, listener)
|
|
|
- PathType.URL -> setUrl(path, animType, extraConfig, listener)
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- override fun onDetachedFromWindow() {
|
|
|
- super.onDetachedFromWindow()
|
|
|
- Log.d(TAG, "hashCode:${hashCode()}, onDetachedFromWindow")
|
|
|
- cancelDownloadJob()
|
|
|
- }
|
|
|
-
|
|
|
- fun setImageMatrix(matrix: Matrix) {
|
|
|
- svgaAnimView?.imageMatrix = matrix
|
|
|
- }
|
|
|
-
|
|
|
- fun isResourceReady(): Boolean {
|
|
|
- return path.isNullOrEmpty().not()
|
|
|
- }
|
|
|
-
|
|
|
- interface IWeAnimPlayListener {
|
|
|
- fun onGetTcInfo(tcAnimInfo: TCEffectAnimInfo?) {}
|
|
|
- fun onGetVapInfo(vapInfo: AnimConfig) {}
|
|
|
- fun onGetSvgaInfo(svgaInfo: SvgaInfo?) {}
|
|
|
- fun onPlayStart() {}
|
|
|
- fun onPlayEnd() {}
|
|
|
- fun onPlayError(errorCode: String) {}
|
|
|
- }
|
|
|
-
|
|
|
- companion object {
|
|
|
- private const val TAG = "WeAnimView"
|
|
|
- }
|
|
|
-
|
|
|
- enum class AnimViewType {
|
|
|
- SVGA,
|
|
|
- VAP,
|
|
|
- TC
|
|
|
- }
|
|
|
-}
|