@@ -99,6 +99,8 @@ class MainActivity : Activity() {
private var confirmOverlay : View ? = null
/** 右上角「立即授权」按钮(未授权时显示) */
private var authBtn : TextView ? = null
/** 右下角「已授权」小字标识(已授权时显示,未授权隐藏) */
private var authStateText : TextView ? = null
/** 首次启动简易教程遮罩 */
private var tutorialOverlay : View ? = null
private var tutorialVisible = false
@@ -216,6 +218,20 @@ class MainActivity : Activity() {
ViewGroup . LayoutParams . WRAP _CONTENT , ViewGroup . LayoutParams . WRAP _CONTENT , Gravity . TOP or Gravity . END )
root . addView ( rightCol , rcLP )
// 4b) 右下角:已授权标识(小字,不打扰观看;未授权时隐藏)
authStateText = TextView ( this ) . apply {
text = " ✓ 已授权 "
textSize = 12f
setTextColor ( 0xB3FFFFFF . toInt ( ) ) // 70% 白,弱化存在感
setBackgroundColor ( 0x59000000 . toInt ( ) ) // 35% 黑底,保证在亮画面上也看得清
setPadding ( 14 , 6 , 14 , 6 )
setShadowLayer ( 3f , 1f , 1f , Color . BLACK )
visibility = View . GONE
}
root . addView ( authStateText , FrameLayout . LayoutParams (
ViewGroup . LayoutParams . WRAP _CONTENT , ViewGroup . LayoutParams . WRAP _CONTENT ,
Gravity . BOTTOM or Gravity . END ) . apply { setMargins ( 0 , 0 , 26 , 26 ) } )
// 5) 数字键大字
digitText = TextView ( this ) . apply {
textSize = 56f ; setTextColor ( Color . WHITE )
@@ -229,8 +245,9 @@ class MainActivity : Activity() {
// 6) 加载罩
loadingMask = FrameLayout ( this ) . apply { setBackgroundColor ( 0xB2000000u . toInt ( ) ) ; visibility = View . GONE }
loadingText = TextView ( this ) . apply {
textSize = 28f ; setTextColor ( Color . WHITE ) ; gravity = Gravity . CENTER
setShadowLayer ( 6f , 3f , 3f , Color . BLACK )
textSize = 16f ; setTextColor ( 0xE6FFFFFF . toInt ( ) ) ; gravity = Gravity . CENTER
setShadowLayer ( 4f , 2f , 2f , Color . BLACK )
setPadding ( 32 , 0 , 32 , 0 )
}
loadingMask . addView ( loadingText , FrameLayout . LayoutParams (
ViewGroup . LayoutParams . MATCH _PARENT , ViewGroup . LayoutParams . MATCH _PARENT ) )
@@ -347,7 +364,11 @@ class MainActivity : Activity() {
runOnUiThread {
isVip = r . ok
prefs . edit ( ) . putBoolean ( " is_vip " , r . ok ) . apply ( )
if ( r . ok ) { trialText . visibility = View . GONE ; trialText . text = " VIP " ; showAuthButton ( false ) } else enterFreeMode ( )
if ( r . ok ) {
trialText . visibility = View . GONE ; trialText . text = " VIP "
showAuthButton ( false )
showAuthState ( true ) // 右下角显示「✓ 已授权」
} else enterFreeMode ( )
}
} . start ( )
}
@@ -355,10 +376,20 @@ class MainActivity : Activity() {
private fun enterFreeMode ( ) {
trialText . visibility = View . VISIBLE
showAuthButton ( true )
showAuthState ( false ) // 免费模式下不显示右下角标识
handler . removeCallbacks ( uiTick )
handler . post ( uiTick )
}
/** 右下角「✓ 已授权」标识 显示/隐藏 */
private fun showAuthState ( show : Boolean ) {
runOnUiThread {
val t = authStateText ?: return @runOnUiThread
t . visibility = if ( show ) View . VISIBLE else View . GONE
t . text = if ( show ) " ✓ 已授权 " else " "
}
}
/** 显示/隐藏右上角「立即授权」按钮 */
private fun showAuthButton ( show : Boolean ) {
runOnUiThread {
@@ -377,15 +408,37 @@ class MainActivity : Activity() {
}
}
/** 打开授权/激活页面(含卡密激活与购买入口) */
private fun openAuthPage ( ) {
/** 菜单键/其他入口统一打开设置页;带三重诊断,打不开时屏幕上有原因 */
private fun openSettingsPage ( ) {
val intent = Intent ( this , SettingsActivity :: class . java )
// ① 预检查:系统能否解析到这个 Activity( manifest 没注册会返回 null)
val resolve = try { packageManager . resolveActivity ( intent , 0 ) } catch ( e : Exception ) { null }
if ( resolve == null ) {
statusText . text = " ⚠ 设置页无法解析:manifest 未注册 SettingsActivity "
android . widget . Toast . makeText ( this , " 设置页无法解析(manifest 未注册) " , android . widget . Toast . LENGTH _LONG ) . show ( )
return
}
// ② 启动
val before = try { getSharedPreferences ( " iptv_main " , MODE _PRIVATE ) . getLong ( " settings_opened_at " , 0L ) } catch ( e : Exception ) { 0L }
try {
startActivity ( Intent ( this , SettingsActivity :: class . java ) )
startActivity ( intent )
} catch ( e : Exception ) {
statusText . text = " 打开授权页 失败: ${e.javaClass.simpleName} : ${e.message} "
android . widget . Toast . makeText ( this , " 打开授权页 失败: ${e.message} " , android . widget . Toast . LENGTH _LONG ) . show ( )
statusText . text = " ⚠ 打开设置 失败: ${e.javaClass.simpleName} : ${e.message} "
android . widget . Toast . makeText ( this , " 打开设置 失败: ${e.message} " , android . widget . Toast . LENGTH _LONG ) . show ( )
return
}
// ③ 1.4 秒后核对:SettingsActivity.onCreate 有没有真的跑起来
handler . postDelayed ( {
val after = try { getSharedPreferences ( " iptv_main " , MODE _PRIVATE ) . getLong ( " settings_opened_at " , 0L ) } catch ( e : Exception ) { 0L }
if ( after <= before ) {
statusText . text = " ⚠ 设置页未响应:onCreate 未执行(启动被拦截或立刻结束) "
android . widget . Toast . makeText ( this , " 设置页未响应,请把这行字告诉我 " , android . widget . Toast . LENGTH _LONG ) . show ( )
}
} , 1400 )
}
/** 打开授权/激活页面(就是设置页:卡密激活 + 购买入口 + 撤销授权) */
private fun openAuthPage ( ) { openSettingsPage ( ) }
/** 首次启动:简易使用教程(可关闭,只弹一次) */
private fun maybeShowTutorial ( ) {
@@ -575,13 +628,7 @@ class MainActivity : Activity() {
setItemsCanFocus ( false )
onItemClickListener = AdapterView . OnItemClickListener { _ , _ , pos , _ ->
if ( pos == 0 ) {
try { startActivity ( Intent ( this @MainActivity , SettingsActivity :: class . java ) ) }
catch ( e : Exception ) {
statusText . text = " 打开设置失败: ${e.javaClass.simpleName} : ${e.message} "
android . widget . Toast . makeText ( this @MainActivity , " 打开设置失败: ${e.message} " , android . widget . Toast . LENGTH _LONG ) . show ( )
return @OnItemClickListener
}
hidePanel ( ) ; return @OnItemClickListener
openSettingsPage ( ) ; hidePanel ( ) ; return @OnItemClickListener
}
if ( pos == 0 ) return @OnItemClickListener
curGroupPos = pos - 1
@@ -912,13 +959,7 @@ class MainActivity : Activity() {
if ( panelCol == 0 ) {
val np = groupAdapter ?. selected ?: 0
if ( np == 0 ) {
// 保险:打开失败要能在屏幕上看到原因,便于排查
try { startActivity ( Intent ( this , SettingsActivity :: class . java ) ) }
catch ( e : Exception ) {
statusText . text = " 打开设置失败: ${e.javaClass.simpleName} : ${e.message} "
android . widget . Toast . makeText ( this @MainActivity , " 打开设置失败: ${e.message} " , android . widget . Toast . LENGTH _LONG ) . show ( )
return true
}
openSettingsPage ( ) // 统一走带诊断的入口
hidePanel ( )
}
else { curGroupPos = np - 1 ; updateChannelListOfGroup ( curGroupPos ) ; panelCol = 1 ; refreshPanelHighlight ( ) }
@@ -936,7 +977,8 @@ class MainActivity : Activity() {
// ★ 修复:返回键必须弹退出确认,绝不直接 finish(原来漏了这个分支,
// 导致 BACK 落到 super.onKeyDown() = 系统默认无提示退出)
KeyEvent . KEYCODE _BACK -> { exitApp ( ) ; return true }
KeyEvent . KEYCODE _MENU -> { autoSwitchLine ( ) ; return true }
// ★ 菜单键 → 打开设置页(原来这里是「换线」,换线已移到设置页里)
KeyEvent . KEYCODE _MENU -> { openSettingsPage ( ) ; return true }
KeyEvent . KEYCODE _CHANNEL _UP -> { playChannel ( ( currentChannel - 1 + channels . size ) % channels . size ) ; return true }
KeyEvent . KEYCODE _CHANNEL _DOWN -> { playChannel ( ( currentChannel + 1 ) % channels . size ) ; return true }
KeyEvent . KEYCODE _DPAD _UP , KeyEvent . KEYCODE _DPAD _DOWN -> {
@@ -1242,10 +1284,12 @@ class MainActivity : Activity() {
isVip = true
trialText . visibility = View . GONE
showAuthButton ( false )
showAuthState ( true ) // 右下角显示「✓ 已授权」
handler . removeCallbacks ( uiTick )
} else if ( ! nowVip && isVip ) {
// ★ 设置页里撤销了授权 → 回到免费模式(倒计时 + 立即授权按钮都恢复)
isVip = false
showAuthState ( false ) // 右下角标识隐藏
enterFreeMode ( )
}
}
@@ -1276,6 +1320,14 @@ class MainActivity : Activity() {
playRequested = - 1
if ( idx >= 0 ) playChannel ( idx )
}
// 设置页点「切换线路」→ 回来执行(原菜单键功能,现已移到设置页)
run {
val sp = getSharedPreferences ( " iptv_main " , MODE _PRIVATE )
if ( sp . getString ( " pending_action " , " " ) == " switch_line " ) {
sp . edit ( ) . remove ( " pending_action " ) . apply ( )
autoSwitchLine ( )
}
}
}
/** 点「继续播放」→ 重新连接当前频道(直播流最稳的恢复方式) */