修复启动时序、重构详情页布局、对接取消订单接口

1. 启动时序修复:Launcher 从 ActivationActivity 改为 HomeActivity,
   已激活用户直接进首页,不再闪现激活页
2. 详情页布局重构:左右分栏设计(左侧海报 + 右侧信息/操作),
   全屏背景图 + 渐变遮罩,TV 端焦点导航
3. 对接后端取消订单接口 POST /api/jst/daycard/cancel/{orderNo},
   用户关闭支付弹窗时自动取消未支付订单
This commit is contained in:
freeboss
2026-08-06 13:43:29 +08:00
parent e1a088ef8e
commit 198022477a
31 changed files with 16212 additions and 147 deletions

1
.gitignore vendored
View File

@@ -17,6 +17,7 @@ captures/
# Local config(含 SDK 路径,不提交) # Local config(含 SDK 路径,不提交)
local.properties local.properties
gradle/config.properties
# OS # OS
.DS_Store .DS_Store

View File

@@ -16,7 +16,7 @@
android:usesCleartextTraffic="true"> android:usesCleartextTraffic="true">
<activity <activity
android:name=".ui.ActivationActivity" android:name=".ui.HomeActivity"
android:exported="true" android:exported="true"
android:screenOrientation="landscape" android:screenOrientation="landscape"
android:configChanges="orientation|screenSize|keyboardHidden"> android:configChanges="orientation|screenSize|keyboardHidden">
@@ -27,7 +27,7 @@
</activity> </activity>
<activity <activity
android:name=".ui.HomeActivity" android:name=".ui.ActivationActivity"
android:screenOrientation="landscape" android:screenOrientation="landscape"
android:configChanges="orientation|screenSize|keyboardHidden" /> android:configChanges="orientation|screenSize|keyboardHidden" />

View File

@@ -5,7 +5,7 @@ package com.pukka.mediaapp.config
*/ */
object DayCardConfig { object DayCardConfig {
/** 天卡 API 基础地址 */ /** 天卡 API 基础地址 */
const val BASE_URL = "http://172.16.100.128:8084/" const val BASE_URL = "http://58.220.83.58:8084/"
/** 激活码长度(管理后台生成的 5 位激活码) */ /** 激活码长度(管理后台生成的 5 位激活码) */
const val ACTIVATION_CODE_LENGTH = 5 const val ACTIVATION_CODE_LENGTH = 5
@@ -37,6 +37,8 @@ object DayCardConfig {
const val DAYCARD_CREATE = "api/jst/daycard/create" const val DAYCARD_CREATE = "api/jst/daycard/create"
/** 查询订单状态 */ /** 查询订单状态 */
const val DAYCARD_STATUS = "api/jst/daycard/status/{orderNo}" const val DAYCARD_STATUS = "api/jst/daycard/status/{orderNo}"
/** 取消订单(用户关闭支付弹窗时调用) */
const val DAYCARD_CANCEL = "api/jst/daycard/cancel/{orderNo}"
/** 心跳续期 */ /** 心跳续期 */
const val HEARTBEAT = "api/jst/heartBeat" const val HEARTBEAT = "api/jst/heartBeat"
/** 刷新 sign */ /** 刷新 sign */

View File

@@ -232,3 +232,42 @@ data class TagItem(
val epg_type: Int = 0, val epg_type: Int = 0,
val subject_type: Int = 0 val subject_type: Int = 0
) )
// ==================== 顶部功能键 (对应 GetTopButtonBean) ====================
data class TopButtonResponse(
val resultcode: String = "",
val data: List<TopButton>? = null
) {
data class TopButton(
val name: String = "",
val type: String = "",
val image: String = "",
val focusImage: String = "",
val param: String = "",
val positionid: String = "",
val url: String = ""
)
}
// ==================== 随心看/短视频 (对应 GetShortVideoBean) ====================
data class ShortVideoResponse(
val resultcode: String = "",
val result: List<ShortVideo>? = null
) {
data class ShortVideo(
val name: String = "",
val image: String = "",
val contentcode: String = "",
val playurl: String = "",
val duration: String = ""
)
}
// ==================== 搜索联想词 (对应 SuggestBean) ====================
data class SuggestResponse(
val resultcode: String = "",
val suggest: List<String> = emptyList()
)

View File

@@ -33,11 +33,13 @@ data class InitRequest(
/** init 响应(唯一不需要 sign 的接口) */ /** init 响应(唯一不需要 sign 的接口) */
data class InitResponse( data class InitResponse(
val sign: String = "",
val data: InitData? = null val data: InitData? = null
) : BaseResponse() ) : BaseResponse() {
val sign: String get() = data?.sign ?: ""
}
data class InitData( data class InitData(
val sign: String = "",
val device: DeviceInfo? = null val device: DeviceInfo? = null
) )

View File

@@ -1,5 +1,6 @@
package com.pukka.mediaapp.network package com.pukka.mediaapp.network
import com.migu.bean.GetTopButtonBean
import com.pukka.mediaapp.model.* import com.pukka.mediaapp.model.*
import retrofit2.http.Body import retrofit2.http.Body
import retrofit2.http.POST import retrofit2.http.POST
@@ -26,4 +27,8 @@ interface ApiService {
/** 栏目标签 */ /** 栏目标签 */
@POST @POST
suspend fun getTagsList(@Url url: String, @Body params: Map<String, String>): TagsListResponse suspend fun getTagsList(@Url url: String, @Body params: Map<String, String>): TagsListResponse
/** 顶部功能键(直连橘视厅,返回 libdata 的 GetTopButtonBean 供 TopView.bindData 使用) */
@POST
suspend fun getTopButton(@Url url: String, @Body params: Map<String, String>): GetTopButtonBean
} }

View File

@@ -33,4 +33,8 @@ interface DayCardApiService {
/** 6. 刷新 sign(用当前 sign 换新 sign,拦截器自动注入当前 sign)。无请求体。 */ /** 6. 刷新 sign(用当前 sign 换新 sign,拦截器自动注入当前 sign)。无请求体。 */
@POST("api/jst/refreshSign") @POST("api/jst/refreshSign")
suspend fun refreshSign(): RefreshSignResponse suspend fun refreshSign(): RefreshSignResponse
/** 7. 取消订单(用户关闭支付弹窗时调用)。sign 由拦截器自动注入。 */
@POST("api/jst/daycard/cancel/{orderNo}")
suspend fun cancelOrder(@Path("orderNo") orderNo: String): BaseResponse
} }

View File

@@ -1,5 +1,6 @@
package com.pukka.mediaapp.network package com.pukka.mediaapp.network
import com.migu.bean.GetTopButtonBean
import com.pukka.mediaapp.config.ApiConfig import com.pukka.mediaapp.config.ApiConfig
import com.pukka.mediaapp.model.* import com.pukka.mediaapp.model.*
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@@ -109,6 +110,18 @@ class MediaRepository {
} }
} }
/** 获取顶部功能键(直连橘视厅 topButton.do,返回 GetTopButtonBean 供 TopView.bindData 渲染) */
suspend fun getTopButton(): Result<GetTopButtonBean> = withContext(Dispatchers.IO) {
try {
val params = SignUtil.buildParams()
val url = ApiConfig.BASE_URL + ApiConfig.PATH_PREFIX + "/" + ApiConfig.Path.TOP_BUTTON
val response = api.getTopButton(url, params)
Result.success(response)
} catch (e: Exception) {
Result.failure(e)
}
}
companion object { companion object {
@Volatile @Volatile
private var instance: MediaRepository? = null private var instance: MediaRepository? = null

View File

@@ -26,13 +26,6 @@ class ActivationActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
// 已激活则直接进首页
if (SignManager.isActivated() && SignManager.hasSign()) {
startActivity(Intent(this, HomeActivity::class.java))
finish()
return
}
binding = ActivityActivationBinding.inflate(layoutInflater) binding = ActivityActivationBinding.inflate(layoutInflater)
setContentView(binding.root) setContentView(binding.root)
@@ -41,6 +34,23 @@ class ActivationActivity : AppCompatActivity() {
submitActivation(activateCode) submitActivation(activateCode)
} }
}) })
// 已激活则异步验证 sign 有效性(调后端 init,防止后台删除设备后 App 仍认为已激活)
if (SignManager.isActivated() && SignManager.hasSign()) {
binding.activationView.showActivateMessage("正在验证...")
lifecycleScope.launch {
try {
withContext(Dispatchers.IO) { SignManager.doInit() }
FileLogger.log("ActivationActivity", "启动验证成功,进入首页")
startActivity(Intent(this@ActivationActivity, HomeActivity::class.java))
finish()
} catch (e: Exception) {
FileLogger.error("ActivationActivity", "启动验证失败,清除缓存重新激活: ${e.message}", e)
SignManager.clear()
binding.activationView.showActivateErrorMessage("设备信息已失效,请重新激活")
}
}
}
} }
private fun submitActivation(code: String) { private fun submitActivation(code: String) {

View File

@@ -447,12 +447,14 @@ class DetailActivity : AppCompatActivity() {
// 关闭按钮 // 关闭按钮
dialogBinding?.btnClose?.setOnClickListener { dialogBinding?.btnClose?.setOnClickListener {
paymentViewModel.cancelOrder()
dismissPaymentDialog() dismissPaymentDialog()
paymentViewModel.reset() paymentViewModel.reset()
} }
// 保留兼容:确认/取消按钮(默认隐藏) // 保留兼容:确认/取消按钮(默认隐藏)
dialogBinding?.btnCancel?.setOnClickListener { dialogBinding?.btnCancel?.setOnClickListener {
paymentViewModel.cancelOrder()
dismissPaymentDialog() dismissPaymentDialog()
paymentViewModel.reset() paymentViewModel.reset()
} }
@@ -531,6 +533,10 @@ class DetailActivity : AppCompatActivity() {
override fun onDestroy() { override fun onDestroy() {
super.onDestroy() super.onDestroy()
// Activity 销毁时,若支付弹窗仍在(未支付),取消订单
if (paymentDialog?.isShowing == true) {
paymentViewModel.cancelOrder()
}
countDownTimer?.cancel() countDownTimer?.cancel()
countDownTimer = null countDownTimer = null
paymentViewModel.cancelPolling() paymentViewModel.cancelPolling()

View File

@@ -124,6 +124,7 @@ class HomeActivity : AppCompatActivity() {
repeatOnLifecycle(Lifecycle.State.STARTED) { repeatOnLifecycle(Lifecycle.State.STARTED) {
launch { viewModel.navItems.collect { navAdapter.submitList(it, viewModel.selectedNavIndex.value) } } launch { viewModel.navItems.collect { navAdapter.submitList(it, viewModel.selectedNavIndex.value) } }
launch { viewModel.sections.collect { sectionAdapter.submitList(it) } } launch { viewModel.sections.collect { sectionAdapter.submitList(it) } }
launch { viewModel.topButton.collect { it?.let { bean -> binding.topView.bindData(bean) } } }
} }
} }
} }

View File

@@ -3,6 +3,7 @@ package com.pukka.mediaapp.viewmodel
import android.util.Log import android.util.Log
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.migu.bean.GetTopButtonBean
import com.pukka.mediaapp.model.LayoutSection import com.pukka.mediaapp.model.LayoutSection
import com.pukka.mediaapp.model.NavItem import com.pukka.mediaapp.model.NavItem
import com.pukka.mediaapp.network.MediaRepository import com.pukka.mediaapp.network.MediaRepository
@@ -41,6 +42,7 @@ class HomeViewModel : ViewModel() {
fun loadNav() { fun loadNav() {
if (hasLoadedNav) return if (hasLoadedNav) return
viewModelScope.launch { viewModelScope.launch {
loadTopButton()
_loading.value = true _loading.value = true
repository.getLayoutNav() repository.getLayoutNav()
.onSuccess { response -> .onSuccess { response ->
@@ -91,6 +93,19 @@ class HomeViewModel : ViewModel() {
} }
} }
/** 顶部功能键数据(供 TopView.bindData 渲染) */
private val _topButton = MutableStateFlow<GetTopButtonBean?>(null)
val topButton: StateFlow<GetTopButtonBean?> = _topButton.asStateFlow()
/** 加载顶部功能键 */
fun loadTopButton() {
viewModelScope.launch {
repository.getTopButton()
.onSuccess { _topButton.value = it }
.onFailure { FileLogger.log("HomeVM", "顶部按钮加载失败: ${it.message}") }
}
}
/** 更新选中的导航索引 */ /** 更新选中的导航索引 */
fun selectNav(index: Int) { fun selectNav(index: Int) {
_selectedNavIndex.value = index _selectedNavIndex.value = index

View File

@@ -229,6 +229,23 @@ class PaymentViewModel : ViewModel() {
pollJob = null pollJob = null
} }
/**
* 取消未支付订单(用户关闭支付弹窗时调用)。
* fire-and-forget:不阻塞 UI,失败仅记录日志。
*/
fun cancelOrder() {
val orderNo = _orderNo.value
if (orderNo.isEmpty()) return
viewModelScope.launch {
try {
val resp = api.cancelOrder(orderNo)
FileLogger.log("PaymentVM", "cancelOrder: orderNo=$orderNo code=${resp.code} msg=${resp.msg}")
} catch (e: Exception) {
FileLogger.error("PaymentVM", "cancelOrder 失败: orderNo=$orderNo ${e.message}", e)
}
}
}
private fun cancelAllJobs() { private fun cancelAllJobs() {
pollJob?.cancel() pollJob?.cancel()
pollJob = null pollJob = null

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 返回按钮焦点态 selector -->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_focused="true">
<shape android:shape="oval">
<solid android:color="#33FFFFFF" />
<stroke android:width="2dp" android:color="#66FFFFFF" />
</shape>
</item>
<item>
<shape android:shape="oval">
<solid android:color="@android:color/transparent" />
</shape>
</item>
</selector>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 播放按钮焦点态 selector -->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_focused="true">
<shape android:shape="rectangle">
<gradient
android:startColor="#FFA940"
android:endColor="#FF8A00"
android:angle="0" />
<corners android:radius="28dp" />
</shape>
</item>
<item>
<shape android:shape="rectangle">
<gradient
android:startColor="#FF8E26"
android:endColor="#FF6A00"
android:angle="0" />
<corners android:radius="28dp" />
</shape>
</item>
</selector>

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 详情页背景渐变遮罩:左浅右深,保证右侧文字可读 -->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<gradient
android:angle="0"
android:startColor="#66000910"
android:centerColor="#99000910"
android:endColor="#F2000910" />
</shape>

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 微信支付 Logo 占位图标(临时)
请用官方素材替换:下载地址见 https://pay.weixin.qq.com/docs/merchant/products/combine-payment-identity/preparation/preparation-app.html
替换方法:将官方 Logo 命名为 ic_wechat_pay_logo.png 放入 drawable-xxhdpi/ 即可覆盖本占位图 -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="28dp"
android:height="28dp"
android:viewportWidth="28"
android:viewportHeight="28">
<!-- 绿色圆形背景 -->
<path
android:fillColor="#07C160"
android:pathData="M14,14m-14,0a14,14 0,1 0,28 0a14,14 0,1 0,-28 0" />
<!-- 白色对话气泡(占位简笔) -->
<path
android:fillColor="#FFFFFF"
android:pathData="M9.5,6.5C6.74,6.5 4.5,8.43 4.5,10.8c0,1.35 0.75,2.55 1.92,3.35L5.7,16.5l2.4,-1.2c0.45,0.12 0.92,0.2 1.4,0.2 0.12,0 0.23,-0.01 0.35,-0.02C9.7,14.9 9.5,14.4 9.5,13.9c0,-2.32 2.24,-4.2 5,-4.2 0.13,0 0.26,0.01 0.39,0.02C14.7,8.12 12.26,6.5 9.5,6.5z" />
<!-- 气泡内两个眼睛 -->
<path
android:fillColor="#07C160"
android:pathData="M8,10m-0.6,0a0.6,0.6 0,1 0,1.2 0a0.6,0.6 0,1 0,-1.2 0" />
<path
android:fillColor="#07C160"
android:pathData="M11,10m-0.6,0a0.6,0.6 0,1 0,1.2 0a0.6,0.6 0,1 0,-1.2 0" />
</vector>

View File

@@ -1,144 +1,156 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- 详情页 --> <!-- 详情页:左右分栏布局(参考生产项目),左侧海报 + 右侧信息/操作 -->
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" <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_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="@color/bg_main" android:background="@color/activity_detail_bg">
android:fillViewport="true"
android:overScrollMode="never"
android:scrollbars="none">
<LinearLayout <!-- 全屏背景图(影片横版剧照) -->
android:layout_width="match_parent" <ImageView
android:id="@+id/ivBackdrop"
android:layout_width="0dp"
android:layout_height="0dp"
android:scaleType="centerCrop"
android:contentDescription="@string/app_name"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<!-- 渐变遮罩:左浅右深,保证右侧文字可读 -->
<View
android:layout_width="0dp"
android:layout_height="0dp"
android:background="@drawable/bg_detail_scrim"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<!-- 左侧竖版海报 -->
<ImageView
android:id="@+id/ivPoster"
android:layout_width="@dimen/qb_px_280"
android:layout_height="@dimen/qb_px_400"
android:scaleType="centerCrop"
android:background="@drawable/bg_poster_rounded"
android:clipToOutline="true"
android:outlineProvider="background"
android:contentDescription="@string/app_name"
android:layout_marginStart="@dimen/qb_px_50"
android:layout_marginTop="@dimen/qb_px_50"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<!-- 标题 -->
<TextView
android:id="@+id/tvTitle"
android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical" android:layout_marginStart="@dimen/qb_px_36"
android:paddingBottom="@dimen/qb_px_40"> android:layout_marginEnd="@dimen/qb_px_50"
android:textColor="@color/text_primary"
android:textSize="@dimen/mg_sp_32"
android:textStyle="bold"
android:singleLine="true"
android:ellipsize="end"
android:includeFontPadding="false"
app:layout_constraintStart_toEndOf="@id/ivPoster"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/ivPoster" />
<!-- 顶部 16:9 横幅背景图 --> <!-- 元信息:分类 · 地区 · 年份 · 时长 -->
<ImageView <TextView
android:id="@+id/ivBackdrop" android:id="@+id/tvType"
android:layout_width="match_parent" android:layout_width="0dp"
android:layout_height="200dp" android:layout_height="wrap_content"
android:scaleType="centerCrop" android:layout_marginStart="@dimen/qb_px_36"
android:contentDescription="@string/app_name" /> android:layout_marginEnd="@dimen/qb_px_50"
android:layout_marginTop="@dimen/qb_px_16"
android:textColor="@color/text_secondary"
android:textSize="@dimen/mg_sp_20"
android:singleLine="true"
android:ellipsize="end"
android:includeFontPadding="false"
app:layout_constraintStart_toEndOf="@id/ivPoster"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/tvTitle" />
<!-- 海报 + 信息区 --> <!-- 演员 -->
<LinearLayout <TextView
android:layout_width="match_parent" android:id="@+id/tvActor"
android:layout_height="wrap_content" android:layout_width="0dp"
android:orientation="horizontal" android:layout_height="wrap_content"
android:layout_marginTop="-40dp" android:layout_marginStart="@dimen/qb_px_36"
android:paddingHorizontal="@dimen/qb_px_50"> android:layout_marginEnd="@dimen/qb_px_50"
android:layout_marginTop="@dimen/qb_px_12"
android:textColor="@color/text_secondary"
android:textSize="@dimen/mg_sp_20"
android:maxLines="2"
android:ellipsize="end"
android:includeFontPadding="false"
app:layout_constraintStart_toEndOf="@id/ivPoster"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/tvType" />
<!-- 海报 --> <!-- 简介 -->
<ImageView <TextView
android:id="@+id/ivPoster" android:id="@+id/tvIntro"
android:layout_width="130dp" android:layout_width="0dp"
android:layout_height="182dp" android:layout_height="wrap_content"
android:scaleType="centerCrop" android:layout_marginStart="@dimen/qb_px_36"
android:background="@drawable/bg_poster_rounded" android:layout_marginEnd="@dimen/qb_px_50"
android:clipToOutline="true" android:layout_marginTop="@dimen/qb_px_12"
android:outlineProvider="background" android:textColor="@color/text_tertiary"
android:contentDescription="@string/app_name" /> android:textSize="@dimen/mg_sp_20"
android:maxLines="3"
android:ellipsize="end"
android:lineSpacingExtra="@dimen/qb_px_4"
android:includeFontPadding="false"
app:layout_constraintStart_toEndOf="@id/ivPoster"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/tvActor" />
<!-- 右侧信息区 --> <!-- 播放按钮 -->
<LinearLayout <TextView
android:layout_width="0dp" android:id="@+id/btnPlay"
android:layout_height="wrap_content" android:layout_width="@dimen/qb_px_180"
android:layout_weight="1" android:layout_height="@dimen/qb_px_56"
android:orientation="vertical" android:layout_marginStart="@dimen/qb_px_36"
android:layout_marginStart="@dimen/qb_px_24" android:layout_marginTop="@dimen/qb_px_24"
android:layout_marginBottom="-40dp"> android:text="▶ 播放"
android:textColor="@color/text_primary"
android:textSize="@dimen/mg_sp_22"
android:textStyle="bold"
android:gravity="center"
android:background="@drawable/bg_detail_play_btn"
android:clickable="true"
android:focusable="true"
android:nextFocusRight="@id/btnBack"
android:nextFocusLeft="@id/btnPlay"
android:nextFocusUp="@id/btnPlay"
app:layout_constraintStart_toEndOf="@id/ivPoster"
app:layout_constraintTop_toBottomOf="@id/tvIntro" />
<!-- 标题 --> <!-- 返回按钮 -->
<TextView <ImageView
android:id="@+id/tvTitle" android:id="@+id/btnBack"
android:layout_width="wrap_content" android:layout_width="@dimen/qb_px_56"
android:layout_height="wrap_content" android:layout_height="@dimen/qb_px_56"
android:textColor="@color/text_primary" android:layout_marginStart="@dimen/qb_px_16"
android:textSize="@dimen/mg_sp_28" android:src="@drawable/ic_back"
android:textStyle="bold" android:scaleType="center"
android:singleLine="true" android:contentDescription="@string/back"
android:ellipsize="end" android:background="@drawable/bg_detail_back_btn"
android:maxWidth="400dp" /> android:clickable="true"
android:focusable="true"
android:nextFocusLeft="@id/btnPlay"
android:nextFocusRight="@id/btnBack"
android:nextFocusUp="@id/btnBack"
app:layout_constraintStart_toEndOf="@id/btnPlay"
app:layout_constraintTop_toTopOf="@id/btnPlay"
app:layout_constraintBottom_toBottomOf="@id/btnPlay" />
<!-- 元信息:分类 · 地区 · 年份 · 时长 --> </androidx.constraintlayout.widget.ConstraintLayout>
<TextView
android:id="@+id/tvType"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/qb_px_8"
android:textColor="@color/text_secondary"
android:textSize="@dimen/mg_sp_16" />
<!-- 分割线 -->
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginTop="@dimen/qb_px_10"
android:layout_marginBottom="@dimen/qb_px_10"
android:background="@color/divider" />
<!-- 演员 -->
<TextView
android:id="@+id/tvActor"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@color/text_secondary"
android:textSize="@dimen/mg_sp_16"
android:maxLines="2"
android:ellipsize="end"
android:lineSpacingExtra="2dp" />
<!-- 简介 -->
<TextView
android:id="@+id/tvIntro"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/qb_px_8"
android:textColor="@color/text_tertiary"
android:textSize="@dimen/mg_sp_16"
android:maxLines="4"
android:ellipsize="end"
android:lineSpacingExtra="@dimen/qb_px_4" />
</LinearLayout>
</LinearLayout>
<!-- 操作按钮区 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical"
android:layout_marginTop="@dimen/qb_px_24"
android:paddingHorizontal="@dimen/qb_px_50">
<!-- 播放按钮 -->
<TextView
android:id="@+id/btnPlay"
android:layout_width="140dp"
android:layout_height="52dp"
android:text="▶ 播放"
android:textColor="@color/text_primary"
android:textSize="@dimen/mg_sp_20"
android:textStyle="bold"
android:gravity="center"
android:background="@drawable/bg_play_button"
android:clickable="true"
android:focusable="true" />
<!-- 返回按钮 -->
<ImageView
android:id="@+id/btnBack"
android:layout_width="@dimen/qb_px_40"
android:layout_height="@dimen/qb_px_40"
android:layout_marginStart="@dimen/qb_px_20"
android:src="@drawable/ic_back"
android:contentDescription="@string/back"
android:background="?selectableItemBackgroundBorderless" />
</LinearLayout>
</LinearLayout>
</ScrollView>

View File

@@ -27,6 +27,31 @@
android:gravity="center" android:gravity="center"
android:layout_marginBottom="@dimen/qb_px_16" /> android:layout_marginBottom="@dimen/qb_px_16" />
<!-- 微信支付品牌标识(显式标识,满足微信支付商户接入规范) -->
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical"
android:layout_marginBottom="@dimen/qb_px_12">
<ImageView
android:layout_width="28dp"
android:layout_height="28dp"
android:src="@drawable/ic_wechat_pay_logo"
android:scaleType="fitCenter"
android:layout_marginEnd="@dimen/qb_px_8"
android:contentDescription="微信支付" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="微信支付"
android:textColor="@color/wechat_green"
android:textSize="@dimen/mg_sp_18"
android:textStyle="bold" />
</LinearLayout>
<!-- 二维码区域(新增) --> <!-- 二维码区域(新增) -->
<FrameLayout <FrameLayout
android:id="@+id/flQrcode" android:id="@+id/flQrcode"

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -63,6 +63,10 @@
<color name="white7">#B3FFFFFF</color> <color name="white7">#B3FFFFFF</color>
<color name="transparent">#00000000</color> <color name="transparent">#00000000</color>
<!-- 微信支付品牌色 -->
<color name="wechat_green">#07C160</color>
<color name="wechat_green_dark">#06AD56</color>
<!-- 筛选页/列表页移植颜色 --> <!-- 筛选页/列表页移植颜色 -->
<color name="index_nav_text_nor">#B3FFFFFF</color> <color name="index_nav_text_nor">#B3FFFFFF</color>
<color name="activity_detail_bg">#000910</color> <color name="activity_detail_bg">#000910</color>

View File

@@ -24,7 +24,7 @@
<string name="str_activate_code_7_tip">激活码需为5位数字</string> <string name="str_activate_code_7_tip">激活码需为5位数字</string>
<string name="str_activate_submit_unavailable">当前无法提交激活,请稍后重试</string> <string name="str_activate_submit_unavailable">当前无法提交激活,请稍后重试</string>
<string name="str_activate_success_refresh">激活成功,正在刷新服务状态...</string> <string name="str_activate_success_refresh">激活成功,正在刷新服务状态...</string>
<string name="str_member_benifest_activate">会员权益激活</string> <string name="str_member_benifest_activate">绑定门店信息</string>
<string name="str_device_name">设备名称</string> <string name="str_device_name">设备名称</string>
<string name="str_version_no">版本号</string> <string name="str_version_no">版本号</string>
<string name="str_channel_id">渠道标识</string> <string name="str_channel_id">渠道标识</string>

View File

@@ -38,7 +38,8 @@ android {
} }
dependencies { dependencies {
implementation fileTree(dir: 'libs', include: ['*.aar', '*.jar'], exclude: []) implementation(name: 'HyMediaPlayer-v1.2.15', ext: 'aar')
implementation(name: 'exoplayer-ffmpeg-extension-2.19.1', ext: 'aar')
app_app_dependencies.each {k, v ->implementation v} app_app_dependencies.each {k, v ->implementation v}
testImplementation 'junit:junit:4.13.2' testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3' androidTestImplementation 'androidx.test.ext:junit:1.1.3'

View File

@@ -15,6 +15,9 @@ dependencyResolutionManagement {
maven { url 'https://jitpack.io' } maven { url 'https://jitpack.io' }
maven { url 'https://developer.huawei.com/repo/' } maven { url 'https://developer.huawei.com/repo/' }
maven { url 'https://developer.hihonor.com/repo/' } maven { url 'https://developer.hihonor.com/repo/' }
flatDir {
dirs "${rootDir}/mediaplayertv/libs"
}
} }
} }
rootProject.name = "DayCard" rootProject.name = "DayCard"