update mannager

This commit is contained in:
Li ZongYing
2024-02-25 10:19:35 +08:00
parent e7780434d7
commit 8a3720049f
18 changed files with 486 additions and 36 deletions
@@ -47,6 +47,11 @@ class CardPresenter(
.centerInside() .centerInside()
.into(cardView.mainImageView) .into(cardView.mainImageView)
"兵团卫视" -> Glide.with(viewHolder.view.context)
.load(R.drawable.bingtuan)
.centerInside()
.into(cardView.mainImageView)
else -> Glide.with(viewHolder.view.context) else -> Glide.with(viewHolder.view.context)
.load(tvViewModel.logo.value) .load(tvViewModel.logo.value)
.centerInside() .centerInside()
@@ -0,0 +1,36 @@
package com.lizongying.mytv
import android.app.AlertDialog
import android.app.Dialog
import android.os.Bundle
import androidx.fragment.app.DialogFragment
class ConfirmationDialogFragment(private val listener: ConfirmationDialogListener) :
DialogFragment() {
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
return activity?.let {
val builder = AlertDialog.Builder(it)
builder.setTitle("确认")
.setMessage("确认更新吗?")
.setPositiveButton(
"确定"
) { _, _ ->
listener.onConfirm()
}
.setNegativeButton(
"取消"
) { _, _ ->
listener.onCancel()
}
// 创建并返回 AlertDialog 对象
builder.create()
} ?: throw IllegalStateException("Activity cannot be null")
}
interface ConfirmationDialogListener {
fun onConfirm()
fun onCancel()
}
}
@@ -9,6 +9,8 @@ class Encryptor {
external fun hash(data: ByteArray): ByteArray? external fun hash(data: ByteArray): ByteArray?
external fun hash2(data: ByteArray): ByteArray?
companion object { companion object {
init { init {
System.loadLibrary("native") System.loadLibrary("native")
@@ -29,10 +29,11 @@ import java.security.MessageDigest
class MainActivity : FragmentActivity() { class MainActivity : FragmentActivity() {
var playerFragment = PlayerFragment() private var ready = 0
private val mainFragment = MainFragment() private var playerFragment = PlayerFragment()
private val infoFragment = InfoFragment() private var mainFragment = MainFragment()
private val channelFragment = ChannelFragment() private var infoFragment = InfoFragment()
private var channelFragment = ChannelFragment()
private lateinit var settingFragment: SettingFragment private lateinit var settingFragment: SettingFragment
private var doubleBackToExitPressedOnce = false private var doubleBackToExitPressedOnce = false
@@ -85,8 +86,15 @@ class MainActivity : FragmentActivity() {
channelNum = sharedPref.getBoolean(CHANNEL_NUM, channelNum) channelNum = sharedPref.getBoolean(CHANNEL_NUM, channelNum)
bootStartup = sharedPref.getBoolean(BOOT_STARTUP, bootStartup) bootStartup = sharedPref.getBoolean(BOOT_STARTUP, bootStartup)
versionName = getPackageInfo().versionName val packageInfo = getPackageInfo()
settingFragment = SettingFragment(versionName, channelReversal, channelNum, bootStartup) versionName = packageInfo.versionName
val versionCode = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
packageInfo.longVersionCode
} else {
packageInfo.versionCode.toLong()
}
settingFragment =
SettingFragment(versionName, versionCode, channelReversal, channelNum, bootStartup)
} }
fun showInfoFragment(tvViewModel: TVViewModel) { fun showInfoFragment(tvViewModel: TVViewModel) {
@@ -172,7 +180,11 @@ class MainActivity : FragmentActivity() {
} }
fun fragmentReady() { fun fragmentReady() {
mainFragment.fragmentReady() ready++
Log.i(TAG, "ready $ready")
if (ready == 4) {
mainFragment.fragmentReady()
}
} }
override fun onTouchEvent(event: MotionEvent?): Boolean { override fun onTouchEvent(event: MotionEvent?): Boolean {
@@ -520,7 +532,7 @@ class MainActivity : FragmentActivity() {
override fun onResume() { override fun onResume() {
Log.i(TAG, "onResume") Log.i(TAG, "onResume")
super.onResume() super.onResume()
if (!mainFragment.isHidden){ if (!mainFragment.isHidden) {
handler.postDelayed(hideMain, delayHideMain) handler.postDelayed(hideMain, delayHideMain)
} }
} }
@@ -41,8 +41,6 @@ class MainFragment : BrowseSupportFragment() {
private val handler = Handler(Looper.getMainLooper()) private val handler = Handler(Looper.getMainLooper())
private lateinit var mUpdateProgramRunnable: UpdateProgramRunnable private lateinit var mUpdateProgramRunnable: UpdateProgramRunnable
private var ready = 0
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
Log.i(TAG, "onCreate") Log.i(TAG, "onCreate")
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@@ -89,6 +87,7 @@ class MainFragment : BrowseSupportFragment() {
tvViewModel.change.observe(viewLifecycleOwner) { _ -> tvViewModel.change.observe(viewLifecycleOwner) { _ ->
if (tvViewModel.change.value != null) { if (tvViewModel.change.value != null) {
val title = tvViewModel.title.value val title = tvViewModel.title.value
Log.i(TAG, "switch $title")
if (tvViewModel.pid.value != "") { if (tvViewModel.pid.value != "") {
Log.i(TAG, "request $title") Log.i(TAG, "request $title")
lifecycleScope.launch(Dispatchers.IO) { lifecycleScope.launch(Dispatchers.IO) {
@@ -113,7 +112,7 @@ class MainFragment : BrowseSupportFragment() {
} }
} }
fragmentReady() (activity as MainActivity).fragmentReady()
} }
fun toLastPosition() { fun toLastPosition() {
@@ -245,12 +244,8 @@ class MainFragment : BrowseSupportFragment() {
} }
fun fragmentReady() { fun fragmentReady() {
ready++
Log.i(TAG, "ready $ready")
if (ready == 4) {
// request.fetchPage() // request.fetchPage()
tvListViewModel.getTVViewModel(itemPosition)?.changed() tvListViewModel.getTVViewModel(itemPosition)?.changed()
}
} }
fun play(itemPosition: Int) { fun play(itemPosition: Int) {
@@ -332,7 +327,9 @@ class MainFragment : BrowseSupportFragment() {
override fun onDestroy() { override fun onDestroy() {
Log.i(TAG, "onDestroy") Log.i(TAG, "onDestroy")
super.onDestroy() super.onDestroy()
handler.removeCallbacks(mUpdateProgramRunnable) if (::mUpdateProgramRunnable.isInitialized) {
handler.removeCallbacks(mUpdateProgramRunnable)
}
} }
companion object { companion object {
@@ -80,6 +80,7 @@ class PlayerFragment : Fragment(), SurfaceHolder.Callback {
super.onPlayerError(error) super.onPlayerError(error)
Log.e(TAG, "PlaybackException $error") Log.e(TAG, "PlaybackException $error")
tvViewModel?.changed()
} }
}) })
} }
@@ -107,10 +108,12 @@ class PlayerFragment : Fragment(), SurfaceHolder.Callback {
if (playerView != null && playerView!!.player?.isPlaying == false) { if (playerView != null && playerView!!.player?.isPlaying == false) {
Log.i(TAG, "replay") Log.i(TAG, "replay")
playerView!!.player?.prepare() playerView!!.player?.prepare()
playerView!!.player?.play()
} }
if (exoPlayer?.isPlaying == false) { if (exoPlayer?.isPlaying == false) {
Log.i(TAG, "replay") Log.i(TAG, "replay")
exoPlayer?.prepare() exoPlayer?.prepare()
exoPlayer?.play()
} }
} }
@@ -8,16 +8,20 @@ import androidx.fragment.app.DialogFragment
import com.lizongying.mytv.databinding.DialogBinding import com.lizongying.mytv.databinding.DialogBinding
class SettingFragment(private val versionName: String, class SettingFragment(
private val channelReversal: Boolean, private val versionName: String,
private val channelNum: Boolean, private val versionCode: Long,
private val bootStartup: Boolean, private val channelReversal: Boolean,
) : private val channelNum: Boolean,
private val bootStartup: Boolean,
) :
DialogFragment() { DialogFragment() {
private var _binding: DialogBinding? = null private var _binding: DialogBinding? = null
private val binding get() = _binding!! private val binding get() = _binding!!
private lateinit var updateManager: UpdateManager
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setStyle(STYLE_NO_TITLE, 0) setStyle(STYLE_NO_TITLE, 0)
@@ -50,14 +54,33 @@ class SettingFragment(private val versionName: String,
(activity as MainActivity).saveBootStartup(isChecked) (activity as MainActivity).saveBootStartup(isChecked)
} }
updateManager = UpdateManager(context, this, versionCode)
_binding?.checkVersion?.setOnClickListener(OnClickListenerCheckVersion(updateManager))
return binding.root return binding.root
} }
fun setVersionName(versionName: String) {
binding.versionName.text = versionName
}
internal class OnClickListenerCheckVersion(private val updateManager: UpdateManager) :
View.OnClickListener {
override fun onClick(view: View?) {
updateManager.checkAndUpdate()
}
}
override fun onDestroyView() { override fun onDestroyView() {
super.onDestroyView() super.onDestroyView()
_binding = null _binding = null
} }
override fun onDestroy() {
super.onDestroy()
// updateManager.destroy()
}
companion object { companion object {
const val TAG = "SettingFragment" const val TAG = "SettingFragment"
} }
@@ -859,10 +859,24 @@ object TVList {
true, true,
mustToken = false mustToken = false
), ),
TV(
56,
"兵团卫视",
"兵团卫视",
listOf(),
0,
"地方频道",
"",
"600170344",
"2022606701",
"",
true,
mustToken = false
),
), ),
"国际频道" to listOf( "国际频道" to listOf(
TV( TV(
56, 57,
"CGTN", "CGTN",
"CGTN", "CGTN",
listOf("http://live.cgtn.com/1000/prog_index.m3u8"), listOf("http://live.cgtn.com/1000/prog_index.m3u8"),
@@ -876,7 +890,7 @@ object TVList {
mustToken = false mustToken = false
), ),
TV( TV(
57, 58,
"CGTN 法语频道", "CGTN 法语频道",
"CGTN法语频道", "CGTN法语频道",
listOf("https://livefr.cgtn.com/1000f/prog_index.m3u8"), listOf("https://livefr.cgtn.com/1000f/prog_index.m3u8"),
@@ -890,7 +904,7 @@ object TVList {
mustToken = false mustToken = false
), ),
TV( TV(
58, 59,
"CGTN 俄语频道", "CGTN 俄语频道",
"CGTN俄语频道", "CGTN俄语频道",
listOf("http://liveru.cgtn.com/1000r/prog_index.m3u8"), listOf("http://liveru.cgtn.com/1000r/prog_index.m3u8"),
@@ -904,7 +918,7 @@ object TVList {
mustToken = false mustToken = false
), ),
TV( TV(
59, 60,
"CGTN 阿拉伯语频道", "CGTN 阿拉伯语频道",
"CGTN阿拉伯语频道", "CGTN阿拉伯语频道",
listOf("http://livear.cgtn.com/1000a/prog_index.m3u8"), listOf("http://livear.cgtn.com/1000a/prog_index.m3u8"),
@@ -918,7 +932,7 @@ object TVList {
mustToken = false mustToken = false
), ),
TV( TV(
60, 61,
"CGTN 西班牙语频道", "CGTN 西班牙语频道",
"CGTN西班牙语频道", "CGTN西班牙语频道",
listOf( listOf(
@@ -935,7 +949,7 @@ object TVList {
mustToken = false mustToken = false
), ),
TV( TV(
61, 62,
"CGTN 纪录频道", "CGTN 纪录频道",
"CGTN外语纪录频道", "CGTN外语纪录频道",
listOf("https://livedoc.cgtn.com/500d/prog_index.m3u8"), listOf("https://livedoc.cgtn.com/500d/prog_index.m3u8"),
@@ -1,5 +1,189 @@
package com.lizongying.mytv package com.lizongying.mytv
class UpdateManager { import android.app.DownloadManager
import android.app.DownloadManager.Request
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.database.Cursor
import android.net.Uri
import android.os.Build
import android.os.Environment
import android.os.Handler
import android.os.Looper
import android.util.Log
import android.widget.Toast
import com.lizongying.mytv.api.Release
import com.lizongying.mytv.requests.MyRequest
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import java.io.File
class UpdateManager(
private var context: Context?,
private var settingFragment: SettingFragment,
private var versionCode: Long
) :
ConfirmationDialogFragment.ConfirmationDialogListener {
private var myRequest = MyRequest()
private var release: Release? = null
private var downloadReceiver: DownloadReceiver? = null
fun checkAndUpdate() {
CoroutineScope(Dispatchers.Main).launch {
try {
release = myRequest.getRelease()
updateUI(release)
Log.i(TAG, "versionCode $versionCode ${release?.data?.versionCode}")
if (release != null) {
if (release?.data?.versionCode!! >= versionCode) {
val dialog = ConfirmationDialogFragment(this@UpdateManager)
dialog.show(settingFragment.fragmentManager, "ConfirmationDialogFragment")
} else {
Toast.makeText(context, "不需要更新", Toast.LENGTH_LONG)
.show()
}
}
} catch (e: Exception) {
Log.e(TAG, "Error occurred: ${e.message}", e)
}
}
}
private fun updateUI(release: Release?) {
if (release?.data?.versionName.isNullOrEmpty()) {
settingFragment.setVersionName("版本获取失败")
} else {
settingFragment.setVersionName("最新版本:${release?.data?.versionName!!}")
}
}
private fun startDownload(release: Release) {
val apkFileName = "my-tv-${release.data.versionName}.apk"
Log.i(TAG, "apkFileName $apkFileName")
val downloadManager =
context!!.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
val request = Request(Uri.parse(release.data.downloadUrl))
Log.i(TAG, "url ${Uri.parse(release.data.downloadUrl)}")
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, apkFileName)
request.setTitle("New Version Download")
request.setNotificationVisibility(Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
// 获取下载任务的引用
val downloadReference = downloadManager.enqueue(request)
downloadReceiver = DownloadReceiver(context!!, apkFileName, downloadReference)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context!!.registerReceiver(
downloadReceiver,
IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE),
Context.RECEIVER_NOT_EXPORTED,
)
} else {
context!!.registerReceiver(
downloadReceiver,
IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE)
)
}
getDownloadProgress(context!!, downloadReference) { progress ->
println("Download progress: $progress%")
}
}
private fun getDownloadProgress(context: Context, downloadId: Long, progressListener: (Int) -> Unit) {
val downloadManager = context.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
val handler = Handler(Looper.getMainLooper())
val intervalMillis: Long = 1000
handler.post(object : Runnable {
override fun run() {
Log.i(TAG, "search")
val query = DownloadManager.Query().setFilterById(downloadId)
val cursor: Cursor = downloadManager.query(query)
cursor.use {
if (it.moveToFirst()) {
val bytesDownloadedIndex =
it.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR)
val bytesTotalIndex =
it.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES)
// 检查列名是否存在
if (bytesDownloadedIndex != -1 && bytesTotalIndex != -1) {
val bytesDownloaded = it.getInt(bytesDownloadedIndex)
val bytesTotal = it.getInt(bytesTotalIndex)
if (bytesTotal != -1) {
val progress = (bytesDownloaded * 100L / bytesTotal).toInt()
progressListener(progress)
if (progress == 100) {
return
}
}
}
}
}
// handler.postDelayed(this, intervalMillis)
}
})
}
private class DownloadReceiver(
private val context: Context,
private val apkFileName: String,
private val downloadReference: Long
) : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val reference = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1)
Log.i(TAG, "reference $reference")
val progress = intent.getIntExtra("progress", 0)
Log.i(TAG, "progress $progress")
// 检查是否是我们发起的下载
if (reference == downloadReference) {
// 下载完成,触发安装
installNewVersion()
}
}
private fun installNewVersion() {
val installIntent = Intent(Intent.ACTION_VIEW)
val apkUri = Uri.fromFile(
File(
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),
apkFileName
)
)
installIntent.setDataAndType(apkUri, "application/vnd.android.package-archive")
installIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
context.startActivity(installIntent)
}
}
companion object {
private const val TAG = "UpdateManager"
}
override fun onConfirm() {
Log.i(TAG, "onConfirm $release")
release?.let { startDownload(it) }
}
override fun onCancel() {
}
fun destroy() {
if (downloadReceiver != null) {
context!!.unregisterReceiver(downloadReceiver)
Log.i(TAG, "destroy downloadReceiver")
}
}
} }
@@ -13,6 +13,7 @@ import javax.net.ssl.X509TrustManager
class ApiClient { class ApiClient {
private val yspUrl = "https://player-api.yangshipin.cn/" private val yspUrl = "https://player-api.yangshipin.cn/"
private val myUrl = "https://lyrics.run/" private val myUrl = "https://lyrics.run/"
private val devUrl = "http://10.0.2.2:8081/"
private val protoUrl = "https://capi.yangshipin.cn/" private val protoUrl = "https://capi.yangshipin.cn/"
private val traceUrl = "https://btrace.yangshipin.cn/" private val traceUrl = "https://btrace.yangshipin.cn/"
@@ -34,6 +35,14 @@ class ApiClient {
.build().create(YSPTokenService::class.java) .build().create(YSPTokenService::class.java)
} }
val releaseService: ReleaseService by lazy {
Retrofit.Builder()
.baseUrl(myUrl)
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create())
.build().create(ReleaseService::class.java)
}
val yspProtoService: YSPProtoService by lazy { val yspProtoService: YSPProtoService by lazy {
Retrofit.Builder() Retrofit.Builder()
.baseUrl(protoUrl) .baseUrl(protoUrl)
@@ -0,0 +1,37 @@
package com.lizongying.mytv.api
import android.util.Log
import okhttp3.MediaType
import okhttp3.RequestBody
import okio.BufferedSink
import java.io.IOException
data class Auth(
val code: Int,
val msg: String,
val data: AuthData,
)
data class AuthData(
val token: String,
)
data class AuthRequest(
var data: String,
) : RequestBody() {
override fun contentType(): MediaType? {
return MediaType.parse("application/x-www-form-urlencoded;charset=UTF-8")
}
override fun writeTo(sink: BufferedSink) {
try {
sink.writeUtf8(data)
} catch (e: IOException) {
Log.e(TAG, "$e")
}
}
companion object {
private const val TAG = "AuthRequest"
}
}
@@ -3,12 +3,25 @@ package com.lizongying.mytv.api
data class Info( data class Info(
val code: Int?, val code: Int?,
val msg: String?, val msg: String?,
val data: InfoData, val data: Data,
) ) {
data class Data(
val token: String,
)
}
data class InfoData( data class Release(
val token: String, val code: Int?,
) val msg: String?,
val data: Data,
) {
data class Data(
val versionName: String,
val versionCode: Int,
val downloadUrl: String,
val updateTime: Int,
)
}
data class TimeResponse( data class TimeResponse(
val data: Time val data: Time
@@ -0,0 +1,11 @@
package com.lizongying.mytv.api
import retrofit2.Call
import retrofit2.http.GET
interface ReleaseService {
@GET("my-tv/v1/release")
fun getRelease(
): Call<Release>
}
@@ -50,6 +50,9 @@ class YSP(var context: Context) {
private var signature = "" private var signature = ""
private var appid = "ysp_pc"
var token = ""
private var encryptor: Encryptor? = null private var encryptor: Encryptor? = null
private lateinit var sharedPref: SharedPreferences private lateinit var sharedPref: SharedPreferences
@@ -83,6 +86,19 @@ class YSP(var context: Context) {
return """{"cnlid":"$cnlid","livepid":"$livepid","stream":"$stream","guid":"$guid","cKey":"$cKey","adjust":$adjust,"sphttps":"$sphttps","platform":"$platform","cmd":"$cmd","encryptVer":"$encryptVer","dtype":"$dtype","devid":"$devid","otype":"$otype","appVer":"$appVer","app_version":"$appVersion","rand_str":"$randStr","channel":"$channel","defn":"$defn","signature":"$signature"}""" return """{"cnlid":"$cnlid","livepid":"$livepid","stream":"$stream","guid":"$guid","cKey":"$cKey","adjust":$adjust,"sphttps":"$sphttps","platform":"$platform","cmd":"$cmd","encryptVer":"$encryptVer","dtype":"$dtype","devid":"$devid","otype":"$otype","appVer":"$appVer","app_version":"$appVersion","rand_str":"$randStr","channel":"$channel","defn":"$defn","signature":"$signature"}"""
} }
fun getAuthData(tvModel: TVViewModel): String {
livepid = tvModel.pid.value!!
randStr = getRand()
if (tvModel.retryTimes > 0) {
guid = newGuid()
}
signature = getAuthSignature()
return """pid=$livepid&guid=$guid&appid=$appid&rand_str=$randStr&signature=$signature"""
}
private fun getTimeStr(): String { private fun getTimeStr(): String {
return getDateTimestamp().toString() return getDateTimestamp().toString()
} }
@@ -131,6 +147,13 @@ class YSP(var context: Context) {
return hashedData.let { it -> it.joinToString("") { "%02x".format(it) } } return hashedData.let { it -> it.joinToString("") { "%02x".format(it) } }
} }
private fun getAuthSignature(): String {
val e =
"appid=${appid}&guid=${guid}&pid=${livepid}&rand_str=${randStr}".toByteArray()
val hashedData = encryptor?.hash2(e) ?: return ""
return hashedData.let { it -> it.joinToString("") { "%02x".format(it) } }
}
companion object { companion object {
private const val TAG = "YSP" private const val TAG = "YSP"
} }
@@ -18,4 +18,16 @@ interface YSPApiService {
@Header("cookie") cookie: String, @Header("cookie") cookie: String,
@Body request: LiveInfoRequest, @Body request: LiveInfoRequest,
): Call<LiveInfo> ): Call<LiveInfo>
@POST("v1/player/auth")
@Headers(
"content-type: application/x-www-form-urlencoded;charset=UTF-8",
"referer: https://www.yangshipin.cn/",
"yspappid: 519748109",
"user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
)
fun getAuth(
@Header("cookie") cookie: String,
@Body request: AuthRequest,
): Call<Auth>
} }
@@ -0,0 +1,44 @@
package com.lizongying.mytv.requests
import com.lizongying.mytv.api.ApiClient
import com.lizongying.mytv.api.Release
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
class MyRequest {
private var releaseService = ApiClient().releaseService
suspend fun getRelease(): Release? {
return withContext(Dispatchers.IO) {
fetchRelease()
}
}
private suspend fun fetchRelease(): Release? {
return suspendCoroutine { continuation ->
releaseService.getRelease()
.enqueue(object : Callback<Release> {
override fun onResponse(call: Call<Release>, response: Response<Release>) {
if (response.isSuccessful) {
continuation.resume(response.body())
} else {
continuation.resume(null)
}
}
override fun onFailure(call: Call<Release>, t: Throwable) {
continuation.resume(null)
}
})
}
}
companion object {
private const val TAG = "MyRequest"
}
}
+25 -1
View File
@@ -27,7 +27,31 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="10dp" android:layout_marginTop="10dp"
android:text="当前版本: $versionName\n获取最新: https://github.com/lizongying/my-tv/releases/" android:text="当前版本: $versionName\n获取最新: https://github.com/lizongying/my-tv/releases/"
></TextView> />
<LinearLayout
android:layout_width="300dp"
android:layout_height="wrap_content"
android:orientation="horizontal"
>
<Button
android:id="@+id/check_version"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="10dp"
android:text="@string/check_version"
/>
<ProgressBar
android:visibility="gone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
<TextView
android:id="@+id/version_name"
android:text=""
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
<Switch <Switch
android:id="@+id/switch_channel_reversal" android:id="@+id/switch_channel_reversal"
android:text="@string/title_channel_reversal" android:text="@string/title_channel_reversal"
+1
View File
@@ -2,5 +2,6 @@
<string name="app_name">我的电视</string> <string name="app_name">我的电视</string>
<string name="title_channel_reversal">换台反转</string> <string name="title_channel_reversal">换台反转</string>
<string name="title_channel_num">数字选台</string> <string name="title_channel_num">数字选台</string>
<string name="check_version">检查更新</string>
<string name="title_boot_startup">开机自启</string> <string name="title_boot_startup">开机自启</string>
</resources> </resources>