修复启动时序、重构详情页布局、对接取消订单接口
1. 启动时序修复:Launcher 从 ActivationActivity 改为 HomeActivity,
已激活用户直接进首页,不再闪现激活页
2. 详情页布局重构:左右分栏设计(左侧海报 + 右侧信息/操作),
全屏背景图 + 渐变遮罩,TV 端焦点导航
3. 对接后端取消订单接口 POST /api/jst/daycard/cancel/{orderNo},
用户关闭支付弹窗时自动取消未支付订单
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -17,6 +17,7 @@ captures/
|
|||||||
|
|
||||||
# Local config(含 SDK 路径,不提交)
|
# Local config(含 SDK 路径,不提交)
|
||||||
local.properties
|
local.properties
|
||||||
|
gradle/config.properties
|
||||||
|
|
||||||
# OS
|
# OS
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|||||||
@@ -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" />
|
||||||
|
|
||||||
|
|||||||
@@ -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 */
|
||||||
|
|||||||
@@ -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()
|
||||||
|
)
|
||||||
|
|||||||
@@ -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
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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) } } }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
15
app/src/main/res/drawable/bg_detail_back_btn.xml
Normal file
15
app/src/main/res/drawable/bg_detail_back_btn.xml
Normal 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>
|
||||||
22
app/src/main/res/drawable/bg_detail_play_btn.xml
Normal file
22
app/src/main/res/drawable/bg_detail_play_btn.xml
Normal 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>
|
||||||
10
app/src/main/res/drawable/bg_detail_scrim.xml
Normal file
10
app/src/main/res/drawable/bg_detail_scrim.xml
Normal 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>
|
||||||
25
app/src/main/res/drawable/ic_wechat_pay_logo.xml
Normal file
25
app/src/main/res/drawable/ic_wechat_pay_logo.xml
Normal 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>
|
||||||
@@ -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"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:paddingBottom="@dimen/qb_px_40">
|
|
||||||
|
|
||||||
<!-- 顶部 16:9 横幅背景图 -->
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/ivBackdrop"
|
android:id="@+id/ivBackdrop"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="0dp"
|
||||||
android:layout_height="200dp"
|
android:layout_height="0dp"
|
||||||
android:scaleType="centerCrop"
|
android:scaleType="centerCrop"
|
||||||
android:contentDescription="@string/app_name" />
|
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" />
|
||||||
|
|
||||||
<!-- 海报 + 信息区 -->
|
<!-- 渐变遮罩:左浅右深,保证右侧文字可读 -->
|
||||||
<LinearLayout
|
<View
|
||||||
android:layout_width="match_parent"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="0dp"
|
||||||
android:orientation="horizontal"
|
android:background="@drawable/bg_detail_scrim"
|
||||||
android:layout_marginTop="-40dp"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
android:paddingHorizontal="@dimen/qb_px_50">
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent" />
|
||||||
|
|
||||||
<!-- 海报 -->
|
<!-- 左侧竖版海报 -->
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/ivPoster"
|
android:id="@+id/ivPoster"
|
||||||
android:layout_width="130dp"
|
android:layout_width="@dimen/qb_px_280"
|
||||||
android:layout_height="182dp"
|
android:layout_height="@dimen/qb_px_400"
|
||||||
android:scaleType="centerCrop"
|
android:scaleType="centerCrop"
|
||||||
android:background="@drawable/bg_poster_rounded"
|
android:background="@drawable/bg_poster_rounded"
|
||||||
android:clipToOutline="true"
|
android:clipToOutline="true"
|
||||||
android:outlineProvider="background"
|
android:outlineProvider="background"
|
||||||
android:contentDescription="@string/app_name" />
|
android:contentDescription="@string/app_name"
|
||||||
|
android:layout_marginStart="@dimen/qb_px_50"
|
||||||
<!-- 右侧信息区 -->
|
android:layout_marginTop="@dimen/qb_px_50"
|
||||||
<LinearLayout
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
android:layout_width="0dp"
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:layout_marginStart="@dimen/qb_px_24"
|
|
||||||
android:layout_marginBottom="-40dp">
|
|
||||||
|
|
||||||
<!-- 标题 -->
|
<!-- 标题 -->
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/tvTitle"
|
android:id="@+id/tvTitle"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="@dimen/qb_px_36"
|
||||||
|
android:layout_marginEnd="@dimen/qb_px_50"
|
||||||
android:textColor="@color/text_primary"
|
android:textColor="@color/text_primary"
|
||||||
android:textSize="@dimen/mg_sp_28"
|
android:textSize="@dimen/mg_sp_32"
|
||||||
android:textStyle="bold"
|
android:textStyle="bold"
|
||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
android:maxWidth="400dp" />
|
android:includeFontPadding="false"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/ivPoster"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="@id/ivPoster" />
|
||||||
|
|
||||||
<!-- 元信息:分类 · 地区 · 年份 · 时长 -->
|
<!-- 元信息:分类 · 地区 · 年份 · 时长 -->
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/tvType"
|
android:id="@+id/tvType"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="@dimen/qb_px_8"
|
android:layout_marginStart="@dimen/qb_px_36"
|
||||||
|
android:layout_marginEnd="@dimen/qb_px_50"
|
||||||
|
android:layout_marginTop="@dimen/qb_px_16"
|
||||||
android:textColor="@color/text_secondary"
|
android:textColor="@color/text_secondary"
|
||||||
android:textSize="@dimen/mg_sp_16" />
|
android:textSize="@dimen/mg_sp_20"
|
||||||
|
android:singleLine="true"
|
||||||
<!-- 分割线 -->
|
android:ellipsize="end"
|
||||||
<View
|
android:includeFontPadding="false"
|
||||||
android:layout_width="match_parent"
|
app:layout_constraintStart_toEndOf="@id/ivPoster"
|
||||||
android:layout_height="1dp"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
android:layout_marginTop="@dimen/qb_px_10"
|
app:layout_constraintTop_toBottomOf="@id/tvTitle" />
|
||||||
android:layout_marginBottom="@dimen/qb_px_10"
|
|
||||||
android:background="@color/divider" />
|
|
||||||
|
|
||||||
<!-- 演员 -->
|
<!-- 演员 -->
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/tvActor"
|
android:id="@+id/tvActor"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="@dimen/qb_px_36"
|
||||||
|
android:layout_marginEnd="@dimen/qb_px_50"
|
||||||
|
android:layout_marginTop="@dimen/qb_px_12"
|
||||||
android:textColor="@color/text_secondary"
|
android:textColor="@color/text_secondary"
|
||||||
android:textSize="@dimen/mg_sp_16"
|
android:textSize="@dimen/mg_sp_20"
|
||||||
android:maxLines="2"
|
android:maxLines="2"
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
android:lineSpacingExtra="2dp" />
|
android:includeFontPadding="false"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/ivPoster"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/tvType" />
|
||||||
|
|
||||||
<!-- 简介 -->
|
<!-- 简介 -->
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/tvIntro"
|
android:id="@+id/tvIntro"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="@dimen/qb_px_8"
|
android:layout_marginStart="@dimen/qb_px_36"
|
||||||
|
android:layout_marginEnd="@dimen/qb_px_50"
|
||||||
|
android:layout_marginTop="@dimen/qb_px_12"
|
||||||
android:textColor="@color/text_tertiary"
|
android:textColor="@color/text_tertiary"
|
||||||
android:textSize="@dimen/mg_sp_16"
|
android:textSize="@dimen/mg_sp_20"
|
||||||
android:maxLines="4"
|
android:maxLines="3"
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
android:lineSpacingExtra="@dimen/qb_px_4" />
|
android:lineSpacingExtra="@dimen/qb_px_4"
|
||||||
|
android:includeFontPadding="false"
|
||||||
</LinearLayout>
|
app:layout_constraintStart_toEndOf="@id/ivPoster"
|
||||||
</LinearLayout>
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/tvActor" />
|
||||||
<!-- 操作按钮区 -->
|
|
||||||
<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
|
<TextView
|
||||||
android:id="@+id/btnPlay"
|
android:id="@+id/btnPlay"
|
||||||
android:layout_width="140dp"
|
android:layout_width="@dimen/qb_px_180"
|
||||||
android:layout_height="52dp"
|
android:layout_height="@dimen/qb_px_56"
|
||||||
|
android:layout_marginStart="@dimen/qb_px_36"
|
||||||
|
android:layout_marginTop="@dimen/qb_px_24"
|
||||||
android:text="▶ 播放"
|
android:text="▶ 播放"
|
||||||
android:textColor="@color/text_primary"
|
android:textColor="@color/text_primary"
|
||||||
android:textSize="@dimen/mg_sp_20"
|
android:textSize="@dimen/mg_sp_22"
|
||||||
android:textStyle="bold"
|
android:textStyle="bold"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:background="@drawable/bg_play_button"
|
android:background="@drawable/bg_detail_play_btn"
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
android:focusable="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" />
|
||||||
|
|
||||||
<!-- 返回按钮 -->
|
<!-- 返回按钮 -->
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/btnBack"
|
android:id="@+id/btnBack"
|
||||||
android:layout_width="@dimen/qb_px_40"
|
android:layout_width="@dimen/qb_px_56"
|
||||||
android:layout_height="@dimen/qb_px_40"
|
android:layout_height="@dimen/qb_px_56"
|
||||||
android:layout_marginStart="@dimen/qb_px_20"
|
android:layout_marginStart="@dimen/qb_px_16"
|
||||||
android:src="@drawable/ic_back"
|
android:src="@drawable/ic_back"
|
||||||
|
android:scaleType="center"
|
||||||
android:contentDescription="@string/back"
|
android:contentDescription="@string/back"
|
||||||
android:background="?selectableItemBackgroundBorderless" />
|
android:background="@drawable/bg_detail_back_btn"
|
||||||
|
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" />
|
||||||
|
|
||||||
</LinearLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
</ScrollView>
|
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
1979
app/src/main/res/values-sw1080dp-land/dimens.xml
Executable file
1979
app/src/main/res/values-sw1080dp-land/dimens.xml
Executable file
File diff suppressed because it is too large
Load Diff
1980
app/src/main/res/values-sw360dp-land/dimens.xml
Executable file
1980
app/src/main/res/values-sw360dp-land/dimens.xml
Executable file
File diff suppressed because it is too large
Load Diff
1980
app/src/main/res/values-sw480dp-land/dimens.xml
Executable file
1980
app/src/main/res/values-sw480dp-land/dimens.xml
Executable file
File diff suppressed because it is too large
Load Diff
1980
app/src/main/res/values-sw540dp-land/dimens.xml
Executable file
1980
app/src/main/res/values-sw540dp-land/dimens.xml
Executable file
File diff suppressed because it is too large
Load Diff
1980
app/src/main/res/values-sw617dp-land/dimens.xml
Executable file
1980
app/src/main/res/values-sw617dp-land/dimens.xml
Executable file
File diff suppressed because it is too large
Load Diff
1980
app/src/main/res/values-sw720dp-land/dimens.xml
Executable file
1980
app/src/main/res/values-sw720dp-land/dimens.xml
Executable file
File diff suppressed because it is too large
Load Diff
1977
app/src/main/res/values-sw768dp-land/dimens.xml
Executable file
1977
app/src/main/res/values-sw768dp-land/dimens.xml
Executable file
File diff suppressed because it is too large
Load Diff
1977
app/src/main/res/values-sw864dp-land/dimens.xml
Executable file
1977
app/src/main/res/values-sw864dp-land/dimens.xml
Executable file
File diff suppressed because it is too large
Load Diff
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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'
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
Reference in New Issue
Block a user