545 lines
21 KiB
Kotlin
545 lines
21 KiB
Kotlin
|
|
package com.pukka.mediaapp.ui
|
|||
|
|
|
|||
|
|
import android.app.AlertDialog
|
|||
|
|
import android.content.Intent
|
|||
|
|
import android.os.Bundle
|
|||
|
|
import android.os.CountDownTimer
|
|||
|
|
import android.util.Log
|
|||
|
|
import android.view.LayoutInflater
|
|||
|
|
import android.view.View
|
|||
|
|
import android.widget.Toast
|
|||
|
|
import androidx.activity.viewModels
|
|||
|
|
import androidx.appcompat.app.AppCompatActivity
|
|||
|
|
import androidx.lifecycle.Lifecycle
|
|||
|
|
import androidx.lifecycle.lifecycleScope
|
|||
|
|
import androidx.lifecycle.repeatOnLifecycle
|
|||
|
|
import com.bumptech.glide.Glide
|
|||
|
|
import com.pukka.mediaapp.databinding.ActivityDetailBinding
|
|||
|
|
import com.pukka.mediaapp.databinding.DialogPaymentBinding
|
|||
|
|
import com.pukka.mediaapp.model.ContentItem
|
|||
|
|
import com.pukka.mediaapp.network.SignManager
|
|||
|
|
import com.pukka.mediaapp.sdk.JetsenxSdkUtil
|
|||
|
|
import com.pukka.mediaapp.ui.adapter.AppColors
|
|||
|
|
import com.pukka.mediaapp.utils.QRCodeUtil
|
|||
|
|
import com.pukka.mediaapp.utils.FileLogger
|
|||
|
|
import com.pukka.mediaapp.viewmodel.DetailViewModel
|
|||
|
|
import com.pukka.mediaapp.viewmodel.PaymentState
|
|||
|
|
import com.pukka.mediaapp.viewmodel.PaymentViewModel
|
|||
|
|
import kotlinx.coroutines.Dispatchers
|
|||
|
|
import kotlinx.coroutines.launch
|
|||
|
|
import kotlinx.coroutines.withContext
|
|||
|
|
import java.text.SimpleDateFormat
|
|||
|
|
import java.util.Locale
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 详情页:海报、标题、评分、演员、简介 + 播放(含微信扫码支付流程)+ 返回。
|
|||
|
|
*
|
|||
|
|
* 完整播放流程(改造后):
|
|||
|
|
* 1. 点击播放 → 采集设备标识 → 检查天卡播放权限
|
|||
|
|
* 2. 有权限 → 调用SDK获取视频地址 → 跳转播放器
|
|||
|
|
* 3. 无权限 → 创建订单 → 展示微信支付二维码 → 轮询支付状态
|
|||
|
|
* 4. 支付成功 → 获取权益 → 初始化SDK → 获取视频地址 → 跳转播放器
|
|||
|
|
* 5. 超时/关闭 → 展示重新生成按钮
|
|||
|
|
*/
|
|||
|
|
class DetailActivity : AppCompatActivity() {
|
|||
|
|
|
|||
|
|
private lateinit var binding: ActivityDetailBinding
|
|||
|
|
private val viewModel: DetailViewModel by viewModels()
|
|||
|
|
private val paymentViewModel: PaymentViewModel by viewModels()
|
|||
|
|
|
|||
|
|
private var paymentDialog: AlertDialog? = null
|
|||
|
|
private var dialogBinding: DialogPaymentBinding? = null
|
|||
|
|
|
|||
|
|
private var contentItem: ContentItem? = null
|
|||
|
|
private var sdkInitialized = false
|
|||
|
|
private var isFetchingVideoUrl = false // 防止重复跳转播放器
|
|||
|
|
|
|||
|
|
private var currentMediaId: String = ""
|
|||
|
|
private var countDownTimer: CountDownTimer? = null
|
|||
|
|
|
|||
|
|
override fun onCreate(savedInstanceState: Bundle?) {
|
|||
|
|
super.onCreate(savedInstanceState)
|
|||
|
|
binding = ActivityDetailBinding.inflate(layoutInflater)
|
|||
|
|
setContentView(binding.root)
|
|||
|
|
|
|||
|
|
val contentcode = intent.getStringExtra(EXTRA_CONTENTCODE).orEmpty()
|
|||
|
|
val name = intent.getStringExtra(EXTRA_NAME).orEmpty()
|
|||
|
|
currentMediaId = contentcode
|
|||
|
|
|
|||
|
|
binding.tvTitle.text = name
|
|||
|
|
applyTextColors()
|
|||
|
|
|
|||
|
|
binding.btnBack.setOnClickListener { finish() }
|
|||
|
|
binding.btnPlay.setOnClickListener {
|
|||
|
|
Log.d("DetailActivity", "btnPlay clicked! contentcode=$contentcode")
|
|||
|
|
isFetchingVideoUrl = false // 重置标志
|
|||
|
|
startPaymentFlow()
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
observeViewModel()
|
|||
|
|
observePaymentState()
|
|||
|
|
viewModel.loadDetail(contentcode)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/** 开始鉴权/支付流程(sign 由 SignManager 管理,无需 App 传设备标识) */
|
|||
|
|
private fun startPaymentFlow() {
|
|||
|
|
// 取消之前的倒计时
|
|||
|
|
countDownTimer?.cancel()
|
|||
|
|
countDownTimer = null
|
|||
|
|
|
|||
|
|
paymentViewModel.startPaymentFlow()
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private fun applyTextColors() {
|
|||
|
|
binding.tvTitle.setTextColor(AppColors.TEXT_PRIMARY)
|
|||
|
|
binding.tvActor.setTextColor(AppColors.TEXT_SECONDARY)
|
|||
|
|
binding.tvIntro.setTextColor(AppColors.TEXT_TERTIARY)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/** 时长格式化:秒 -> "104分钟" 或 "1小时44分钟" */
|
|||
|
|
private fun formatDuration(duration: String?): String? {
|
|||
|
|
if (duration.isNullOrEmpty()) return null
|
|||
|
|
val seconds = duration.toLongOrNull() ?: return null
|
|||
|
|
if (seconds <= 0) return null
|
|||
|
|
val totalMinutes = seconds / 60
|
|||
|
|
if (totalMinutes < 60) return "${totalMinutes}分钟"
|
|||
|
|
val hours = totalMinutes / 60
|
|||
|
|
val mins = totalMinutes % 60
|
|||
|
|
return if (mins > 0) "${hours}小时${mins}分钟" else "${hours}小时"
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private fun observeViewModel() {
|
|||
|
|
lifecycleScope.launch {
|
|||
|
|
repeatOnLifecycle(Lifecycle.State.STARTED) {
|
|||
|
|
viewModel.detail.collect { item ->
|
|||
|
|
if (item == null) return@collect
|
|||
|
|
contentItem = item
|
|||
|
|
binding.tvTitle.text = item.name
|
|||
|
|
|
|||
|
|
// 元信息行:分类 · 地区 · 年份 · 时长
|
|||
|
|
val metaParts = mutableListOf<String>()
|
|||
|
|
item.firstproject?.takeIf { it.isNotEmpty() }?.let { metaParts.add(it) }
|
|||
|
|
item.secondproject?.takeIf { it.isNotEmpty() }?.let { metaParts.add(it) }
|
|||
|
|
item.originalcountry?.takeIf { it.isNotEmpty() }?.let { metaParts.add(it) }
|
|||
|
|
item.releaseyear?.takeIf { it.isNotEmpty() }?.let { metaParts.add(it) }
|
|||
|
|
formatDuration(item.duration)?.let { metaParts.add(it) }
|
|||
|
|
binding.tvType.text = metaParts.joinToString(" · ")
|
|||
|
|
|
|||
|
|
// 演员:| 分割取前4个
|
|||
|
|
val actors = item.actordisplay?.split("|")?.map { it.trim() }?.filter { it.isNotEmpty() }
|
|||
|
|
binding.tvActor.text = if (!actors.isNullOrEmpty()) "主演: ${actors.take(4).joinToString(" ")}" else ""
|
|||
|
|
|
|||
|
|
binding.tvIntro.text = item.getIntroText()
|
|||
|
|
|
|||
|
|
// 横幅背景图
|
|||
|
|
val backdrop = item.thumbimg?.takeIf { it.isNotEmpty() }
|
|||
|
|
?: item.midimg?.takeIf { it.isNotEmpty() }
|
|||
|
|
?: item.image?.takeIf { it.isNotEmpty() }
|
|||
|
|
if (!backdrop.isNullOrEmpty()) {
|
|||
|
|
Glide.with(this@DetailActivity).load(backdrop).into(binding.ivBackdrop)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 海报
|
|||
|
|
val poster = item.posterimg?.takeIf { it.isNotEmpty() }
|
|||
|
|
?: item.getPosterUrl()
|
|||
|
|
Glide.with(this@DetailActivity).load(poster).into(binding.ivPoster)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private fun observePaymentState() {
|
|||
|
|
lifecycleScope.launch {
|
|||
|
|
repeatOnLifecycle(Lifecycle.State.STARTED) {
|
|||
|
|
paymentViewModel.state.collect { state ->
|
|||
|
|
FileLogger.log("DetailActivity", "collect state=${state::class.simpleName}")
|
|||
|
|
when (state) {
|
|||
|
|
is PaymentState.Idle -> {}
|
|||
|
|
is PaymentState.CheckingPermission -> {
|
|||
|
|
// 静默鉴权:不弹窗、不提示,直接走后续播放
|
|||
|
|
}
|
|||
|
|
is PaymentState.HasPermission -> {
|
|||
|
|
if (isFetchingVideoUrl) return@collect
|
|||
|
|
isFetchingVideoUrl = true
|
|||
|
|
dismissPaymentDialog()
|
|||
|
|
playWithThirdCardNo(state.thirdCardNo)
|
|||
|
|
}
|
|||
|
|
is PaymentState.CreatingOrder -> {
|
|||
|
|
showPaymentDialog("正在创建天卡订单...")
|
|||
|
|
}
|
|||
|
|
is PaymentState.WaitingForPayment -> {
|
|||
|
|
// 展示二维码
|
|||
|
|
showQrCode(
|
|||
|
|
codeUrl = state.codeUrl,
|
|||
|
|
amount = state.amount,
|
|||
|
|
orderNo = state.orderNo,
|
|||
|
|
expireAt = state.expireAt
|
|||
|
|
)
|
|||
|
|
}
|
|||
|
|
is PaymentState.PaymentSuccess -> {
|
|||
|
|
if (isFetchingVideoUrl) return@collect
|
|||
|
|
isFetchingVideoUrl = true
|
|||
|
|
countDownTimer?.cancel()
|
|||
|
|
dismissPaymentDialog()
|
|||
|
|
playWithThirdCardNo(state.thirdCardNo)
|
|||
|
|
}
|
|||
|
|
is PaymentState.PaymentTimeout -> {
|
|||
|
|
countDownTimer?.cancel()
|
|||
|
|
showPaymentTimeout()
|
|||
|
|
}
|
|||
|
|
is PaymentState.Error -> {
|
|||
|
|
countDownTimer?.cancel()
|
|||
|
|
isFetchingVideoUrl = false
|
|||
|
|
FileLogger.log("DetailActivity", "Error: msg=${state.message} dialogShowing=${paymentDialog?.isShowing}")
|
|||
|
|
if (paymentDialog?.isShowing != true) showPaymentDialog("")
|
|||
|
|
updatePaymentDialog(
|
|||
|
|
errorText = "失败: ${state.message}",
|
|||
|
|
showCloseButton = true
|
|||
|
|
)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 展示微信支付二维码
|
|||
|
|
*/
|
|||
|
|
private fun showQrCode(codeUrl: String, amount: Int, orderNo: String, expireAt: String) {
|
|||
|
|
val db = dialogBinding ?: showPaymentDialog("").let { dialogBinding } ?: return
|
|||
|
|
|
|||
|
|
// 隐藏加载状态
|
|||
|
|
db.progressBar.visibility = View.GONE
|
|||
|
|
db.pbQrcodeLoading.visibility = View.VISIBLE
|
|||
|
|
db.flQrcode.visibility = View.VISIBLE
|
|||
|
|
db.ivQrcode.visibility = View.GONE
|
|||
|
|
|
|||
|
|
// 金额显示(分转元)
|
|||
|
|
db.tvPayAmount.visibility = View.VISIBLE
|
|||
|
|
db.tvPayAmount.text = "¥${"%.2f".format(amount / 100.0)}"
|
|||
|
|
|
|||
|
|
// 订单号
|
|||
|
|
db.tvPayOrderNo.visibility = View.VISIBLE
|
|||
|
|
db.tvPayOrderNo.text = "订单号: $orderNo"
|
|||
|
|
|
|||
|
|
// 状态文本
|
|||
|
|
db.tvPayStatus.text = "请使用微信扫码支付"
|
|||
|
|
|
|||
|
|
// 按钮:显示取消(用户可随时取消支付)
|
|||
|
|
db.llButtons.visibility = View.GONE
|
|||
|
|
db.btnClose.visibility = View.VISIBLE
|
|||
|
|
db.btnClose.text = "取消支付"
|
|||
|
|
db.btnRegenerate.visibility = View.GONE
|
|||
|
|
|
|||
|
|
// 倒计时
|
|||
|
|
db.tvCountdown.visibility = View.VISIBLE
|
|||
|
|
startCountdown(expireAt)
|
|||
|
|
|
|||
|
|
// 异步生成二维码
|
|||
|
|
lifecycleScope.launch(Dispatchers.IO) {
|
|||
|
|
try {
|
|||
|
|
val bitmap = QRCodeUtil.generateQRCode(codeUrl, 400)
|
|||
|
|
withContext(Dispatchers.Main) {
|
|||
|
|
val currentBinding = dialogBinding ?: return@withContext
|
|||
|
|
currentBinding.pbQrcodeLoading.visibility = View.GONE
|
|||
|
|
currentBinding.ivQrcode.setImageBitmap(bitmap)
|
|||
|
|
currentBinding.ivQrcode.visibility = View.VISIBLE
|
|||
|
|
}
|
|||
|
|
} catch (e: Exception) {
|
|||
|
|
Log.e("DetailActivity", "QR code generation failed", e)
|
|||
|
|
withContext(Dispatchers.Main) {
|
|||
|
|
updatePaymentDialog(
|
|||
|
|
errorText = "二维码生成失败,请重试",
|
|||
|
|
showCloseButton = true
|
|||
|
|
)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 启动倒计时
|
|||
|
|
*/
|
|||
|
|
private fun startCountdown(expireAt: String) {
|
|||
|
|
countDownTimer?.cancel()
|
|||
|
|
|
|||
|
|
val remainMillis = calculateRemainMillis(expireAt)
|
|||
|
|
if (remainMillis <= 0) {
|
|||
|
|
paymentViewModel.cancelPolling()
|
|||
|
|
showPaymentTimeout()
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
countDownTimer = object : CountDownTimer(remainMillis, 1000) {
|
|||
|
|
override fun onTick(millisUntilFinished: Long) {
|
|||
|
|
val db = dialogBinding ?: return
|
|||
|
|
val minutes = millisUntilFinished / 60000
|
|||
|
|
val seconds = (millisUntilFinished % 60000) / 1000
|
|||
|
|
db.tvCountdown.text = String.format("%02d:%02d", minutes, seconds)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
override fun onFinish() {
|
|||
|
|
val db = dialogBinding ?: return
|
|||
|
|
db.tvCountdown.text = "00:00"
|
|||
|
|
paymentViewModel.cancelPolling()
|
|||
|
|
showPaymentTimeout()
|
|||
|
|
}
|
|||
|
|
}.start()
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 解析 expireAt 计算剩余时间(毫秒)
|
|||
|
|
*/
|
|||
|
|
private fun calculateRemainMillis(expireAt: String): Long {
|
|||
|
|
if (expireAt.isEmpty()) {
|
|||
|
|
// 默认 15 分钟
|
|||
|
|
return 15 * 60 * 1000L
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 尝试多种格式解析
|
|||
|
|
val formats = listOf(
|
|||
|
|
"yyyy-MM-dd'T'HH:mm:ssXXX",
|
|||
|
|
"yyyy-MM-dd'T'HH:mm:ss.SSSXXX",
|
|||
|
|
"yyyy-MM-dd'T'HH:mm:ssZ",
|
|||
|
|
"yyyy-MM-dd'T'HH:mm:ss"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
for (pattern in formats) {
|
|||
|
|
try {
|
|||
|
|
val sdf = SimpleDateFormat(pattern, Locale.getDefault())
|
|||
|
|
val expireTime = sdf.parse(expireAt)
|
|||
|
|
if (expireTime != null) {
|
|||
|
|
val remain = expireTime.time - System.currentTimeMillis()
|
|||
|
|
return if (remain > 0) remain else 0
|
|||
|
|
}
|
|||
|
|
} catch (_: Exception) {
|
|||
|
|
// 尝试下一种格式
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 解析失败,默认 15 分钟
|
|||
|
|
Log.w("DetailActivity", "Failed to parse expireAt: $expireAt, using default 15min")
|
|||
|
|
return 15 * 60 * 1000L
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 显示支付超时
|
|||
|
|
*/
|
|||
|
|
private fun showPaymentTimeout() {
|
|||
|
|
val db = dialogBinding ?: return
|
|||
|
|
db.ivQrcode.visibility = View.GONE
|
|||
|
|
db.pbQrcodeLoading.visibility = View.GONE
|
|||
|
|
db.progressBar.visibility = View.GONE
|
|||
|
|
db.tvCountdown.visibility = View.GONE
|
|||
|
|
db.tvPayAmount.visibility = View.GONE
|
|||
|
|
db.llButtons.visibility = View.GONE
|
|||
|
|
db.tvPayStatus.text = "支付超时,请重新生成"
|
|||
|
|
db.btnRegenerate.visibility = View.VISIBLE
|
|||
|
|
db.btnClose.visibility = View.VISIBLE
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 用 thirdCardNo 初始化 SDK 并播放:
|
|||
|
|
* 1. 用 thirdCardNo 初始化SDK
|
|||
|
|
* 2. 调用SDK获取视频地址
|
|||
|
|
* 3. 跳转播放器
|
|||
|
|
*/
|
|||
|
|
private fun playWithThirdCardNo(thirdCardNo: String) {
|
|||
|
|
val item = contentItem
|
|||
|
|
if (item == null) {
|
|||
|
|
isFetchingVideoUrl = false
|
|||
|
|
Toast.makeText(this, "内容信息未加载", Toast.LENGTH_SHORT).show()
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (thirdCardNo.isEmpty()) {
|
|||
|
|
isFetchingVideoUrl = false
|
|||
|
|
Toast.makeText(this, "天卡卡号缺失,请重试", Toast.LENGTH_LONG).show()
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
JetsenxSdkUtil.init(this, thirdCardNo, object : JetsenxSdkUtil.InitCallback {
|
|||
|
|
override fun onSuccess(memberCode: String) {
|
|||
|
|
Log.d("DetailActivity", "SDK init success with thirdCardNo: memberCode=$memberCode")
|
|||
|
|
sdkInitialized = true
|
|||
|
|
fetchVideoUrlFromSdk(item)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
override fun onFail(message: String) {
|
|||
|
|
Log.e("DetailActivity", "SDK init fail with thirdCardNo: $message")
|
|||
|
|
runOnUiThread {
|
|||
|
|
isFetchingVideoUrl = false
|
|||
|
|
SignManager.clearBenefits()
|
|||
|
|
Toast.makeText(this@DetailActivity, "播放SDK初始化失败: $message", Toast.LENGTH_LONG).show()
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/** 调用SDK获取视频地址并跳转播放器 */
|
|||
|
|
private fun fetchVideoUrlFromSdk(item: ContentItem) {
|
|||
|
|
val parentContentType = item.getSdkParentContentType()
|
|||
|
|
val programCode = item.getVideoCode()
|
|||
|
|
|
|||
|
|
Log.d("DetailActivity", "fetchVideoUrlFromSdk: parentContentType=$parentContentType programCode=$programCode")
|
|||
|
|
|
|||
|
|
if (programCode.isEmpty()) {
|
|||
|
|
runOnUiThread {
|
|||
|
|
isFetchingVideoUrl = false
|
|||
|
|
Toast.makeText(this@DetailActivity, "无法获取节目编码", Toast.LENGTH_LONG).show()
|
|||
|
|
}
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
JetsenxSdkUtil.getAllVideoUrl(parentContentType, programCode,
|
|||
|
|
object : JetsenxSdkUtil.VideoUrlCallback {
|
|||
|
|
override fun onSuccess(videoUrl1080: String, videoUrl720: String, isFree: Boolean) {
|
|||
|
|
runOnUiThread {
|
|||
|
|
val videoUrl = if (videoUrl1080.isNotEmpty()) videoUrl1080 else videoUrl720
|
|||
|
|
Log.d("DetailActivity", "获取视频地址成功: $videoUrl (isFree=$isFree)")
|
|||
|
|
|
|||
|
|
if (videoUrl.isEmpty()) {
|
|||
|
|
isFetchingVideoUrl = false
|
|||
|
|
Toast.makeText(this@DetailActivity, "未获取到视频地址", Toast.LENGTH_LONG).show()
|
|||
|
|
return@runOnUiThread
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
dismissPaymentDialog()
|
|||
|
|
|
|||
|
|
// 跳转播放器
|
|||
|
|
val intent = Intent(this@DetailActivity, VideoPlayerActivity::class.java).apply {
|
|||
|
|
putExtra(VideoPlayerActivity.EXTRA_VIDEO_URL, videoUrl)
|
|||
|
|
putExtra(VideoPlayerActivity.EXTRA_TITLE, item.name)
|
|||
|
|
}
|
|||
|
|
startActivity(intent)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
override fun onError(code: String, message: String) {
|
|||
|
|
runOnUiThread {
|
|||
|
|
isFetchingVideoUrl = false
|
|||
|
|
Log.e("DetailActivity", "获取视频地址失败: code=$code msg=$message")
|
|||
|
|
Toast.makeText(this@DetailActivity, "获取视频地址失败: $message", Toast.LENGTH_LONG).show()
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/** 显示支付对话框 */
|
|||
|
|
private fun showPaymentDialog(statusText: String) {
|
|||
|
|
if (paymentDialog?.isShowing == true) {
|
|||
|
|
updatePaymentDialog(loadingText = statusText)
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
dialogBinding = DialogPaymentBinding.inflate(LayoutInflater.from(this))
|
|||
|
|
dialogBinding?.tvPayStatus?.text = statusText
|
|||
|
|
|
|||
|
|
// 重新生成按钮 → 重新发起支付流程
|
|||
|
|
dialogBinding?.btnRegenerate?.setOnClickListener {
|
|||
|
|
countDownTimer?.cancel()
|
|||
|
|
dialogBinding?.btnRegenerate?.visibility = View.GONE
|
|||
|
|
dialogBinding?.btnClose?.visibility = View.GONE
|
|||
|
|
isFetchingVideoUrl = false
|
|||
|
|
startPaymentFlow()
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 关闭按钮
|
|||
|
|
dialogBinding?.btnClose?.setOnClickListener {
|
|||
|
|
dismissPaymentDialog()
|
|||
|
|
paymentViewModel.reset()
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 保留兼容:确认/取消按钮(默认隐藏)
|
|||
|
|
dialogBinding?.btnCancel?.setOnClickListener {
|
|||
|
|
dismissPaymentDialog()
|
|||
|
|
paymentViewModel.reset()
|
|||
|
|
}
|
|||
|
|
dialogBinding?.btnConfirm?.visibility = View.GONE
|
|||
|
|
dialogBinding?.flQrcode?.visibility = View.GONE
|
|||
|
|
|
|||
|
|
paymentDialog = AlertDialog.Builder(this)
|
|||
|
|
.setView(dialogBinding?.root)
|
|||
|
|
.setCancelable(false)
|
|||
|
|
.create()
|
|||
|
|
paymentDialog?.window?.setBackgroundDrawableResource(android.R.color.transparent)
|
|||
|
|
paymentDialog?.show()
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 更新支付对话框状态
|
|||
|
|
* @param loadingText 加载中文本(显示进度条)
|
|||
|
|
* @param errorText 错误文本(显示关闭按钮)
|
|||
|
|
* @param showCloseButton 是否显示关闭按钮
|
|||
|
|
*/
|
|||
|
|
private fun updatePaymentDialog(
|
|||
|
|
loadingText: String? = null,
|
|||
|
|
errorText: String? = null,
|
|||
|
|
showCloseButton: Boolean = false
|
|||
|
|
) {
|
|||
|
|
val db = dialogBinding ?: return
|
|||
|
|
|
|||
|
|
val text = loadingText ?: errorText ?: ""
|
|||
|
|
db.tvPayStatus.text = text
|
|||
|
|
|
|||
|
|
if (loadingText != null) {
|
|||
|
|
// 加载状态:显示进度条 + 取消按钮(始终允许用户取消)
|
|||
|
|
db.progressBar.visibility = View.VISIBLE
|
|||
|
|
db.flQrcode.visibility = View.GONE
|
|||
|
|
db.ivQrcode.visibility = View.GONE
|
|||
|
|
db.pbQrcodeLoading.visibility = View.GONE
|
|||
|
|
db.tvPayAmount.visibility = View.GONE
|
|||
|
|
db.tvCountdown.visibility = View.GONE
|
|||
|
|
db.tvPayOrderNo.visibility = View.GONE
|
|||
|
|
db.llButtons.visibility = View.GONE
|
|||
|
|
db.btnRegenerate.visibility = View.GONE
|
|||
|
|
db.btnClose.visibility = View.VISIBLE
|
|||
|
|
db.btnClose.text = "取消"
|
|||
|
|
} else if (errorText != null || showCloseButton) {
|
|||
|
|
// 错误/完成状态:隐藏进度条,显示关闭按钮
|
|||
|
|
db.progressBar.visibility = View.GONE
|
|||
|
|
db.pbQrcodeLoading.visibility = View.GONE
|
|||
|
|
db.flQrcode.visibility = View.GONE
|
|||
|
|
db.ivQrcode.visibility = View.GONE
|
|||
|
|
db.tvCountdown.visibility = View.GONE
|
|||
|
|
db.llButtons.visibility = View.GONE
|
|||
|
|
db.btnRegenerate.visibility = View.GONE
|
|||
|
|
db.btnClose.visibility = View.VISIBLE
|
|||
|
|
db.btnClose.text = "关闭"
|
|||
|
|
db.tvPayAmount.visibility = View.GONE
|
|||
|
|
db.btnRegenerate.visibility = View.VISIBLE
|
|||
|
|
db.btnRegenerate.text = "重试"
|
|||
|
|
|
|||
|
|
// 订单号
|
|||
|
|
val orderNo = paymentViewModel.orderNo.value
|
|||
|
|
if (orderNo.isNotEmpty()) {
|
|||
|
|
db.tvPayOrderNo.visibility = View.VISIBLE
|
|||
|
|
db.tvPayOrderNo.text = "订单号: $orderNo"
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private fun dismissPaymentDialog() {
|
|||
|
|
countDownTimer?.cancel()
|
|||
|
|
countDownTimer = null
|
|||
|
|
paymentViewModel.cancelPolling()
|
|||
|
|
paymentDialog?.dismiss()
|
|||
|
|
paymentDialog = null
|
|||
|
|
dialogBinding = null
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
override fun onDestroy() {
|
|||
|
|
super.onDestroy()
|
|||
|
|
countDownTimer?.cancel()
|
|||
|
|
countDownTimer = null
|
|||
|
|
paymentViewModel.cancelPolling()
|
|||
|
|
dismissPaymentDialog()
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
companion object {
|
|||
|
|
const val EXTRA_CONTENTCODE = "extra_contentcode"
|
|||
|
|
const val EXTRA_NAME = "extra_name"
|
|||
|
|
}
|
|||
|
|
}
|